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