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

如何让运行时实例化的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 09:45:31