1
votes

Comment accéder aux variables singleton avec un clic et ne pas provoquer d'exception NullReferenceException

c'est ma classe Singleton qui lance E

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

maintenant quand j'essaye d'y accéder via OnPointerClick () pour tenter de modifier les variables EA et EG dans la méthode UseItem () le système générerait une NullReferenceException. J'ai essayé de changer l'ordre d'exécution mais je n'ai pas fonctionné. Est-ce parce que j'essaye de le modifier? Désolé, je suis nouveau dans Unity et je ne connais pas ces choses.

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

    }
}


5 commentaires

Vous avez Player.E jamais initialisé donc je suis Null. C'est du petit-lait lorsque vous essayez d'accéder à n'importe quoi sur Player.E, cela vous donne NullReferenceExpception. Vous pouvez appeler Awake avant d'accéder à Player.E .


dofactory.com/net/singleton-design-pattern


code-maze.com/singleton


Avant de fournir des commentaires / réponses, veuillez consulter wiki.unity3d.com/index.php/Singleton et stackoverflow.com/questions/51682613/… . Notez que l'on ne contrôle pas la création d'objets dans Unity3d de la même manière que dans une application C # classique.


Je conseillerais que des noms comme "E" vous causent des problèmes plus tard.


3 Réponses :


-3
votes

Modifiez votre code sur cette ligne

private static Player e;

public static Player E
{
    get
    {
       if(e==null) 
        {
              e= new Player() ;
        } 
       return e;
   } 
    set
    {
      e=value;
    } 
} 

Avec ce code

static public Player E;


5 commentaires

Voici ce qu'indique l'erreur du compilateur après l'avoir modifiée: "Assets / _Script / Player.cs (33,13): erreur CS0200: La propriété ou l'indexeur 'Player.E' ne peut pas être attribué à - il est en lecture seule"


ce n'est pas un singleton


Vous ne pouvez pas utiliser new pour MonoBehaviour !!


Pas de mono-comportement


@AhmedAli dans le code d'OP dans la question qu'il dit public class Player: MonoBehaviour



2
votes

Cela ressemble plus à une question Unity / Monobehavior qu'à une question C #. Awake est déclenché avant le début du jeu, une seule fois. Il peut être traité comme un initialiseur.

Comme vous avez un E public, vous devez lui fournir une instance: en ajoutant un objet dans l'interface Unity IDE (glisser-déposer) ou par code.

Vous pouvez également utiliser des balises à la place. Vous trouverez ici plus d'informations sur la façon de procéder: https://docs.unity3d.com/ScriptReference/MonoBehaviour.Awake.html


2 commentaires

Pas de problème. @ AustinZhangZZRTheBeasT


Note latérale: si vous n'affichez pas le code, cela répondrait en fait à la question comme demandé (notez que @AustinZhangZZRTheBeasT ne semble en fait pas être intéressé par le comportement d'instance unique d'Unity3d, mais plutôt standard [singleton]) csharpindepth.com/Articles/Singleton )). La création d'une seconde instance sur Awake garantit que l'instance statique que vous avez et celle qui est réellement instanciée / utilisée par Unity3d serait différente ... Il vous suffit de "lui fournir une instance: en ajoutant un objet dans L'interface Unity IDE (glisser-déposer) ou par code "suffit à faire fonctionner le code dans le post.



2
votes

J'irais avec une propriété d'initialisation paresseuse mais si vous voulez vraiment que ce soit un Singleton, cela devrait ressembler à

Player.EA = i;

Ensuite, notez que je ne ferais pas cela dans Mettre à jour (chaque cadre)

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

Définissez plutôt EA et EG également des propriétés et faites le cocher uniquement lorsque vous modifiez réellement la valeur:

Player.E.EA = i;

Maintenant, chaque fois que vous définissez l'une de ces valeurs via par exemple

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

le setter correspondant est exécuté et vos champs de sauvegarde mis à jour en fonction des événements.


Et maintenant, oui maintenant, il est douteux que vous ayez même besoin de cela comme MonoBehaviour ... en fait vous n'avez même pas besoin d'une instance et donc pas du tout de Singleton!

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

Cela n'a pas besoin d'être attaché à quoi que ce soit dans la scène mais c'est une classe statique qui "vit" simplement pour toute la session dès que l'application est lancée.

Maintenant, lisez et attribuez simplement par exemple

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

Remarque générale:

Noms tels que E , E Un et un EG doivent être évités! Utilisez des noms plus descriptifs! Il faut savoir ce qu'est ment et ce que représente une valeur en regardant simplement le nom. Ces raccourcis vous causeront des ennuis tôt ou tard. Si vous regardez votre code 6 mois plus tard, vous ne saurez plus ce que EA ment dans votre tête;)


0 commentaires