Post

Making Fruit Box game (2)

Starting the Project, creating the Apple, and creating Drag and Select

Topic

-Creating the Apple object -Player Input -SelectionManager -Conclusion

Creating the Apple object

One of the module that must be done is creating the Apple object. Through using the Prefab in Unity I was able to create the Apple object without making the sprite.

I’ve done so by using a simple circular 2D sprite and having a text mesh pro as a child to show the number

Apple Object
Desktop View

Our goal for apple object is to once created, it should have a random value from 1 to 9. Thus our script looks like the one below

Apple.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using UnityEngine;
using TMPro;

public class Apple : MonoBehaviour
{
    public SpriteRenderer apple;
    public SpriteRenderer SelectionSprite;
    public TextMeshPro txt;
    [SerializeField]
    public int value = 0;

    void Awake()
    {
        value = Random.Range(1, 9);
        txt.text = value.ToString();
        SelectionManager.Instance.availableApples.Add(this);
    }

    public void OnSelected()
    {
        SelectionSprite.gameObject.SetActive(true);
    }

    public void OnDeselected()
    {
        SelectionSprite.gameObject.SetActive(false);
    }

    public void Destroy()
    {
        Destroy(gameObject);
    }

}

Breaking down the Apples.cs script

Variables

1
2
3
4
5
6
7
8
9
// apple sprite 
public SpriteRenderer apple;
// if apple is selected it should have contour
public SpriteRenderer SelectionSprite;
// text to represent the apple's value
public TextMeshPro txt;
// apple value
[SerializeField]
public int value = 0;

Functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// once created, give a random assigned value and add it to available Apple 
void Awake()
{
    value = Random.Range(1, 9);
    txt.text = value.ToString();
    SelectionManager.Instance.availableApples.Add(this);
}

// on selected, show the user that apple has been selected
public void OnSelected()
{
    SelectionSprite.gameObject.SetActive(true);
}

// deselct, show the user that apple has been deselected
public void OnDeselected()
{
    SelectionSprite.gameObject.SetActive(false);
}

// destory the game object
public void Destroy()
{
    Destroy(gameObject);
}
Result of the code when we spawn apple object
Desktop View

Player Input

There are several functionality and several ways of doing this.

What I wanted to implement was drag and selecting the Apples, and in order for this to happen, I required mouse input from user.

My general idea was when player clicks a mouse button, and until player lets go of the mouse button we should an UI box.

PlayerInput.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public class PlayerInput : MonoBehaviour
{
    [SerializeField]
    private Camera Camera;
    [SerializeField]
    private RectTransform selectionBox;
    private Vector2 startMousePosition;
    private Color origin = new Color(1, 1, 0, 0.31f);
    private Color success = new Color(1, 0, 0, 0.31f);

    void Update()
    {
        HandleSelectionInputs();
    }

    private void HandleSelectionInputs(){
        if (Input.GetMouseButtonDown(0)){
            selectionBox.sizeDelta = Vector2.zero;
            selectionBox.gameObject.SetActive(true);
            startMousePosition = Input.mousePosition;
        }
        else if (Input.GetKey(KeyCode.Mouse0))
        {
            ResizeSelectionBox();
        }
        else if (Input.GetMouseButtonUp(0))
        {
            selectionBox.sizeDelta = Vector2.zero;
            selectionBox.GetComponent<UnityEngine.UI.Image>().color = origin;
            selectionBox.gameObject.SetActive(false);
            SelectionManager.Instance.DeselectAll();
            print(SelectionManager.Instance.score);
        }
    }

    private void ResizeSelectionBox()
    {
        float width = Input.mousePosition.x - startMousePosition.x;
        float height = Input.mousePosition.y - startMousePosition.y;

        selectionBox.anchoredPosition = startMousePosition + new Vector2(width/2 , height/2);
        selectionBox.sizeDelta = new Vector2(Mathf.Abs(width), Mathf.Abs(height));

        Bounds bounds = new Bounds(selectionBox.position, selectionBox.sizeDelta);

        for (int i = 0; i < SelectionManager.Instance.availableApples.Count; i++)
        {
            if (UnitIsInSelectionBox(Camera.WorldToScreenPoint(SelectionManager.Instance.availableApples[i].transform.position), bounds))
            {
                SelectionManager.Instance.Select(SelectionManager.Instance.availableApples[i]);
            }
            else
            {
                SelectionManager.Instance.Deselect(SelectionManager.Instance.availableApples[i]);
            }
            bool res = SelectionManager.Instance.GetSum();
            if (res == true)
            {
                selectionBox.GetComponent<UnityEngine.UI.Image>().color = success;
            }
            else
            {
                selectionBox.GetComponent<UnityEngine.UI.Image>().color = origin;
            }
        }
    }

    private bool UnitIsInSelectionBox(Vector2 pos, Bounds bounds)
    {
        return pos.x > bounds.min.x && pos.x < bounds.max.x && pos.y > bounds.min.y && pos.y < bounds.max.y;
    }
}

