Unity中如何让按钮事件调用实例化角色的移动方法?
我在做一款2D横版卷轴手游,玩家角色的左右移动和跳跃通过带Event Trigger的按钮的Pointer Up/Down事件控制。现在遇到的问题是,游戏开始前有角色选择场景,玩家选完角色后才会在游戏场景中实例化对应的角色。
按钮原本需要指定角色对象的引用才能调用移动脚本的方法,但可选角色有多个,没法固定绑定单个角色。想知道能不能写个脚本,让移动按钮按下时自动识别并调用当前实例化角色身上的移动脚本,不用在按钮的Event Trigger引用槽里手动指定角色对象?
目前我的角色移动脚本代码如下:
public class TestPlayerMovement : MonoBehaviour { [SerializeField] public float movespeed; Rigidbody2D rb; float xAxisMove; bool moveleft; bool moveright; private void Start() { rb = GetComponent<Rigidbody2D>(); } private void Update() { MovePlayerXAxis(); } public void PointerUpleft() { moveleft = false; } public void PointerDownleft() { moveleft = true; } public void PointerDownRight() { moveright = true; } public void PointerUpRight() { moveright = false; } //Checks if player will move left or right void MovePlayerXAxis() { if (moveleft) { xAxisMove = -1; } else if (moveright) { xAxisMove = 1; } else { xAxisMove = 0; } rb.velocity = new Vector2(xAxisMove * movespeed, rb.velocity.y); } }
解决方案1:单例模式管理当前玩家
这是最直接的方案,让移动脚本成为单例,确保场景中只有一个活跃玩家实例,按钮直接调用单例方法。
修改TestPlayerMovement脚本添加单例逻辑:
public class TestPlayerMovement : MonoBehaviour { public static TestPlayerMovement CurrentPlayer; // 全局单例实例 [SerializeField] public float movespeed; Rigidbody2D rb; float xAxisMove; bool moveleft; bool moveright; private void Awake() { // 保证场景中唯一玩家实例 if (CurrentPlayer != null && CurrentPlayer != this) { Destroy(gameObject); } else { CurrentPlayer = this; } } private void Start() { rb = GetComponent<Rigidbody2D>(); } // 原有Update和移动逻辑保持不变 private void Update() { MovePlayerXAxis(); } public void PointerUpleft() { moveleft = false; } public void PointerDownleft() { moveleft = true; } public void PointerDownRight() { moveright = true; } public void PointerUpRight() { moveright = false; } void MovePlayerXAxis() { /* 原逻辑不变 */ } }
创建按钮事件中转脚本ButtonInputHandler(挂在UI按钮所在对象上):
public class ButtonInputHandler : MonoBehaviour { public void OnLeftDown() { TestPlayerMovement.CurrentPlayer?.PointerDownleft(); } public void OnLeftUp() { TestPlayerMovement.CurrentPlayer?.PointerUpleft(); } public void OnRightDown() { TestPlayerMovement.CurrentPlayer?.PointerDownRight(); } public void OnRightUp() { TestPlayerMovement.CurrentPlayer?.PointerUpRight(); } }
之后把按钮Event Trigger的事件绑定到这个中转脚本的对应方法即可,不管实例化哪个角色,单例都会自动指向当前活跃玩家。
解决方案2:标签查找当前玩家
给所有可选角色的GameObject设置统一标签(比如"Player"),在中转脚本中动态查找:
public class ButtonInputHandler : MonoBehaviour { private TestPlayerMovement _currentPlayer; private void Update() { // 仅当玩家实例为空时查找,避免重复消耗 if (_currentPlayer == null) { GameObject playerObj = GameObject.FindGameObjectWithTag("Player"); if (playerObj != null) { _currentPlayer = playerObj.GetComponent<TestPlayerMovement>(); } } } public void OnLeftDown() { _currentPlayer?.PointerDownleft(); } public void OnLeftUp() { _currentPlayer?.PointerUpleft(); } // 右按钮方法同理 }
如果想优化性能,可以改成在角色实例化完成后,通过自定义事件通知中转脚本更新_currentPlayer引用,避免每帧查找。
解决方案3:事件系统解耦输入与角色
用自定义事件广播输入指令,角色脚本监听事件,完全解耦按钮和角色:
首先定义输入事件类:
public static class PlayerInputEvents { public static event Action OnLeftDown; public static event Action OnLeftUp; public static event Action OnRightDown; public static event Action OnRightUp; public static void TriggerLeftDown() => OnLeftDown?.Invoke(); public static void TriggerLeftUp() => OnLeftUp?.Invoke(); public static void TriggerRightDown() => OnRightDown?.Invoke(); public static void TriggerRightUp() => OnRightUp?.Invoke(); }
修改按钮中转脚本触发事件:
public class ButtonInputHandler : MonoBehaviour { public void OnLeftDown() { PlayerInputEvents.TriggerLeftDown(); } public void OnLeftUp() { PlayerInputEvents.TriggerLeftUp(); } // 其他按钮方法同理 }
最后修改TestPlayerMovement脚本监听事件:
public class TestPlayerMovement : MonoBehaviour { [SerializeField] public float movespeed; Rigidbody2D rb; float xAxisMove; bool moveleft; bool moveright; private void OnEnable() { // 注册事件监听 PlayerInputEvents.OnLeftDown += PointerDownleft; PlayerInputEvents.OnLeftUp += PointerUpleft; PlayerInputEvents.OnRightDown += PointerDownRight; PlayerInputEvents.OnRightUp += PointerUpRight; } private void OnDisable() { // 取消注册防止内存泄漏 PlayerInputEvents.OnLeftDown -= PointerDownleft; PlayerInputEvents.OnLeftUp -= PointerUpleft; PlayerInputEvents.OnRightDown -= PointerDownRight; PlayerInputEvents.OnRightUp -= PointerUpRight; } private void Start() { rb = GetComponent<Rigidbody2D>(); } private void Update() { MovePlayerXAxis(); } // 把原公共方法改为私有,仅内部响应事件 private void PointerUpleft() { moveleft = false; } private void PointerDownleft() { moveleft = true; } private void PointerDownRight() { moveright = true; } private void PointerUpRight() { moveright = false; } void MovePlayerXAxis() { /* 原逻辑不变 */ } }
这种方式扩展性最强,后续新增角色或输入方式都不需要修改按钮逻辑,只要角色脚本监听事件即可。
内容的提问来源于stack exchange,提问作者Ryan Cat

