如何让运行时实例化的Prefab绑定到MoveController沿路径移动?
问题
现有一套预制体移动系统,当前MoveController仅支持绑定单个带Moveable脚本的对象并沿预设路径移动,运行正常。需要实现运行时实例化的新预制体自动绑定到MoveController并沿同一路径移动的功能。
原脚本
Moveable 脚本(负责移动逻辑+实例化预制体)
public class Moveable : MonoBehaviour { [SerializeField] private float _speedMetersPerSecond = 25f; private Vector3? _destination; private Vector3 _startPosition; private float _totalLerpDuration; private float _elapsedLerpDuration; private Action _onCompleteCallback; public GameObject Electron; // Update is called once per frame void Update() { if (Input.GetKeyDown(KeyCode.Space)) { var NextOnPath = Instantiate(Electron, transform.position, Quaternion.identity); NextOnPath.AddComponent<Moveable>(); } if (_destination.HasValue == false) return; if (_elapsedLerpDuration >= _totalLerpDuration && _totalLerpDuration > 0) return; _elapsedLerpDuration += Time.deltaTime; float percent = (_elapsedLerpDuration / _totalLerpDuration); Debug.Log($"{percent} = {_elapsedLerpDuration} / {_totalLerpDuration}"); transform.position = Vector3.Lerp(_startPosition, _destination.Value, percent); } public void MoveTo(Vector3 destination, Action onComplete = null) { var distanceToNextWaypoint = Vector3.Distance(transform.position, destination); _totalLerpDuration = distanceToNextWaypoint / _speedMetersPerSecond; _startPosition = transform.position; _destination = destination; _elapsedLerpDuration = 0f; _onCompleteCallback = onComplete; } }
MoveController 脚本(路径控制)
using System.Linq; public class MoverController : MonoBehaviour { [SerializeField] private Moveable target; private List<Transform> _waypoints; private int _nextWaypointIndex; private void OnEnable() { _waypoints = GetComponentsInChildren<Transform>().ToList(); _waypoints.RemoveAt(0); MoveToNextWaypoint(); } private void MoveToNextWaypoint() { var targetWaypointTransform = _waypoints[_nextWaypointIndex]; target.MoveTo(targetWaypointTransform.position, MoveToNextWaypoint); target.transform.LookAt(_waypoints[_nextWaypointIndex].position); _nextWaypointIndex++; if (_nextWaypointIndex >= _waypoints.Count) _nextWaypointIndex = 0; } }
解决方案
核心思路是将MoveController改为管理多个Moveable对象,同时在实例化新预制体时自动将其注册到控制器中。
1. 修改 MoveController,支持多对象管理
把单个目标改为列表存储,添加注册/移除方法,为每个对象独立处理路径循环:
using System.Collections.Generic; using System.Linq; public class MoverController : MonoBehaviour { private List<Moveable> _moveableObjects = new List<Moveable>(); private List<Transform> _waypoints; private void OnEnable() { _waypoints = GetComponentsInChildren<Transform>().ToList(); _waypoints.RemoveAt(0); // 初始化已绑定对象的移动 foreach (var moveable in _moveableObjects) { StartMoveForObject(moveable); } } // 供外部调用的注册方法 public void RegisterMoveable(Moveable moveable) { if (!_moveableObjects.Contains(moveable)) { _moveableObjects.Add(moveable); StartMoveForObject(moveable); } } // 供外部调用的移除方法(防止内存泄漏) public void UnregisterMoveable(Moveable moveable) { _moveableObjects.Remove(moveable); } // 为单个对象启动路径循环 private void StartMoveForObject(Moveable moveable) { MoveToNextWaypointForObject(moveable, 0); } // 单个对象的路径移动逻辑 private void MoveToNextWaypointForObject(Moveable moveable, int currentWaypointIndex) { var targetWaypoint = _waypoints[currentWaypointIndex]; moveable.MoveTo(targetWaypoint.position, () => { int nextIndex = (currentWaypointIndex + 1) % _waypoints.Count; MoveToNextWaypointForObject(moveable, nextIndex); }); moveable.transform.LookAt(targetWaypoint.position); } }
2. 修改 Moveable,实例化后自动注册
调整实例化逻辑,让新生成的对象自动绑定并注册到控制器,同时完善移动完成的回调触发:
public class Moveable : MonoBehaviour { [SerializeField] private float _speedMetersPerSecond = 25f; [SerializeField] private MoverController _moveController; // 可在Inspector绑定,或动态查找 private Vector3? _destination; private Vector3 _startPosition; private float _totalLerpDuration; private float _elapsedLerpDuration; private Action _onCompleteCallback; public GameObject Electron; private void Awake() { // 动态查找控制器(如果Inspector未绑定) if (_moveController == null) { _moveController = FindObjectOfType<MoverController>(); } } void Update() { if (Input.GetKeyDown(KeyCode.Space)) { var NextOnPath = Instantiate(Electron, transform.position, Quaternion.identity); // 获取或添加Moveable组件 var newMoveable = NextOnPath.GetComponent<Moveable>() ?? NextOnPath.AddComponent<Moveable>(); // 绑定控制器并注册 newMoveable._moveController = _moveController; _moveController.RegisterMoveable(newMoveable); } if (_destination.HasValue == false) return; if (_elapsedLerpDuration >= _totalLerpDuration && _totalLerpDuration > 0) { _onCompleteCallback?.Invoke(); _destination = null; return; } _elapsedLerpDuration += Time.deltaTime; float percent = (_elapsedLerpDuration / _totalLerpDuration); transform.position = Vector3.Lerp(_startPosition, _destination.Value, percent); } public void MoveTo(Vector3 destination, Action onComplete = null) { var distanceToNextWaypoint = Vector3.Distance(transform.position, destination); _totalLerpDuration = distanceToNextWaypoint / _speedMetersPerSecond; _startPosition = transform.position; _destination = destination; _elapsedLerpDuration = 0f; _onCompleteCallback = onComplete; } // 对象销毁时从控制器移除 private void OnDestroy() { _moveController?.UnregisterMoveable(this); } }
3. 额外优化提示
- 如果预制体
Electron本身已经挂载Moveable脚本,实例化时可跳过AddComponent步骤,避免重复; - 若场景中有多个
MoverController,建议通过标签或特定路径查找,避免绑定错误。
内容的提问来源于stack exchange,提问作者SChen
相关产品推荐
相关产品推荐

