Issue with Life functionality implementation

Here’s the code from the three relevant class files:

using TMPro;
using UnityEngine;

public class GameSession : MonoBehaviour
{
    // Configuration parameters
    [Range(0.1f, 10f)] [SerializeField] float gameSpeed = 1f;
    [SerializeField] int pointsPerBlockDestroyed = 10;
    [SerializeField] TextMeshProUGUI scoreText;
    [SerializeField] TextMeshProUGUI livesText;

    //State parameters
    [SerializeField] int currentScore = 0;
    [SerializeField] int numberOfLives = 2;

    // Singleton implementation to maintain game data on level progression. 
    private void Awake()
    {
        int gameStatusCount = FindObjectsOfType<GameSession>().Length;
        if (gameStatusCount > 1)
        {
            gameObject.SetActive(false);
            Destroy(gameObject);
            
        }
        else
        {
            DontDestroyOnLoad(gameObject);
            
        }
    }

    public void Start()
    {
        ScoreToString();
        LivesToString();
    }

    // Update is called once per frame
    void Update()
    {
        Time.timeScale = gameSpeed;
    }

    // Updates number of lives and total current score in the display area
    public void UpdateDisplay()
    {
        LivesToString();
        ScoreToString();

    }
    // Resets the game
    public void gameReset()
    {
        Destroy(gameObject);
    }
    
    // Converts the score integer to a string to be displayed in the display area
    public void ScoreToString()
    {
        scoreText.text = currentScore.ToString();
        
    }

    // Converts the lives integer to a string to be displayed in the display area
    public void LivesToString()
    {
        livesText.text = numberOfLives.ToString();
    }

    // Updates current score; called in the Block.cs file when a block is destroyed
    public void scoreCounter()
    {
        currentScore += pointsPerBlockDestroyed;
    }

    // Adds a life ever 200 points
    public void addALife()
    {
        if (currentScore != 0 && currentScore % 200 == 0)
        {
            numberOfLives++;
        }
    }

    // Called in LoseCollider.cs on bottom boundary collision
    public void loseALife()
    {
        numberOfLives--;
    }

    // Called in LoseCollider.cs; if number of lives == 0 game ends
    public int livesCheck()
    {
        return numberOfLives;
    }
}

the LoseCollider.cs:

using UnityEngine;
using UnityEngine.SceneManagement;

public class LoseCollider : MonoBehaviour
{

    string gameOver = "GameOver";
    int noMoreLives = 0;
    GameSession gameStatus;
    Ball ballReset;

    private void OnTriggerEnter2D(Collider2D collision)
    {
        SceneManager.LoadScene("GameOver");

        // gameStatus.loseALife();

        /*
        if (gameStatus.livesCheck() == noMoreLives)
        {
            SceneManager.LoadScene(gameOver);
        }
        else
        {
            ballReset.ballReset();
        }
        */
    }
}

and the Ball.cs class

using UnityEngine;

public class Ball : MonoBehaviour
{

    // Configuration parameters
    [SerializeField] Paddle paddle1;
    [SerializeField] float launchX = 2f;
    [SerializeField] float launchY = 15f;
    [SerializeField] AudioClip[] ballSounds;
    
    // State parameters
    Vector2 paddleToBallVector; 
    bool hasStarted = false;

    // cached component references
    AudioSource myAudioSource;

    // Start is called before the first frame update
    void Start()
    {
        paddleToBallVector = transform.position - paddle1.transform.position;
        myAudioSource = GetComponent<AudioSource>();
    }

    // Update is called once per frame
    void Update()
    {
        if (!hasStarted)
        {
            LockBallToPaddle();
            LaunchOnMouseClick();
        }
    }

    private void LaunchOnMouseClick()
    {
        if (Input.GetMouseButtonDown(0))
        {
            hasStarted = true;
            GetComponent<Rigidbody2D>().velocity = new Vector2(launchX, launchY);
        }
    }

    private void LockBallToPaddle()
    {
        Vector2 paddlePosition = new Vector2(paddle1.transform.position.x,
                    paddle1.transform.position.y);
        transform.position = paddleToBallVector + paddlePosition;
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (hasStarted)
        {
            AudioClip clip = ballSounds[Random.Range(0, ballSounds.Length)];
            myAudioSource.PlayOneShot(clip);
        }
    }

    public void ballReset()
    {
        hasStarted = false;
    }
}

The issue I’m having is getting the LoseALife() function to initialize when the player misses the ball with the paddle. I can’t get the script to update to minus one from the numberOfLives variable and then reflect the new value on the display screen in-game. I also am having trouble getting the ball and paddle to reset to their starting position on a lost life.

Any suggestions would be greatly appreciated. I’m not looking for the answer just some helpful hints.

Hi @CAlexander,

First of all, good job on challenging yourself.

I skimmed your code. Where do you want to call LoseALife? From what I see, it was supposed to get called in the LoseCollider instance, which makes sense. I assume that you got a NullReferenceException error message before you commented the code out. NullReferenceException means that a reference (“link”) to an instance is missing. You declared a variable of type GameSession at the top of your code, which is fine, but the value is null by default. I cannot see where an object gets assigned to the variable.

The second issue, apart from the code which is commented out and cannot run, is the spelling of the method. C# is case-sensitive.

Last but not least, add a Debug.Log to the loseALife/LoseALife method in the GameSession class so you can see whether the method gets called during runtime.

All in all, you are on the right track. The solution is almost complete. :slight_smile:


See also:

1 Like

Thanks a bunch, Nina! Your advice helped me reorient myself and look at my code from a fresh perspective!

I’m going to leave my solution here and some screenshots for anyone who has a similar problem :smiley:

using UnityEngine;
using UnityEngine.SceneManagement;

public class LoseCollider : MonoBehaviour
{
    GameSession gameSession;
    string gameOver = "GameOver";
    int noMoreLives = 0;
    Ball ballReset;

    private void OnTriggerEnter2D(Collider2D collision)
    {
        LifeLost();
        GameOverCheck();
    }

    private void GameOverCheck()
    {
        if (gameSession.LivesCheck() == noMoreLives)
        {
            SceneManager.LoadScene(gameOver);
        }
        else
        {
            PaddleAndBallReset();
        }
    }

    private void LifeLost()
    {
        gameSession = FindObjectOfType<GameSession>();
        gameSession.LoseALife();
        gameSession.LivesToString();
    }

    private void PaddleAndBallReset()
    {
        ballReset = FindObjectOfType<Ball>();
        ballReset.BallReset();
    }
}

1 Like

Good job! :slight_smile:

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms