
In this challenge I was required to put my User Interface skills to the test with this whack-a-mole-like challenge. The project scope is a game in which you have to get all the food that pops up on a grid while avoiding the skulls.

In order to successfully complete this project it was necessary to debug buttons, mouse clicks, score tracking, restart sequences, and difficulty setting to get to resolve all of the bugs in the code.

Challenge Outcome:
- All of the buttons look nice with their text properly aligned
- When you select a difficulty, the spawn rate changes accordingly
- When you click a food, it is destroyed and the score is updated in the top-left
- When you lose the game, a restart button appears that lets you play again
C# Code Samples
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TargetX : MonoBehaviour
{
private Rigidbody rb;
private GameManagerX gameManagerX;
public int pointValue;
public GameObject explosionFx;
public float timeOnScreen = 1.0f;
private float minValueX = -3.75f; // the x value of the center of the left-most square
private float minValueY = -3.75f; // the y value of the center of the bottom-most square
private float spaceBetweenSquares = 2.5f; // the distance between the centers of squares on the game board
void Start()
{
rb = GetComponent<Rigidbody>();
gameManagerX = GameObject.Find("Game Manager").GetComponent<GameManagerX>();
transform.position = RandomSpawnPosition();
StartCoroutine(RemoveObjectRoutine()); // begin timer before target leaves screen
}
// When target is clicked, destroy it, update score, and generate explosion
private void OnMouseDown()
{
if (gameManagerX.isGameActive)
{
Destroy(gameObject);
gameManagerX.UpdateScore(pointValue);
Explode();
Debug.Log("did i get it");
}
}
// Generate a random spawn position based on a random index from 0 to 3
Vector3 RandomSpawnPosition()
{
float spawnPosX = minValueX + (RandomSquareIndex() * spaceBetweenSquares);
float spawnPosY = minValueY + (RandomSquareIndex() * spaceBetweenSquares);
Vector3 spawnPosition = new Vector3(spawnPosX, spawnPosY, 0);
return spawnPosition;
}
// Generates random square index from 0 to 3, which determines which square the target will appear in
int RandomSquareIndex ()
{
return Random.Range(0, 4);
}
// If target that is NOT the bad object collides with sensor, trigger game over
private void OnTriggerEnter(Collider other)
{
Destroy(gameObject);
if (other.gameObject.CompareTag("Sensor") && !gameObject.CompareTag("Bad"))
{
gameManagerX.GameOver();
}
}
// Display explosion particle at object's position
void Explode ()
{
Instantiate(explosionFx, transform.position, explosionFx.transform.rotation);
}
// After a delay, Moves the object behind background so it collides with the Sensor object
IEnumerator RemoveObjectRoutine ()
{
yield return new WaitForSeconds(timeOnScreen);
if (gameManagerX.isGameActive)
{
transform.Translate(Vector3.forward * 5, Space.World);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class GameManagerX : MonoBehaviour
{
//********** declare the UI elements that will be used*************
public TextMeshProUGUI scoreText;
public TextMeshProUGUI gameOverText;
public TextMeshProUGUI timeText;
public GameObject titleScreen;
public Button restartButton;
// create a list to add our target prefab game objects
public List<GameObject> targetPrefabs;
//******* add basic gameplay settings
private int score;
private float spawnRate = 1.5f;
public bool isGameActive;
private float spaceBetweenSquares = 2.5f;
private float minValueX = -3.75f; // x value of the center of the left-most square
private float minValueY = -3.75f; // y value of the center of the bottom-most square
public float timeLeft;
float currCountdownValue;
Coroutine mytimer;
// Start the game, remove title screen, reset score, and adjust spawnRate based on difficulty button clicked
public void StartGame(int difficulty)
{
spawnRate /= difficulty;
isGameActive = true;
StartCoroutine(SpawnTarget());
score = 0;
UpdateScore(0);
titleScreen.SetActive(false);
// timer feature
mytimer = StartCoroutine(CountDownTimer(timeLeft));
}
// While game is active spawn a random target
IEnumerator SpawnTarget()
{
while (isGameActive)
{
yield return new WaitForSeconds(spawnRate);
int index = Random.Range(0, targetPrefabs.Count);
if (isGameActive)
{
Instantiate(targetPrefabs[index], RandomSpawnPosition(), targetPrefabs[index].transform.rotation);
}
}
}
// Generate a random spawn position based on a random index from 0 to 3
Vector3 RandomSpawnPosition()
{
float spawnPosX = minValueX + (RandomSquareIndex() * spaceBetweenSquares);
float spawnPosY = minValueY + (RandomSquareIndex() * spaceBetweenSquares);
Vector3 spawnPosition = new Vector3(spawnPosX, spawnPosY, 0);
return spawnPosition;
}
// Generates random square index from 0 to 3, which determines which square the target will appear in
int RandomSquareIndex()
{
return Random.Range(0, 4);
}
// Update score with value from target clicked
public void UpdateScore(int scoreToAdd)
{
score += scoreToAdd;
scoreText.text = "score: " + score.ToString();
}
// Stop game, bring up game over text and restart button
public void GameOver()
{
gameOverText.gameObject.SetActive(true);
restartButton.gameObject.SetActive(true);
isGameActive = false;
StopCoroutine(mytimer);
}
// Restart game by reloading the scene
public void RestartGame()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
// a countdown timer to control how long the game lasts
public IEnumerator CountDownTimer(float howLong)
{
Debug.Log("hello from guie in count down timer. Going to count down from " + howLong.ToString());
currCountdownValue = howLong;
while (currCountdownValue > 0)
{
Debug.Log("Time Left: " + currCountdownValue);
timeText.text = "Time left: " + currCountdownValue.ToString();
yield return new WaitForSeconds(1.0f);
currCountdownValue--;
}
if(currCountdownValue == 0)
{
Debug.Log("Time up, game over.");
timeText.text = "Time left: " + currCountdownValue.ToString();
GameOver();
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class DifficultyButtonX : MonoBehaviour
{
private Button button;
private GameManagerX gameManagerX;
public int difficulty;
// Start is called before the first frame update
void Start()
{
gameManagerX = GameObject.Find("Game Manager").GetComponent<GameManagerX>();
button = GetComponent<Button>();
button.onClick.AddListener(SetDifficulty);
}
/* When a button is clicked, call the StartGame() method
* and pass it the difficulty value (1, 2, 3) from the button
*/
void SetDifficulty()
{
Debug.Log(button.gameObject.name + " was clicked");
gameManagerX.StartGame(difficulty);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DestroyObjectX : MonoBehaviour
{
void Start()
{
Destroy(gameObject, 2); // destroy particle after 2 seconds
}
}