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; } } } }
方法二:手动标记摇杆触摸状态
如果是自定义摇杆,在摇杆脚本中添加触摸状态标记,射击脚本根据标记判断是否触发:
- 修改移动摇杆脚本:
// 公开触摸状态,供射击脚本读取 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; }
- 修改射击脚本:
// 引用摇杆控制脚本 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
相关产品推荐
相关产品推荐

