You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在OnEnable执行前将实例化对象添加至目标列表?

实例化对象列表索引越界问题解决方案

问题描述

实例化游戏对象时遇到逻辑顺序冲突:

  • 维护一个目标列表targetsList,实例化对象后将其加入列表,同时通过PlayerPrefs为对象分配ID
  • 对象的OnEnable生命周期方法会先于“添加至列表”的代码执行,导致OnEnable中调用的方法访问列表时抛出ArgumentOutOfRangeException
  • 不想通过禁用预制体规避,担心误激活引发其他错误

实例化脚本代码

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

对象挂载脚本代码

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

注:使用Photon,但问题与Photon无关,RPC可视为普通方法调用

解决方案

方法1:实例化时先禁用,加入列表后再激活

修改实例化逻辑,先让对象处于禁用状态,确保加入列表后再触发OnEnable:

private void CreateTargetOnNetwork(Vector3 position, Quaternion rotation)
{
    PlayerPrefs.SetInt("Index", index);
    // 实例化后立即禁用,避免提前触发OnEnable
    GameObject newTarget = Instantiate(Resources.Load("TargetPrefabNetwork") as GameObject, position, rotation, areaTargetManager.GetCurrentArea().transform);
    newTarget.SetActive(false);
    // 添加到列表后再激活,此时OnEnable执行时列表已包含该对象
    targetsList.Add(newTarget);
    index++;
    newTarget.SetActive(true);
}

方法2:直接传递索引,弃用PlayerPrefs

取消PlayerPrefs的全局状态传递,实例化后直接给对象脚本赋值索引,彻底规避顺序问题:

首先修改对象脚本,将索引改为可外部赋值的公开字段:

public int _targetIndex;
private TargetManager targetManager;
private PhotonView targetManagerPhotonView;

private void Awake()
{
    targetManager = FindObjectOfType<TargetManager>();
    targetManagerPhotonView = targetManager.GetComponent<PhotonView>();
}

private void OnEnable()
{
    targetManagerPhotonView.RPC("UpdateTargetOnOffOnNetwork", RpcTarget.AllBuffered, _targetIndex, true);
}

然后修改实例化脚本:

private void CreateTargetOnNetwork(Vector3 position, Quaternion rotation)
{
    GameObject newTarget = Instantiate(Resources.Load("TargetPrefabNetwork") as GameObject, position, rotation, areaTargetManager.GetCurrentArea().transform);
    // 先添加到列表,再赋值索引
    targetsList.Add(newTarget);
    newTarget.GetComponent<TargetScript>()._targetIndex = index;
    index++;
}

这种方式逻辑更直接,完全避免了全局状态带来的不确定性。

方法3:延迟执行OnEnable中的逻辑

如果不想修改实例化流程,可通过延迟调用确保列表已完成添加:

private void OnEnable()
{
    // 下一帧执行,给实例化脚本足够时间将对象加入列表
    Invoke(nameof(SendUpdateRPC), 0);
}

private void SendUpdateRPC()
{
    targetManagerPhotonView.RPC("UpdateTargetOnOffOnNetwork", RpcTarget.AllBuffered, _targetIndex, true);
}

注:该方法依赖帧执行顺序,稳定性不如前两种,仅作为临时方案使用

内容的提问来源于stack exchange,提问作者Noam Riahi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 19:10:52