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

Unity 2D手游:摇杆移动与射击的触摸事件分离方案问询

问题描述

我正在开发一款2D移动端游戏,其中一个脚本通过摇杆(joystick)控制角色移动,另一个脚本实现点击屏幕任意位置发射物体的功能。当前问题是操作摇杆时会同时触发对应方向的射击,请问如何分离触摸事件,实现摇杆移动时不触发射击,同时支持移动与射击并行操作?

现有代码

移动控制代码

private void Update()
{
    Vector2 moveInput = new Vector2(joystick.Horizontal, joystick.Vertical);
    moveAmount = moveInput.normalized * speed;
}

射击控制代码

private void Update()
{
    Vector2 direction = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
    float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
    Quaternion rotation = Quaternion.AngleAxis(angle - 90, Vector3.forward);
    transform.rotation = rotation;

    if(Input.GetMouseButton(0))
    {
        if (Time.time >= shotTime)
        {
            Instantiate(projectile, shotPoint.position, transform.rotation);
            shotTime = Time.time + timeBetweenShots;
        }
    }
}
解决方案

核心逻辑是判断触摸事件是否发生在摇杆区域内,摇杆区域内的触摸只响应移动,区域外的触摸才触发射击,同时支持多触摸并行操作。

方法一:利用摇杆UI区域判断(推荐)

如果使用的是UI类摇杆插件(比如Unity官方或第三方UI摇杆),直接通过摇杆的RectTransform判断触摸点是否在其范围内:

修改射击脚本:

private RectTransform joystickRect;

void Start()
{
    // 获取摇杆的RectTransform组件
    joystickRect = joystick.GetComponent<RectTransform>();
}

private void Update()
{
    Vector2 direction = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
    float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
    Quaternion rotation = Quaternion.AngleAxis(angle - 90, Vector3.forward);
    transform.rotation = rotation;

    if(Input.GetMouseButton(0))
    {
        Vector2 touchPosInUISpace;
        // 将屏幕触摸点转换为摇杆UI的局部坐标
        RectTransformUtility.ScreenPointToLocalPointInRectangle(joystickRect, Input.mousePosition, Camera.main, out touchPosInUISpace);
        
        // 只有触摸点不在摇杆区域内时,才触发射击
        if (!joystickRect.rect.Contains(touchPosInUISpace))
        {
            if (Time.time >= shotTime)
            {
                Instantiate(projectile, shotPoint.position, transform.rotation);
                shotTime = Time.time + timeBetweenShots;
            }
        }
    }
}

方法二:手动标记摇杆触摸状态

如果是自定义摇杆,在摇杆脚本中添加触摸状态标记,射击脚本根据标记判断是否触发:

  1. 修改移动摇杆脚本:
// 公开触摸状态,供射击脚本读取
public bool isTouchingJoystick = false;

// 实现UI触摸接口,标记触摸状态
public void OnPointerDown(PointerEventData eventData)
{
    isTouchingJoystick = true;
}

public void OnPointerUp(PointerEventData eventData)
{
    isTouchingJoystick = false;
}

private void Update()
{
    Vector2 moveInput = new Vector2(joystick.Horizontal, joystick.Vertical);
    moveAmount = moveInput.normalized * speed;
}
  1. 修改射击脚本:
// 引用摇杆控制脚本
public MoveScript moveScript;

private void Update()
{
    Vector2 direction = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
    float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
    Quaternion rotation = Quaternion.AngleAxis(angle - 90, Vector3.forward);
    transform.rotation = rotation;

    // 只有未触摸摇杆时,才响应射击
    if(Input.GetMouseButton(0) && !moveScript.isTouchingJoystick)
    {
        if (Time.time >= shotTime)
        {
            Instantiate(projectile, shotPoint.position, transform.rotation);
            shotTime = Time.time + timeBetweenShots;
        }
    }
}

额外优化:支持多触摸并行操作

如果需要同时用两根手指操作(一根摇杆移动,一根触摸射击),可以遍历触摸事件区分不同触摸ID:

修改射击脚本:

private RectTransform joystickRect;

void Start()
{
    joystickRect = joystick.GetComponent<RectTransform>();
}

private void Update()
{
    Vector2 direction = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
    float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
    Quaternion rotation = Quaternion.AngleAxis(angle - 90, Vector3.forward);
    transform.rotation = rotation;

    // 遍历所有触摸事件
    foreach(Touch touch in Input.touches)
    {
        if(touch.phase == TouchPhase.Began || touch.phase == TouchPhase.Stationary || touch.phase == TouchPhase.Moved)
        {
            Vector2 touchPosInUISpace;
            RectTransformUtility.ScreenPointToLocalPointInRectangle(joystickRect, touch.position, Camera.main, out touchPosInUISpace);
            
            if (!joystickRect.rect.Contains(touchPosInUISpace))
            {
                if (Time.time >= shotTime)
                {
                    Instantiate(projectile, shotPoint.position, transform.rotation);
                    shotTime = Time.time + timeBetweenShots;
                    // 避免多触摸重复触发,可根据需求调整是否break
                    break;
                }
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 21:50:38