Let’s try and analyze this code before we move on to SelectionManager.cs

The variables are as follows:

1
2
3
4
5
6
7
8
9
10
11
12
// Main Camera
[SerializeField]
private Camera Camera;
// This is the box that will appear on the screen when player drags the mouse
[SerializeField]
private RectTransform selectionBox;
// Starting Mouse position is needed to create the box
private Vector2 startMousePosition;
// This will be the color to indicate that player is dragging the mouse
private Color origin = new Color(1, 1, 0, 0.31f);
// If the sum of apples = 10, then we change the color of square to red
private Color success = new Color(1, 0, 0, 0.31f);

Since we need to constantly check for user’s action, we used Update(), and to clean the code better we made handling input as a separate function. Let’s take a look at the function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// handles uesr's input
private void HandleSelectionInputs(){
    // When player clicks left mouse
    if (Input.GetMouseButtonDown(0)){
        // make selectionBox's anchor to stay the same
        selectionBox.sizeDelta = Vector2.zero;
        // since player has clicked we must show the image
        selectionBox.gameObject.SetActive(true);
        // box should start from here
        startMousePosition = Input.mousePosition;
    }
    // If player has not let go of the left click
    else if (Input.GetKey(KeyCode.Mouse0))
    {
        // update the box size accordingly
        ResizeSelectionBox();
    }
    // If player lets go of the left click
    else if (Input.GetMouseButtonUp(0))
    {
        selectionBox.sizeDelta = Vector2.zero;
        // set the selectionBox's color back to original color
        selectionBox.GetComponent<UnityEngine.UI.Image>().color = origin;
        // since we no longer need to game object, we set active to false
        selectionBox.gameObject.SetActive(false);
        // Deselect all the object that's been selected
        SelectionManager.Instance.DeselectAll();
        // print the score of the user to check
        print(SelectionManager.Instance.score);
    }
}

I have yet to explain the SelectionManager class, but to keep in simple SelectionManager class keeps track of selected apple in a list/hash set.

It also has the ability to keep track of score.

Let’s move on and finish explaining PlayerInput.cs with rest of the function ResizeSelectionBox() and UnitIsInSelectionBox(Vector2 pos, Bounds bounds)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// resizes the image of box to display to user
private void ResizeSelectionBox()
{
    // width of the box
    float width = Input.mousePosition.x - startMousePosition.x;
    // height of the box
    float height = Input.mousePosition.y - startMousePosition.y;

    selectionBox.anchoredPosition = startMousePosition + new Vector2(width/2 , height/2);
    // the size must be calculated with abs value as it will result in negative values
    selectionBox.sizeDelta = new Vector2(Mathf.Abs(width), Mathf.Abs(height));
    
    // bounding box with size
    Bounds bounds = new Bounds(selectionBox.position, selectionBox.sizeDelta);

    // For all the Available apple
    for (int i = 0; i < SelectionManager.Instance.availableApples.Count; i++)
    {
        // If Apple is in Selection Box,
        if (UnitIsInSelectionBox(Camera.WorldToScreenPoint(SelectionManager.Instance.availableApples[i].transform.position), bounds))
        {
            // selects the apple
            SelectionManager.Instance.Select(SelectionManager.Instance.availableApples[i]);
        }
        else
        {
            // deselects the apple
            SelectionManager.Instance.Deselect(SelectionManager.Instance.availableApples[i]);
        }

        //checks if the selected is apples sum to 10
        bool res = SelectionManager.Instance.GetSum();
        if (res == true)
        {
            selectionBox.GetComponent<UnityEngine.UI.Image>().color = success;
        }
        else
        {
            selectionBox.GetComponent<UnityEngine.UI.Image>().color = origin;
        }    
    }
}

