【问题标题】:Cannot access class variable in member function无法访问成员函数中的类变量
【发布时间】:2016-07-03 07:05:34
【问题描述】:

在 Unity C# 脚本中,我有一个单例游戏控制器对象,其中存储了游戏变量,但在成员函数中访问它们时出现了奇怪的行为。它打印初始化值,而不是当前值。然而,在更新功能中,它每帧打印正确的值。我总结了下面的课程。控制器类对自身有一个静态引用。如果您需要了解其他详细信息,可以询问。我是 C# 和 Unity 的新手,所以我可能缺乏一些明显的答案。

谢谢

public class controller : MonoBehaviour {

    public int[] star = new int[64];

    void Start(){ /* calls another function to set 0 for each star index */ }

    void Update(){  // during gameplay star[0] gets a value of 1
        print(star[0]);  // prints correct value which is 1
    }

    public void checkValue(){
        print(star[0]); // prints 0 incorrectly which should be 1     
    }


}

【问题讨论】:

  • 我找到了问题,但我不知道如何解决它。 checkValue 函数在按下按钮时运行,如果从更新中调用它,它会返回正确的值。不知道是什么原因造成的
  • Start 应该是无操作的,因为无论如何数组都被初始化为零。否则会发生在这个 sn-p 代码中不可见的事情。
  • 在所有情况下是否都是controller 的同一个实例。当您使用非静态成员 star 时,每个实例都有自己的字段(“类变量”)。
  • @JeppeStigNielsen 是的,它与第一次创建的实例相同。请阅读我在其他答案中的评论,我需要知道如何解决它。
  • 只是为了确保:您说的是单例,但这段代码没有实现该模式。你在别的地方有吗? unity中的单例很容易实现。

标签: c# oop unity3d


【解决方案1】:

我已经建立了一个小例子。我创建了一个按钮和一个空的游戏对象GameController。我将此代码添加到GameController

using UnityEngine;
using System.Collections;

public class GameController : MonoBehaviour {

    public static GameController instance;

    [System.NonSerialized] public int[] star = new int[64];

    private void Awake()
    {
        if(instance == null)
            instance = this;
        else if(instance != this)
            Destroy(gameObject);

        DontDestroyOnLoad(gameObject);
    }

    private void Start()
    {
        StartCoroutine(SetAllTo(1));
    }

    // for simulating some changes during the game
    private IEnumerator SetAllTo(int value)
    {
        yield return new WaitForSeconds(2.0f);

        for(int i = 0; i < star.Length; i++)
            star[i] = value;

        Debug.Log("Setting done");
    }

    public void PrintFirst()
    {
        Debug.Log(star[0]);
    }
}

现在我向按钮添加了一个OnClick 事件,将GameController 游戏对象拖到插槽中并选择了PrintFirst

我开始游戏并在 Coroutine 日志之前和之后单击按钮一次,控制台给出以下信息:

0
Setting Done
1

编辑:
OnClick 事件的游戏对象必须在场景中,不能是 assets 文件夹中的预制件。

【讨论】:

  • 这很有趣。您是否将场景中存在的 GameController 拖到 OnClick 事件中?这也适用于我,但如果我拖动预制件,它就不起作用。
  • 哦,是的,来自现场。如果by dragging a prefab 您实际上是指将其从资产文件夹中拖出:那将永远起作用。预制件不是游戏的一部分,只是硬盘上的蓝图。您只能引用场景中的内容(层次结构)或以某种方式连接到场景中的某些内容(例如使用 new 创建的 MonoBehaviour 脚本中的非 MonoBehaviour 脚本)。
  • 好的,我知道了,但我可以运行预制件的功能,例如它打印一个字符串。它返回它看起来的类变量的默认值。但是它如何在不调用构造函数的情况下调用它们呢?我在构造函数中放了一个打印,单击按钮时它没有打印。非常感谢您的帮助!
  • 派生自MonoBehaviour 的类没有构造函数(您不能使用new 创建对此类类的引用)。我真的不知道,当你在预制件上试用它时会发生什么。
  • 您好 Gunnar,为了避免我输入答案,我编辑了您的答案以表明“star”绝对应该是 [System.NonSerialized] public
【解决方案2】:

似乎在调用 ckeckValue 之前,您的外部调用中也可能发生了对 start 函数的调用

通过在每个函数中放置断点来尝试调试,您​​可以了解发生了什么。

【讨论】:

  • 开始函数在游戏开始时运行一次,不再运行。 checkValue 函数在按下按钮时运行。如果是这样的话,我猜在单击按钮后,案例更新功能不会返回正确的。
  • 添加公共属性并使用字段星号并将字段设为私有。在设置器中添加断点以观察何时设置值并使用代码映射查看调用者
【解决方案3】:

start 函数在游戏开始时运行一次,不会再次运行。

为此你有模式 - Singleton

第二件事为什么要调用外部函数来初始化类成员?在您的默认承包商中执行此操作。

- C# 默认构造函数已在 int[] 中初始化 0。

如果您仍然要从代码中的其他范围开始运行,请将其设为公开(c# 默认将变量/方法设为私有)

【讨论】:

  • 感谢您的帮助。我还在学习设计。当我使用 print 进行调试时,我看到 Start 在场景中运行了一次。我在 Start 中调用另一个函数的原因是它必须根据游戏的状态进行保存和加载,它们是类函数而不是外部函数。跨度>
  • 我找到了问题所在。给出错误结果的函数由 Unity 中的按钮调用。不知何故,当按钮被按下时,它不会寻找场景中存在的控制器对象,而是使用类本身,其星形变量的默认值为 0。但更有趣的是,它不会在此过程中触发其构造函数。如果我告诉函数专门查找场景中存在的该类的实例并使用它的变量,那么它就可以工作了。
  • 嗨莱昂。请注意,这是一个 UNITY3D 项目 - 没有单例。
  • 一种单例方式来创建你的类的一个实例并防止新实例被创建超过一次它是设计模式而不是技术。 (设计模式 - 是软件设计中常见问题的通用可重复解决方案。)
  • 嗨莱昂。 我不知道我能说清楚多少,这是针对游戏引擎 Unity3D 的。它是一个 ECS 系统,它没有单例。 (你怎么能做一个“游戏对象的单一组件”。这意味着什么?)ECS 系统与 OO 完全没有联系。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-12
  • 1970-01-01
  • 2016-08-29
  • 1970-01-01
  • 2012-12-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多