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
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);
}
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;
}
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;
}
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)
- User Input (Mouse/Touchpad, something to detect drag motion)
- 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)