【问题标题】:Accessing instances from different scripts in c# [duplicate]在c#中从不同脚本访问实例[重复]
【发布时间】:2020-10-21 09:30:25
【问题描述】:

这是我陷入的 OOP 问题。 假设我有这种情况 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();
}

如何从不同的脚本访问播放器实例并使用它,如何在另一个脚本中为它声明其他方法?

【问题讨论】:

  • 如果脚本附加到播放器,那么您可以简单地使用gameObject 字段作为对播放器对象的引用。然后,使用 GetComponent 获取另一个脚本的实例。
  • @Pac0 我每次访问实例时都必须为脚本获取组件,但我不记得我在哪里看到一些 youtuber 只是以另一种更简单的方式访问它,但我不记得如何
  • 我写了一个正确的答案,如果不清楚,请不要犹豫
  • 我必须强烈推荐你观看一些 Unity 教程。

标签: c# unity3d oop


【解决方案1】:

最好的方法是在你的其他脚本中获取玩家参考:

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

或者,如果它不是MonoBehaviour,则在类的构造中传递它

如果不可能,您可以使用Singleton 模式。

这是我使用的一个例子(灵感来自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);
        }
    }
}

您可以创建您的播放器管理器:

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

并访问它

public class OtherClass : MonoBehaviour
{
    public void SomeMethod()
    {
        PlayerManager.Instance.Player.minusHP();
    }
}

【讨论】:

  • 感谢您的回答,我想这就是我一直在寻找的东西,我会花时间理解这种模式,因为我想我可能会非常感激地使用它!
  • 注意不要滥用它。不要陷入意大利面条代码漩涡。尝试通过公共字段或构造函数使用“硬”引用,当您没有其他选择时,您可以使用 Singleton。
【解决方案2】:

假设您的所有脚本都已附加到同一个播放器对象,则可以在任何脚本中使用gameObject 访问播放器对象。

从那里,您可以调用GetComponent&lt;script class&gt; 来获取另一个脚本的实例。

例如:

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

请注意,还有优化的空间。对于 GetComponent 的这种“搜索”只能在每个脚本启动时使用一个字段进行一次。

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();
    }
}

当然这只能在播放器脚本从头附加到播放器对象的情况下才能做到,我想可能是这样的。

如果您必须在许多脚本上重复相同的代码(获取播放器实例),您可以使用继承来“分解”相同的代码。

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();
    }
}

【讨论】:

  • 注意:我不确定在这种情况下是否应该使用Awake 而不是Start。如果您在使用Start 时遇到问题,请尝试改用Awake
  • 您在其他地方的评论副本:“Pac0 是否有任何其他方法可以访问类实例,而不必为我制作的每个脚本都使用 GetComponent,因为想象我不是在统一,而是只使用简单的 CMD 程序执行 p.minusHP() 的一些输入;无论如何,您的回答非常感谢!”
  • @YESSINE:我看到的最简单的方法是设置这些字段,以便您可以在编辑器上“拖放”脚本(即:使用public Player player;!)。您还可以使用“单例模式”(参见 [D.B 的回答 [(stackoverflow.com/users/6224598/d-b)。
  • 您也可以使用继承,请参阅我(已编辑)答案的最后一部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多