Right, here we go I decided to be patient, went back through this thread to go step by step, for a moment I was worried of it not working, until I re-wired the button to add the GameManager function then the lives would work perfectly fine.
The only issue I have if that a few scripts are reading as errors because they don’t seem to know what the GameManager is, however during play there is no problem and they work fine so it shouldn’t be a big issues.
For future reference to anyone who might want to use some of this code here are all the scripts, as well as making sure to follow Rob’s directions about the UI being implimented and re-wiring the buttons.
Ball:
using UnityEngine;
public class Ball : MonoBehaviour
{
private Paddle _paddle = null;
private bool _inMotion = false;
private Vector3 _paddleToBallVector;
// Initialisation
private void Start()
{
Initialise();
}
/// <summary>
/// Update
/// </summary>
private void Update()
{
DetermineMotionState();
}
/// <summary>
/// Sets the position of the Ball to the Paddle
/// </summary>
private void Initialise()
{
_paddle = GameObject.FindObjectOfType<Paddle>();
_paddleToBallVector = this.transform.position - _paddle.transform.position;
}
/// <summary>
/// Determins the current state of the Ball's motion. If the Ball isn't in motion it's position is reset.
/// </summary>
private void DetermineMotionState()
{
if (_inMotion != true)
{
SetLaunchPosition();
LaunchOnMouseClick();
}
}
/// <summary>
/// Sets the launch position of the Ball on the Paddle and keeps it there until launch
/// </summary>
private void SetLaunchPosition()
{
this.transform.position = _paddle.transform.position + _paddleToBallVector;
}
/// <summary>
/// Launches the Ball upon mouse click
/// </summary>
private void LaunchOnMouseClick()
{
if (Input.GetMouseButtonDown(0) == true)
{
_inMotion = true;
this.GetComponent<Rigidbody2D>().velocity = new Vector2(2f, 10f);
}
}
void OnCollisionEnter2D(Collision2D collision)
{
// Ball does not trigger sound when brick is destoyed.
// Not 100% sure why, possibly because brick isn't there.
Vector2 tweak = new Vector2(Random.Range(0f, 0.5f), Random.Range(0f, 0.5f));
if (_inMotion)
{
GetComponent<AudioSource>().Play();
GetComponent<Rigidbody2D>().velocity += tweak;
}
}
/// <summary>
/// Resets the Ball's position after exiting the PlaySpace
/// </summary>
public void ResetLaunchPosition()
{
_inMotion = false;
}
}
GameManager:
using UnityEngine;
public class GameManager : MonoBehaviour
{
// GameManager instance
private GameManager _gameManager = null;
// Player
private int _playerDefaultLives = 5; // Hard coded value
private int _playerLivesRemaining = -1;
/// <summary>
/// Returns the number of lives remaining for the player
/// </summary>
public int PlayerLivesRemaining
{
get { return _playerLivesRemaining; }
}
/// <summary>
/// Pre-Initialisation
/// </summary>
private void Awake()
{
// singleton pattern for GameManager
if (_gameManager != null)
{
Destroy(gameObject);
}
else
{
_gameManager = this;
GameObject.DontDestroyOnLoad(gameObject);
}
}
/// <summary>
/// Initialises a new game by setting the player lives to the default value (3)
/// </summary>
public void NewGame()
{
_playerLivesRemaining = _playerDefaultLives;
}
/// <summary>
/// Decreases the player's lives by one and checks for a game over state.
/// </summary>
public void PlayerLosesLife()
{
LevelManager levelManager = GameObject.FindObjectOfType<LevelManager>();
--_playerLivesRemaining;
if (_playerLivesRemaining <= -1)
{
GameOver();
}
else
{
levelManager.UpdateUIPlayerLivesRemaining();
levelManager.ResetBall();
}
}
/// <summary>
/// Loads the "Loose" scene ;) and destroys the GameManager
/// </summary>
private void GameOver()
{
LevelManager levelManager = GameObject.FindObjectOfType<LevelManager>();
levelManager.LoadLevel("Lose");
Destroy(this);
}
}
Brick:
using UnityEngine;
using System.Collections;
public class Brick : MonoBehaviour {
public AudioClip crack;
public Sprite[] hitSprites;
public static int breakableCount = 0;
public GameObject smoke;
private int timesHit;
private LevelManager levelManager;
private bool isBreakable;
// Use this for initialization
void Start () {
isBreakable = (this.tag == "Breakable");
// Keep track of breakable bricks
if (isBreakable) {
breakableCount++;
}
timesHit = 0;
levelManager = GameObject.FindObjectOfType<LevelManager>();
}
// Update is called once per frame
void Update () {
}
void OnCollisionEnter2D (Collision2D col) {
AudioSource.PlayClipAtPoint (crack, transform.position, 0.8f);
if (isBreakable) {
HandleHits();
}
}
void HandleHits () {
timesHit++;
int maxHits = hitSprites.Length + 1;
if (timesHit >= maxHits) {
breakableCount--;
levelManager.BrickDestoyed();
PuffSmoke();
Destroy(gameObject);
} else {
LoadSprites();
}
}
void PuffSmoke () {
GameObject smokePuff = Instantiate (smoke, transform.position, Quaternion.identity) as GameObject;
smokePuff.GetComponent<ParticleSystem>().startColor = gameObject.GetComponent<SpriteRenderer>().color;
}
void LoadSprites () {
int spriteIndex = timesHit - 1;
if (hitSprites[spriteIndex] != null) {
this.GetComponent<SpriteRenderer>().sprite = hitSprites[spriteIndex];
} else {
Debug.LogError ("Brick sprite missing");
}
}
// TODO Remove this method once we can actually win!
void SimulateWin () {
levelManager.LoadNextLevel();
}
}
LevelManager:
using UnityEngine;
using UnityEngine.UI;
using System.Linq;
public class LevelManager : MonoBehaviour
{
/// <summary>
/// Initialisation
/// </summary>
private void Start()
{
if (GameObject.Find("Canvas")) // This is a bit messy. It checks to see if there is a canvas, if so, updates the remaining lives
{ // which prevents a null reference exception error occuring on non-playable levels (Win, Loose, Start)
UpdateUIPlayerLivesRemaining();
}
}
public void LoadLevel(string name)
{
Debug.Log("New Level load: " + name);
Brick.breakableCount = 0;
Application.LoadLevel(name);
}
public void QuitRequest()
{
Debug.Log("Quit requested");
Application.Quit();
}
public void LoadNextLevel()
{
Brick.breakableCount = 0;
Application.LoadLevel(Application.loadedLevel + 1);
}
public void BrickDestoyed()
{
if (Brick.breakableCount <= 0)
{
LoadNextLevel();
}
}
/// <summary>
/// Updates the player lives UI.Text object with the player's remaining lives
/// </summary>
public void UpdateUIPlayerLivesRemaining()
{
Text uiDisplayPlayerLives = GetTextObjectByName("PlayerLives");
GameManager gameManager = GameObject.FindObjectOfType<GameManager>();
uiDisplayPlayerLives.text = "lives x " + gameManager.PlayerLivesRemaining.ToString();
}
/// <summary>
/// Resets the location of the Ball to the Paddle
/// </summary>
public void ResetBall()
{
Ball ball = GameObject.FindObjectOfType<Ball>();
ball.ResetLaunchPosition();
}
// Helper method which could be moved to a separate class later
/// <summary>
/// Returns a UI.Text object for the specified name
/// </summary>
/// <param name="name">The name of the UI.Text object to return</param>
/// <returns></returns>
private Text GetTextObjectByName(string name)
{
var canvas = GameObject.Find("Canvas"); // Hard coded, assumes the canvas will always be called "Canvas"
var texts = canvas.GetComponentsInChildren<Text>();
return texts.FirstOrDefault(textObject => textObject.name == name);
}
}
Paddle:
using UnityEngine;
using System.Collections;
public class Paddle : MonoBehaviour {
public bool autoPlay = false;
public float minX, maxX;
private Ball ball;
void Start () {
ball = GameObject.FindObjectOfType<Ball>();
}
// Update is called once per frame
void Update () {
if (!autoPlay) {
MoveWithMouse();
} else {
AutoPlay();
}
}
void AutoPlay() {
Vector3 paddlePos = new Vector3 (0.5f, this.transform.position.y, 0f);
Vector3 ballPos = ball.transform.position;
paddlePos.x = Mathf.Clamp(ballPos.x, minX, maxX);
this.transform.position = paddlePos;
}
void MoveWithMouse () {
Vector3 paddlePos = new Vector3 (0.5f, this.transform.position.y, 0f);
float mousePosInBlocks = Input.mousePosition.x / Screen.width * 16;
paddlePos.x = Mathf.Clamp(mousePosInBlocks, minX, maxX);
this.transform.position = paddlePos;
}
}
LoseCollider:
using UnityEngine;
using System.Collections;
public class LoseCollider : MonoBehaviour
{
private GameManager _gameManager;
/// <summary>
/// Pre-Initialisation
/// </summary>
private void Awake()
{
_gameManager = GameObject.FindObjectOfType<GameManager>();
}
/// <summary>
/// Handles the OnTriggerEnter2D event triggered by another object entering a trigger collider attached to the LoseCollider
/// </summary>
/// <param name="collider">The other Collider2D involved in the collision</param>
private void OnTriggerEnter2D(Collider2D collider)
{
_gameManager.PlayerLosesLife();
}
}
I have a smoke destroyer script for getting rid of the partical effects, however I don’t have it yet, but I’ll look it up. I’ll go with my origonal plan and when I publish the full game on itch.io I’ll include the entire code for any aspiring developer out there