C# 如何通过单击访问单例变量而不导致NullReferenceException
这是我的独生子女课程,它启动了EC# 如何通过单击访问单例变量而不导致NullReferenceException,c#,unity3d,C#,Unity3d,这是我的独生子女课程,它启动了E using System.Collections.Generic; using UnityEngine; public class Player : MonoBehaviour { static public Player E; public float EA = 5; public float EG = 0; // Start is called before the first frame update void
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
static public Player E;
public float EA = 5;
public float EG = 0;
// Start is called before the first frame update
void Awake()
{
if (E == null)
{
E = this;
}
else
{
Debug.LogError("Hero.Awake() - attempted to assign second Hero.S");
}
}
// Update is called once per frame
void Update()
{
if (EG >= 5 && EA >= 0)
{
EG = 0;
}
if (EA <= 4 && EG > 9)
{
EA = 5;
}
}
}
使用System.Collections.Generic;
使用UnityEngine;
公共类玩家:单一行为
{
静态公共播放器E;
公共浮动EA=5;
公共浮动EG=0;
//在第一帧更新之前调用Start
无效唤醒()
{
如果(E==null)
{
E=这个;
}
其他的
{
Debug.LogError(“Hero.Awake()-试图分配第二个Hero.S”);
}
}
//每帧调用一次更新
无效更新()
{
如果(例如>=5&&EA>=0)
{
EG=0;
}
如果(EA 9)
{
EA=5;
}
}
}
现在,当我试图通过OnPointerClick()访问它,试图修改UseItem()方法中的变量EA和EG时,系统将生成一个NullReferenceException。我尝试过切换执行顺序,但没有成功。是因为我想修改它吗?很抱歉,我是Unity的新手,不知道这些事情
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class Slot : MonoBehaviour , IPointerClickHandler
{
public int ID;
public string type;
public string description;
public bool empty;
public Texture2D icon;
// Start is called before the first frame update
public void OnPointerClick (PointerEventData pointerEventData)
{
useItem();
Debug.Log("clicked" + transform.name);
}
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void useItem()
{
for (int i = 0; i < 10; i++)
{
if (transform.name == "slot(" + (i + 1) + ")")
{
Player.E.EA = i;
}
}
Debug.Log(Player.E.EA);
}
}
使用系统集合;
使用System.Collections.Generic;
使用UnityEngine;
使用UnityEngine.EventSystems;
公共类插槽:MonoBehavior,IPoClickHandler
{
公共int ID;
公共字符串类型;
公共字符串描述;
公共布尔空;
公共纹理2D图标;
//在第一帧更新之前调用Start
PointerEventData PointerEventData上的公共无效点单击(PointerEventData PointerEventData)
{
useItem();
Log(“单击”+transform.name);
}
void Start()
{
}
//每帧调用一次更新
无效更新()
{
}
公用项目()
{
对于(int i=0;i<10;i++)
{
if(transform.name==“插槽(“+(i+1)+”)
{
Player.E.EA=i;
}
}
Debug.Log(Player.E.EA);
}
}
在此行中修改您的代码
static public Player E;
使用此代码
private static Player e;
public static Player E
{
get
{
if(e==null)
{
e= new Player() ;
}
return e;
}
set
{
e=value;
}
}
这看起来更像是一个统一/单一行为问题,而不是一个C#问题。 唤醒在游戏开始前触发,仅一次。 可以将其视为初始值设定项 由于您有一个公共的E,您需要为它提供一个实例:通过在Unity IDE接口中添加一个对象(拖放)或通过代码 您也可以使用标记。 在这里,您可以找到有关如何操作的更多信息:
我会选择惰性初始化属性,但如果您真的希望它是一个单例,它应该是这样的
private static Player _instance;
public static Player E
{
get
{
// if already exists return it directly
if(_instance) return _instance;
// otherwise find it in the scene
_instance = FindObjectOfType<Player>();
// if found return it
if(_instance) return _instance;
// otherwise create it now
// note that anything you have to configure via the Inspector
// will not be there so you would need to find another way to
// initialize all values
_instance = new GameObject ("Player").AddComponent<Player>();
return _instance;
}
}
private void Awake()
{
if(_instance && _instance != this)
{
// another instance already exists!
Destroy (gameObject);
return;
}
_instance = this;
}
而是将EA
和EG
也设置为属性,并仅在实际更改值时进行检查:
private int _ea = 5;
private int _eg = 0;
public int EA
{
get => _ea;
set
{
_ea = value;
if (_eg >= 5 && _ea >= 0) _eg = 0;
if (_ea <= 4 && _eg > 9) _ea = 5;
}
}
public int EG
{
get => _eg;
set
{
_eg = value;
if (_eg >= 5 && _ea >= 0) _eg = 0;
if (_ea <= 4 && _eg > 9) _ea = 5;
}
}
执行相应的setter,并根据事件驱动更新备份字段
现在,是的,如果你甚至需要将其作为一种
单一行为
。。。实际上,您甚至不需要实例,因此根本不需要单例
public static class Player
{
private static int _ea = 5;
private static int _eg = 0;
public static int EA
{
get => _ea;
set
{
_ea = value;
if (_eg >= 5 && _ea >= 0) _eg = 0;
if (_ea <= 4 && _eg > 9) _ea = 5;
}
}
public static int EG
{
get => _eg;
set
{
_eg = value;
if (_eg >= 5 && _ea >= 0) _eg = 0;
if (_ea <= 4 && _eg > 9) _ea = 5;
}
}
}
一般说明:
应该避免使用
E
、EA
和EG
等名称!使用更多描述性名称!只要看一看名称,就应该知道什么是默认值以及值代表什么。这些速记迟早会给你带来麻烦的。如果您在6个月后查看代码,您将不再知道您头脑中的EA
ment;) 您的Player.E从未初始化,因此我将其设置为Null。当您尝试访问Player上的任何内容时,这就是问题所在。它会为您提供NullReferenceExpection。在访问Player.E
之前,您可能需要呼叫Awake
。在提供评论/答案之前,请查看并确认。请注意,在Unity3d中控制对象创建的方式与在常规C#应用程序中控制对象创建的方式不同。我建议使用“E”之类的名称会导致以后出现问题。这是我修改后编译器错误所述的:“Assets/_Script/Player.cs(33,13):错误CS0200:无法为属性或索引器“Player.E”赋值--它是只读的”这不是单一行为您不能使用new
进行monobhavior
!!不MonoBehaviour@AhmedAli在OP的问题代码中,它说公共类玩家:monobhavior
没问题。@austinzhangzzr背面注:如果你不显示代码,这实际上会回答问题(请注意,@austinzhangzzerthebeast实际上似乎对Unity3d单实例行为不感兴趣,而是标准的[singleton])。在Awake
上创建第二个实例可以确保您拥有的静态实例与Unity3d实际实例化/使用的实例不同。。。只要“您需要为它提供一个实例:通过在Unity IDE界面(拖放)中添加一个对象或通过代码”就足以在后期工作中获得代码。
Player.E.EA = i;
public static class Player
{
private static int _ea = 5;
private static int _eg = 0;
public static int EA
{
get => _ea;
set
{
_ea = value;
if (_eg >= 5 && _ea >= 0) _eg = 0;
if (_ea <= 4 && _eg > 9) _ea = 5;
}
}
public static int EG
{
get => _eg;
set
{
_eg = value;
if (_eg >= 5 && _ea >= 0) _eg = 0;
if (_ea <= 4 && _eg > 9) _ea = 5;
}
}
}
Player.EA = i;