Home > front end >  Add object to list before "OnEnable"
Add object to list before "OnEnable"

Time:11-08

I had and issue when I Instantiate a game object. I have a list of targets and when I instantiate my game object, I add this object to a list and set an Id to my object using PlayerPrefs. my issue is that I have call a method onEnable that use the list and because OnEnable happen before the next line on the script, I get a ArgumentOutOfRangeException. I can disable my prefab but I dont like the idea that if someone active it by mistake it will give me an error. someone know how can I add the object to my list before "OnEnable" happen?

this is my script thast Instantiate the game object:

    private void CreateTargetOnNetwork(Vector3 position, Quaternion rotation)
    {
        PlayerPrefs.SetInt("Index", index);
        targetsList.Add(Instantiate(Resources.Load("TargetPrefabNetwork") as GameObject, position, rotation, areaTargetManager.GetCurrentArea().transform));
        index  ;
    }
    [PunRPC]
    private void UpdateTargetOnOffOnNetwork(int id, bool status)
    {
        targetsList[id].SetActive(status);
    }

and this is my script on the game object:

    private void Awake()
    {
        _targetIndex = PlayerPrefs.GetInt("Index");
        PlayerPrefs.DeleteKey("Index");
        targetManager = FindObjectOfType<TargetManager>();
        targetManagerPhotonView = targetManager.GetComponent<PhotonView>();
    }
    private void OnEnable()
    {
        targetManagerPhotonView.RPC("UpdateTargetOnOffOnNetwork", RpcTarget.AllBuffered, _targetIndex, true);
    }

I work with photon but this is not my issue so the RPC is just like calling a method.

CodePudding user response:

You could add a second component to the instantiated GameObject with an earlier execution order, and have the component invoke a static event during its initialization, which will take place before the other component is initialized.

[DefaultExecutionOrder(-1000)]
public sealed class OnBefore : MonoBehaviour
{
    public static event Action<GameObject> Enabled;
    public static event Action<GameObject> Disabled;
    
    private void OnEnable() => Enabled?.Invoke(gameObject);
    private void OnDisable() => Disabled?.Invoke(gameObject);
}

Usage:

private void CreateTargetOnNetwork(Vector3 position, Quaternion rotation)
{
    PlayerPrefs.SetInt("Index", index);
    var prefab = Resources.Load<GameObject>("TargetPrefabNetwork");
    
    OnBefore.Enabled  = OnBeforeEnabled;
    Instantiate(prefab);
    OnBefore.Enabled -= OnBeforeEnabled;

    index  ;

    void OnBeforeEnabled(GameObject instance)
    {
        targetsList.Add(instance, position, rotation, areaTargetManager.GetCurrentArea().transform));
    }
}

CodePudding user response:

One solution I could think of would be the other way round and prevent OnEnable in that one special initialization case via a flag.

Then after adding it to the list make the call once explicitly, set the flag and from there on use OnEnable.

With a bit of refactoring I would probably do something like

private Action<YourTargetClass> onEnableDisable;
private bool initialized;

public int Index => _targetIndex;

public void Initialize(int index, Action<YourTargetClass> onEnableDisableCallback)
{
    _targetIndex = index;
    onEnableDisable = onEnableDisableCallback;
    initialized = true;

    onEnableDisable?.Invoke(this);
}

private void OnEnable()
{
    if(initialized) onEnableDisable?.Invoke(this);
}

private void OnDisable()
{
    if(initialized)  onEnableDisable?.Invoke(this);
}

And then load it like

private void CreateTargetOnNetwork(Vector3 position, Quaternion rotation)
{
    var targetPrefab = Resources.Load("TargetPrefabNetwork");
    var newTarget = Instantiate(targetPrefab);
    targetsList.Add(newTarget, position, rotation, areaTargetManager.GetCurrentArea().transform));

    newTarget.Initialize(index, OnTargetEnableDisable);

    index  ;
}

// Leave all networked code together
// Your target doesn't need to know 
private void OnTargetEnableDisable(YourTargetClass target)
{
    photoView.RPC(nameof(UpdateTargetOnOffOnNetwork), RpcTarget.AllBuffered, target.Index, target.enabled));
}

[PunRPC]
private void UpdateTargetOnOffOnNetwork(int id, bool status)
{
    targetsList[id].SetActive(status);
}
  • Related