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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 04:35:55