0

This is an OOP issue that i'm stuck in.. Let's say I have this situation Player.cs :

public class Player
{
    int hp,armor;
    string nickName;
    public Player()
    {
        hp=3;
        armor=3;
        nickName="Unknown";
    }
    public void minusHP()
    {
        hp--;
    }
}

PlayerManager.cs

public class PlayerManager
{
    Player p = new Player();
}

TakeDMG.cs

public class TakeDMG
{
    //if event happens then : 
    p.minusHP();
}

How can I access the player instance from different scripts and use it and how can I declare other methods for it in another script?

Martin Costello
  • 9,672
  • 5
  • 60
  • 72
YESSINE
  • 274
  • 1
  • 9
  • If the scripts are attached to the player, then you can simply use the `gameObject` field as reference to the player object. Then, use GetComponent to get the instance of another script. – Pac0 Oct 21 '20 at 09:35
  • @Pac0 i'd have to GetComponent for the script everytime i'm accessing the instance but I dont remember where I saw some youtuber just accessing it in another simpler way but I dont remember how – YESSINE Oct 21 '20 at 09:39
  • I wrote a proper answer, don't hesitate to comment if it's not clear – Pac0 Oct 21 '20 at 09:42
  • I must strongly recommend you watch some of the Unity tutorials. – Immersive Oct 21 '20 at 13:50

2 Answers2

1

The best way to do it is to get the player reference on your other script:

public class TakeDMG : MonoBehaviour
{
    //This is the player reference to set on editor mode
    public Player player;
}

Or you pass it at construction of the class if it's not a MonoBehaviour

If it's not possible you can use the Singleton pattern.

Here's an exemple of what I use (inspired from here):

public abstract class Singleton : MonoBehaviour
{
    public static bool Quitting { get; private set; }
    private void OnApplicationQuit()
    {
        Quitting = true;
    }

    private void OnDestroy()
    {
        Quitting = true;
    }
}

public class Singleton<T> : Singleton where T : MonoBehaviour
{
    /// <summary>
    /// Singleton instance
    /// </summary>
    protected static T instance;

    /// <summary>
    /// Access singleton instance through this propriety.
    /// </summary>
    public static T Instance
    {
        get
        {
            if (Quitting)
            {
                return null;
            }

            if (instance == null)
            {
                instance = FindObjectOfType<T>();
                DontDestroyOnLoad(instance.gameObject);
            }

            return instance;
        }
    }

    /// <summary>
    /// Awake the singleton -> add instance to the same gameobject than this class
    /// </summary>
    protected virtual void Awake()
    {
        if (instance == null)
        {
            instance = Instance;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            // We have already an instance, we destroy this one
            if(this != instance)
                Destroy(gameObject);
        }
    }
}

    public class SingletonInterface<T> : Singleton where T : MonoBehaviour
{
    /// <summary>
    /// Singleton instance
    /// </summary>
    protected static T instance;

    /// <summary>
    /// Access singleton instance through this propriety.
    /// </summary>
    protected static T Instance
    {
        get
        {
            if (Quitting)
            {
                return null;
            }

            if (instance == null)
            {
                instance = FindObjectOfType<T>();
                DontDestroyOnLoad(instance.gameObject);
            }

            return instance;
        }
    }

    /// <summary>
    /// Awake the singleton -> add instance to the same gameobject than this class
    /// </summary>
    protected virtual void Awake()
    {
        if (instance == null)
        {
            instance = Instance;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            // We have already an instance, we destroy this one
            if(this != instance)
                Destroy(gameObject);
        }
    }
}

You can create your player manager :

public class PlayerManager : Singleton<PlayerManager>
{
    public Player Player;
}

And access to it

public class OtherClass : MonoBehaviour
{
    public void SomeMethod()
    {
        PlayerManager.Instance.Player.minusHP();
    }
}
D.B
  • 596
  • 4
  • 17
  • thanks for the answer i guess this is what i was looking for i'll spend my time understanding this pattern since i think i'll probably be using it a lot really appreciated! – YESSINE Oct 21 '20 at 10:34
  • Take care to not abuse of it. Don't fall into the spagetti code vortex. Try to use "hard" reference through public field or constructor and when you have no other option you can use the Singleton. – D.B Oct 21 '20 at 11:31
0

Supposing all your scripts are already attached to the same player object, the player object can be accessed using gameObject in any of the script.

From there, you can call GetComponent<script class> to get the instance of another script.

For instance:

public class TakeDMG
{
    //if event happens then : 
    public void SomeEvent()
    {
        var p = gameObject.GetComponent<Player>();
        p.minusHP();
    }
}

Note that there is room for optimisation. This "search" for GetComponent can be done only once per script at startup, using a field.

public class TakeDMG
{
    Player player;   // private field to store the reference to Player script;

    public Start()
    {
        player = gameObject.GetComponent<Player>();
    }

    //if event happens then : 
    public void SomeEvent()
    {
        player.minusHP();
    }
}

Of course this can be done only of Player script is attached from the beginning to the player object, I think that's probably the case.

If you must repeat same code on many scripts (get the player instance), you can use inheritance to "factor out" the identical code.

public class PlayerScript
{
    protected Player player;   // field to store the reference to Player script;

    public virtual Start()
    {
        player = gameObject.GetComponent<Player>();
    }
}

// This scripts inherit from PlayerScript
public class TakeDMG : PlayerScript
{
    //if event happens then : 
    public void SomeEvent()
    {
        player.minusHP();
    }
}


// This one as well
public class AnotherScript : PlayerScript
{
    //if event happens then : 
    public void SomeOtherEvent()
    {
        player.minusHP();
    }
}

Pac0
  • 21,465
  • 8
  • 65
  • 74
  • Note: I'm unsure if I should use `Awake` instead of `Start` in this case. If you run into trouble with `Start`, try using `Awake` instead. – Pac0 Oct 21 '20 at 09:44
  • copy of your comment elsewhere: "Pac0 is there any other way to access class instances without having to GetComponent for each script I make because imagine i'm not on unity but making just a CMD program with simple some input to execute p.minusHP(); Your is answer is very appreciate thank you anyways!" – Pac0 Oct 21 '20 at 09:47
  • @YESSINE: The simplest way I see is to set up those fields so you can "drag-drop" the scripts on the editor (i.e.: use `public Player player;`!). You can also use the "singleton pattern" (see [D.B's answe[(https://stackoverflow.com/users/6224598/d-b). – Pac0 Oct 21 '20 at 09:50
  • you can also use inheritance, see last part of my (edited) answer. – Pac0 Oct 21 '20 at 09:56