Как получить доступ к одноэлементным переменным с помощью click и не вызывать NullReferenceException - PullRequest
1 голос
/ 06 марта 2020

это мой класс Singleton, который инициирует 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 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;
        }

    }
}

сейчас, когда я пытаюсь получить к нему доступ через OnPointerClick (), чтобы попытаться изменить переменные EA и EG в методе UseItem (), который система будет создать исключение 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);
    }
}

Ответы [ 3 ]

2 голосов
/ 06 марта 2020

Это больше похоже на вопрос Unity / Monobehavior, чем C#. Пробуждение запускается до начала игры, только один раз. Его можно рассматривать как инициализатор.

Поскольку у вас есть publi c E, вам необходимо предоставить ему экземпляр: путем добавления объекта в интерфейс Unity IDE (перетаскивание) или с помощью кода.

Вместо этого вы также можете использовать теги. Здесь вы можете найти более подробную информацию о том, как это сделать: https://docs.unity3d.com/ScriptReference/MonoBehaviour.Awake.html

2 голосов
/ 06 марта 2020

Я бы go с ленивым инициализирующим свойством, но если вы действительно хотите, чтобы это был Singleton, он должен go, как

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

Тогда обратите внимание, что я не буду делать это в Update (каждый кадр)

void Update()
{
    if (EG >= 5 && EA >= 0)
    {
        EG = 0;
    }
    if (EA <= 4 && EG > 9)
    {
        EA = 5;
    }
}

Скорее задайте 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;
    }
}

Сейчас каждый раз, когда вы устанавливаете одно из этих значений с помощью, например,

Player.E.EA = i;

, соответствующий установщик выполняется, и ваши вспомогательные поля обновляются по событию.


И теперь, да, теперь сомнительно, если Вы даже нуждаетесь в этом как MonoBehaviour ... на самом деле вам даже не нужен экземпляр и, следовательно, ни одного синглтона вообще!

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

Это не обязательно должно быть прикреплено к чему-либо в сцене, но static class, который просто «живет» на протяжении всего сеанса сразу после запуска приложения.

Теперь просто прочитайте и присвойте, например,

Player.EA = i;

Общее примечание:

Следует избегать таких имен, как E, EA и EG! Используйте больше описательных имен! Нужно знать, что такое мент и что означает ценность, просто взглянув на название. Эти короткие пути рано или поздно доставят вам неприятности. Если вы посмотрите на свой код 6 месяцев спустя, вы больше не будете знать, что у вас в голове EA;

0 голосов
/ 06 марта 2020

Измените свой код в этой строке

static public Player E;

С этим кодом

private static Player e;

public static Player E
{
    get
    {
       if(e==null) 
        {
              e= new Player() ;
        } 
       return e;
   } 
    set
    {
      e=value;
    } 
} 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...