// simply checks if apple is in the boundary of the box
private bool UnitIsInSelectionBox(Vector2 pos, Bounds bounds)
{
    return pos.x > bounds.min.x && pos.x < bounds.max.x && pos.y > bounds.min.y && pos.y < bounds.max.y;
}
Selection BoxSelection Box when sum is 10
Desktop ViewDesktop View

SelectionManager

Now let’s move on to SelectionManager.cs this file is used to handle all the work regarding selecting and deselecting of the apple.

Since there should only be 1 SelectionManager in the entire game, I used singleton method to create this script, and is not a Monobehavior script.

SelectionManager.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
public class SelectionManager
{
    private static SelectionManager instance;
    public static SelectionManager Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new SelectionManager();
            }
            return instance;
        }
        set
        {
            instance = value;
        }
    }

    public HashSet<Apple> selectedApples = new HashSet<Apple>();
    public List<Apple> availableApples = new List<Apple>();
    public int score = 0;

    private SelectionManager() { }

    public void Select(Apple apple)
    {
        selectedApples.Add(apple);
        apple.OnSelected();
    }

    public void Deselect(Apple apple)
    {
        apple.OnDeselected();
        selectedApples.Remove(apple);
    }

    public void DeselectAll()
    {
        if (GetSum())
        {
            foreach (Apple apple in selectedApples)
            {
                score += 1;
                availableApples.Remove(apple);
                apple.Destroy();
            }
            selectedApples.Clear();
        }
        else
        {
            foreach (Apple apple in selectedApples)
            {
                apple.OnDeselected();
            }
            selectedApples.Clear();
        }
    }

    public bool boolIsSelected(Apple apple)
    {
        return selectedApples.Contains(apple);
    }

    public bool GetSum()
    {
        int sum = 0;
        foreach (Apple apple in selectedApples)
        {
            sum += apple.value;
        }
        return sum == 10;
    }
}

Breaking this down again

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// constructor for Selection Manager
private static SelectionManager instance;
public static SelectionManager Instance
{
    get
    {
        if (instance == null)
        {
            instance = new SelectionManager();
        }
        return instance;
    }
    set
    {
        instance = value;
    }
}

private SelectionManager() { }

Variables that are used

1
2
3
4
5
6
// selected apples (apples that are in user's square)
public HashSet<Apple> selectedApples = new HashSet<Apple>();
// available apples (apples that user can select)
public List<Apple> availableApples = new List<Apple>();
// score 
public int score = 0;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// adds selected apple into hash set
public void Select(Apple apple)
{
    selectedApples.Add(apple);
    apple.OnSelected();
}

// deselects apple from hashset
public void Deselect(Apple apple)
{
    apple.OnDeselected();
    selectedApples.Remove(apple);
}

// once user lets go of the mouse, calculate the score if available and destroy the apple, else deselect all 
public void DeselectAll()
{
    // if sum of selected apples are 10 destory the apples
    if (GetSum())
    {
        foreach (Apple apple in selectedApples)
        {
            score += 1;
            availableApples.Remove(apple);
            apple.Destroy();
        }
        selectedApples.Clear();
    }
    // deselect all the apples
    else
    {
        foreach (Apple apple in selectedApples)
        {
            apple.OnDeselected();
        }
        selectedApples.Clear();
    }
}

// checks if apple is selected
public bool boolIsSelected(Apple apple)
{
    return selectedApples.Contains(apple);
}

// gets if all the selected apples == 10
public bool GetSum()
{
    int sum = 0;
    foreach (Apple apple in selectedApples)
    {
        sum += apple.value;
    }
    return sum == 10;
}
Showing the score
Desktop View

Conclusion

Task List

  • Main Menu (what players see first)
    • Button (Play button)
    • CheckBox (light/dark mode)
    • Slider (Controlling Audio)
  • Gameplay
    • User Input (Mouse/Touchpad, something to detect drag motion)
      • Box should appear to show the user’s dragged box and what kind of apples are included
      • Some mechanic that could detect if sum is 10
      • Should change color if sum is 10
    • Board (10 x 17 board? some sort of board)
      • Apple Object (with signed number 1 to 9)
      • Reset Button (when player presses resets the board)
      • Timer Slider (2 minute)
      • Combo meter
    • Combo Mechanic
      • 3 second time window if player gets point within, they get extra point
    • Score Tracker (high score, score)
  • After Game (Once timer finishes)
    • Should show your score, and high score
    • Replay button
    • Continue button (allows player to continue on the board, score should stop/add don’t know yet)
This post is licensed under CC BY 4.0 by the author.