0
\$\begingroup\$

I'm trying to make a gameover-text show when the player is leaving the plane-area. In GameManager.cs I've written:

public void GameOver()
{
    gameOverText.gameObject.SetActive(true);
}

And in PlayerController.cs I've used the following code:

private GameManager gameManager;

and:

void Update()
{
    float horizontalInput = Input.GetAxis("Horizontal");
    float verticalInput = Input.GetAxis("Vertical");

    playerRb.AddForce(Vector3.right * speed * horizontalInput);
    playerRb.AddForce(Vector3.forward * speed * verticalInput);

    if (transform.position.y < -4)
    {
        gameManager.GameOver();
    }
}

But this doesn't really work, and the console is giving me this message:

Assets/Scripts/PlayerController.cs(8,25): warning CS0649: Field 'PlayerController.gameManager' is never assigned to, and will always have its default value null

I've already used the OnTriggerEnter (for powerups) and OnCollisionEnter (for enemies). How can I get the GameOver-text to show when the player leaves the "Plane"?

\$\endgroup\$
1
  • 1
    \$\begingroup\$ How do you expect a reference to your GameManager script instance will get populated into your private GameManager gameManager variable? Remember this variable will hold a value of "null" until you put something there, either by assigning it in your script, or exposing it as a field you can populate in the Inspector UI. So, what's your strategy for wiring up this value? \$\endgroup\$
    – DMGregory
    Commented Nov 8, 2019 at 18:00

3 Answers 3

0
\$\begingroup\$

TLDR: Add the SerializeField attribute to the GameManager variable in the PlayerController, then reference the desired GameManager instance through the inspector.


In the PlayerController class you have included a variable of the type GameManager, but you have not populated the variable with any instance of GameManager.

Populating variables and referencing can be achieved in a variety of ways, but one simple method I like in Unity is to use the inspector to manually populate variables. Here is one example of how to populate this variable with a reference to the specific instance of GameManager, through the inspector via the SerializeField attribute.

In PlayerController class:

[SerializeField] //added this attibute which will affect the `gameManager` variable
private GameManager gameManager;

By adding the SerializeField attribute the variable will be visible in the inspector, even though normally only public variables are shown in inspector. You can then drag the GameManager instance in your scene into this serialized variable on the PlayerController. Now when your scene starts up it will actually have a reference to a specific instance on the GameManager class, so it should be able to call functions etc.


edit: Note that the SerializeField attribute only works for certain types, I am assuming that your GameManager inherits from Unity.Object (or one of its children like GameObject, Component, etc). For more details on serialization through this attribute, check out the official docs for SerializeField

\$\endgroup\$
1
\$\begingroup\$

In a case like this you would either want to make that a public GameManager gameManager or add a [SerializeField]above the variable and simply drag it to the slot to set it.

If you would like to keep it private you could either use a Gameobject.Find(); or something like FindObjectOfType() to set that manager in Start()

After assigning your manager when you call gameManager.GameOver() you will no longer have a null value.

\$\endgroup\$
0
\$\begingroup\$

Some developers hate singletons, but the canonical answer is to make a single long-lived class instance into a singleton, so there is one instance which can be accessed from any other script.

public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }

    void Awake()
    {
        Instance = this;
    }
}

Then any other script can call its public methods as so:

GameManager.Instance.DoSomething();

To simplify this logic so other classes can be singletons easily, use the following helper (this interesting inheritance pattern is called the curiously recurring template pattern):

public abstract class Singleton<T> : MonoBehaviour where T : Singleton<T> // or where T : MonoBehaviour, depending on which types of subclass you need
{
    public static T Instance { get; private set; }

    protected void Awake()
    {
        Instance = (T)this;
    }
}

public class GameManager : Singleton<GameManager>
{
    protected new void Awake()
    {
        base.Awake();
        // do other needed init
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ what is the { get; private set; } doing in this example? \$\endgroup\$
    – Amy Gamble
    Commented Jun 14, 2020 at 16:02
  • \$\begingroup\$ @AmyGamble It serves as the field that stores the long lived instance, but also as the public function that allows other logic to get the reference to the instance by calling GameManager.Instance. A simple public static T Instance; would work the same way but would have the non-ideal characteristic of letting other classes assign to the value, for example setting the instance to null. \$\endgroup\$
    – piojo
    Commented Jun 15, 2020 at 1:54

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .