在Unity中使用C#静态变量的最佳实践是什么?

5
根据在Youtube上演示的教程,可以通过创建静态变量来保存得分并在其他脚本中修改它以奖励玩家。但是建议不要在Unity的C#脚本中使用静态变量。以下是我构建计分系统的步骤:在绑定到UI文本组件以显示分数的ScoreManger中:
public class ScoreManager : MonoBehaviour {
    public static int score;
    Text text;
    private void Awake()
    {
        text = GetComponent<Text>();
    }
    private void FixedUpdate()
    {
        text.text = "Score: " + score;
    }
}

添加分数的步骤:

private void OnCollisionEnter2D(Collision2D collision)
{
    if (collision.gameObject.CompareTag("Player"))
    {
        ScoreManager.score = ScoreManager.score + score;
        Destroy(gameObject);
    }
}

那么,如果不使用静态变量,最好的方法是什么?如果有人能解释为什么不建议使用静态变量,我会更感激。

编辑

尝试了@rogalski在答案中演示的事件处理方式,但IDE显示在ExecuteEvents.Execute<T>的lambda函数上存在类型转换错误。 enter image description here


使用或不使用静态变量是您自己的决定,这取决于许多情况。没有通用答案。 - ElChupacabra
确实,这是一个基于观点的问题,不适合在SO上讨论。 - DavidG
2
@NumLock 这是相同的代码,相同的作者,但不同类型的问题。在标记问题之前,请先阅读问题。 - mrogal.ski
1
@m.rogalski 这是同样的问题,因此已被删除。祝你有美好的一天。 - Num Lock
建议您采用组件化编程,但在您当前的情况下,这并非必须。不过,Unity 也使用了这种方法,所以您可以自由选择。 - Muhammad Faizan Khan
显示剩余2条评论
2个回答

4
你需要记住,Unity是一个基于组件化的引擎,这意味着组件必须独立地完成某些具体的工作。这可以很好地与事件驱动编程方法结合使用,这意味着应用程序(或其各个组件)依赖于某些事件输入。 Unity引入了设计良好的事件(或消息)系统,您应该使用它来代替制作静态字段、类等。
为了指导您正确实现这一点,我将使用最简单的示例。
(编辑->添加了对EventData的定义,这是Unity消息系统所需的)
首先注册您的事件:
public interface ICoinPickedHandler: IEventSystemHandler
{
    void OnCoinPickedUp(CoinPickedEventData eventData);
}

然后将此EventTarget实现到您的脚本中:
public class ScoreManager
    : MonoBehaviour, ICoinPickedHandler
{
    private int m_Score;

    public void OnCoinPickedUp(CoinPickedEventData eventData)
    {
        this.m_Score += eventData.Score;
        eventData.Use();
    }

    // code to display score or whatever ...
}

现在,您只需要触发此事件即可使用它:
private void OnCollisionEnter2D(Collision2D collision)
{
    if (collision.gameObject.CompareTag("Player"))
    {
        // assuming your collision.gameObject
        // has the ICoinPicker interface implemented
        ExecuteEvents.Execute<ICoinPickedHandler>(collision.gameObject, new CoinPickedEventData(score), (a,b)=>a.OnCoinPickedUp(b));
    }
}

现在您需要创建 EventData
public class CoinPickedEventData
    : BaseEventData
{
    public readonly int Score;

    public CoinPickedEventData(int score)
        : base(EventSystem.current)
    {
        Score = score;
    }
}

当然,这个示例需要你知道你想要发送事件的目标。但是如果你不知道目标,你可以使用ExecuteHierarchy方法。

更多关于此主题的信息请参见docs.unity3d.com


1
证明这一点是基于观点的问题:我认为上述内容过于冗长/是事件系统使用方式的糟糕示例。即,得分管理器不需要显式了解硬币 - 按照这种模式,得分管理器最终会有大量接口,用于任何可以增加得分的事物,可能会变得混乱。 - Luke Briggs
1
它应该被使用的方式:一个硬币触发了一个“得分”事件,并订阅了 _那个_。 - Luke Briggs
1
@m.rogalski 我在使用W3C术语 - 简而言之,事件流是:[硬币碰撞] -> [得分事件] -> [由得分侦听器接收]。或例如 [任务完成] -> [得分事件] -> [由得分侦听器接收]。侦听器被称为对事件进行“订阅”。只有一种类型的得分事件,而不是 ScoreManager 具有针对硬币和任务等的代码(“硬币知识”特指 ScoreManager 中的 CoinPickedUp 方法)。 - Luke Briggs
1
理想情况下,您应该有一个第三类,它捕获事件并处理得分、管理器和其他一般事项。类似于ScoreManagerBinder这样的东西。这样,您可以挂接不同的得分管理器,而得分管理器本身不需要担心硬币、钻石、击杀或任何导致得分变化的因素。 - Mark Smit
1
@m.rogalski 这里有一篇带有图表的博客文章(http://futrworld.blogspot.co.uk/2017/03/the-easy-reading-guide-to-unity-event.html),应该会让这个概念更清晰易懂。 - Luke Briggs
显示剩余19条评论

1

首先,最好将静态变量封装在一个方法中:addToScore,这样如果行为发生变化,您只需要实现一次更改即可。

对于静态变量:我认为最好将得分管理器作为singleton实例而不是静态变量。因为这样,如果您决定需要更多的得分管理器、重置得分管理器、在得分管理器中保存更多数据或者其他操作,都会变得更加容易。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接