GameData Version1

GameData - Version 1

Important Concepts: 1. Singleton Pattern - One and only one component instance exists - Globally accessible variable - Component Persistence between Scene-change 2. DontDestroyOnLoad( ) - Persistence 3. Static Variables - Globally accessible variable 4. Component Communication via Methods

In the game we'll create a custom component to store game-data that is persisted throughout the gameplay session so that we can keep track of score, lives, health, etc. This is the first version for class: GameData, we'll add more complexity to this class definition throughout the course.

Singleton Pattern:

GameData will use the Singleton Programming Pattern, this means that we'll add code to insure that there is always one and only one of this gameObject in existence throughout the lifetime a gamePlay session.

Awake( ) - GameData will be initialized in the Awake( ) event-handler method, so that it's created before other gameObjects that may need to access the object during their initialization in the Start( ) event-handler.

GameObject Persistence: DontDestroyOnLoad( )

In order to insure that the GameData Component exists throughout the entire gameplay session, it's necessary to insure that the gameObject is not destroyed when changing scenes. So, the code below shows the use of the method: DontDestroyOnLoad( someGameObject ).

Test to determine existence of any other GameObject Instances: When initializing GameData in Awake( ) we need logic to test to determine if a GameData object already exits in a scene: The code below checks to see if the instanceRef variable already points to an active GameData object, if not, then this variable should point to: this (the object currently executing the code ). Otherwise a GameData object already exists and the one executing the code should destroy itself!

if( instanceRef == null)  //this code hasn't been executed before
        {
            instanceRef = this; //point to object instance currently executing this code
            DontDestroyOnLoad(this.gameObject); //don't destroy the gameObject this is attached to
        }

GameData instanceRef; is a the variable used to implement the Singleton Programming pattern for GameData.cs. A static variable belongs to the class, rather than to an object instance of a class, there can only be one instance of a static variable during a program's execution. Typical class-defined variables, each time an object is created from the class definition, each object gets it's own unique instance of each variable. Example: a class static variable can be used to keep track of the number of object instances currently instantiated in an executing program. public static numZombies; This variable could be incremented each time a zombie object is created and decremented each time a zombie object is destroyed during a program's execution, so that a count of the number of zombies could be displayed during the program. The data-type of the static variable instanceRef is GameData. So it's a variable that can 'point' to an instance of an object of the GameData class. While this seems a bit odd, think of it as you wearing a name-tag...it provides other gameObjects with easy access to the one GameData object in the game.

public static GameData instanceRef; //null //variable that can point to a GameData object

This script will be attached to the GameManager in the first game Scene and will be persisted throughout the game since it uses the singleton design pattern.

Add GameData script to an Empty GameObject: named: GameManager in your starting scene, so it will be executed as a singleton and shows up as 'DontDestroyOnLoad' in the Hierarchy when the game is played.

Event-Driven Component Communication via public Methods

Inter-Component Communication: When the Player gameObject has a collision with a gameObject that has an attached PickUp component, and a Collider2D Component with Trigger set to true: this can be considered the initializing event in a chain of events that need to be communicated with other components on other GameObjects, so that corresponding components are notified that the GameState has changed, so they can be updated to reflect that change. For example, the Score and Health displays should be updated. Requirement: PlayerController component must notify GameData component that Score or Health values have changed: Solution: When PlayerController logic has determined that a GameState changing event has occurred, it will execute a public GameData Method, so that GameData values for Score or Health are updated. We'll also discuss how other GameObjects can be notified to update, when a GameState changing-event occurs. See example below: How to use GameData Singleton To use the singleton reference in another class use the following syntax, for example, when calling the Add method from the PlayerController script:

Example of using GameData singleton in PlayerController.cs

// Inside PlayerController.cs
// Inside event-handler: OnTriggerEnter2D( )
// After PlayerController logic has deteremined 
//that the GameData.score must be updated 
GameData.instanceRef.Add( item.value );

GameData Class Definition - Version1

modified 4/10/2020

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// Singleton Object to store all GameData
/// Is not destroyed when changing scenes
/// </summary>
public class GameData : MonoBehaviour
{
    public static GameData instanceRef; //null //variable that can point to a GameData object

    private int score;
    private int health;
  
    //TODO - Add properties: Score, Health

    // Awake is called before Start() is called on any GameObject
    // Other objects have dependencies on this object so it must be created first
    void Awake()
    {
        if( instanceRef == null)  //this code hasn't been executed before
        {
            instanceRef = this; //point to object instance currently executing this code
            DontDestroyOnLoad(this.gameObject); //don't destroy the gameObject this is attached to
        }
        else  //this object is not the first, it's an imposter that must be destroyed
        {
            DestroyImmediate(this.gameObject);
            Debug.Log("Destroy GameData Imposter");
        }

        //initialize after destroying imposters
        score = 0;
        health = 100;
      

    } //end Awake

    //will be executed in PlayerController when colliding with a collectible
    public void Add( int value)
    {
        score += value;
        Debug.Log("Score updated: " + score); //display score in console
    }

    public void TakeDamage( int value)
    {
        health -= value;
        Debug.Log("health updated: " + health); //display health in console
        if( health <= 0)
        {
            Debug.Log("Health less than 0"); //display health in console

        }
    }

    //called when restarting the miniGame
    public void ResetGameData()
    {
        score = 0;
        health = 100;
    }


}//end class GameData

Resets Score and Health to correct initial values. Public, so it can be executed from MiniGameManager Script.

Last updated