Unity中的虚拟摇杆,用于触屏游戏的角色移动控制
示意虚拟摇杆.gif
思路
虚拟摇杆由3部分组成
image.png
[*]触屏区域
在该区域内按住屏幕,可控制摇杆。
[*]背景大圆
在触屏区域内按住屏幕时,将该圆(的中心)设置到合适的位置。
合适的位置:因为要限制该圆显示在触屏区域内,因此实际其中心的位置为图中红色区域内。
[*]中央摇杆
随手指滑动而移动,限制其与背景大圆中心的最大距离。
具体资源
虚拟摇杆GameObject的层级
image.png
触屏控制
image.png
摇杆的显示
image.png
相关代码
VirtualJoystick.cs
监听触屏、滑屏相关事件,发出自己的事件
using System;using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.EventSystems;namespace VirtualJoystick{ public class VirtualJoystick : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IDragHandler { // bgPos, handlePos public event Action<Vector2, Vector2> PointerDownEvent; public event Action<Vector2> DragEvent; public event Action PointerUpEvent; public event Action<Vector2> SetDirEvent; public event Action StopSetDirEvent; private RectTransform _bgRectTrans; private VirtualJoystickAppearance _virtualJoystickAppearance; private Vector2 HandlePos { get { return _virtualJoystickAppearance.HandlePos; } } private int _limitRangeOffset = 0; private float _stopSetDirDistance = 10; private Vector2 _minPos; private Vector2 _maxPos; private bool _hasInited = false; private Vector2 _bgPos; public void OnPointerDown(PointerEventData eventData) { if (!_hasInited) { Init(); } Vector2 handlePos = eventData.position; _bgPos = LimitBgPos(eventData.position); PointerDownEvent?.Invoke(_bgPos, handlePos); SetDir(HandlePos, _bgPos); void Init() { int actualLimitRangeOffset = (int)_bgRectTrans.rect.width / 2 + _limitRangeOffset; float width = GetComponent<RectTransform>().rect.width; float height = GetComponent<RectTransform>().rect.height; _minPos = Vector2.one * actualLimitRangeOffset; _maxPos = new Vector2(width - actualLimitRangeOffset, height - actualLimitRangeOffset); _hasInited = true; } Vector2 LimitBgPos(Vector2 pos) { pos = new Vector2(Mathf.Clamp(pos.x, _minPos.x, _maxPos.x), Mathf.Clamp(pos.y, _minPos.y, _maxPos.y)); return pos; } } public void OnDrag(PointerEventData eventData) { DragEvent?.Invoke(eventData.position); SetDir(eventData.position, _bgPos); } public void OnPointerUp(PointerEventData eventData) { PointerUpEvent?.Invoke(); StopSetDirEvent?.Invoke(); } private void SetDir(Vector2 handlePos, Vector2 bgPos) { if (Vector2.Distance(handlePos, bgPos) > _stopSetDirDistance) { Vector2 dir = (handlePos - bgPos).normalized; SetDirEvent?.Invoke(dir); Debug.Log("SetDir " + dir); } else { StopSetDirEvent?.Invoke(); } } }}
VirtualJoystickAppearance.cs
虚拟摇杆的显示(包括设置其位置等)。
using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.UI;namespace VirtualJoystick{ public class VirtualJoystickAppearance : MonoBehaviour { private VirtualJoystick _virtualJoystick; private RectTransform _bgRectTrans; public Vector2 BgPos{ get { return _bgRectTrans.position; } } private RectTransform _handleRectTrans; public Vector2 HandlePos { get { return _handleRectTrans.position; } } private CanvasGroup _canvasGroup; private float _hideAlpha = 0.2f; private Vector2 _bgDefaultPos; private float _handleAndBgMaxDistance = 120; void Start() { Init(); } private void OnDestroy() { Destroy(); } void Init() { var joystickRect = _virtualJoystick.GetComponent<RectTransform>().rect; _bgDefaultPos = new Vector2(joystickRect.width / 2, joystickRect.height/2); Hide(); ResetPos(); _virtualJoystick.PointerDownEvent += OnPointerDown; _virtualJoystick.SetDirEvent += OnSetDir; _virtualJoystick.StopSetDirEvent += OnStopSetDir; _virtualJoystick.DragEvent += OnDrag; _virtualJoystick.PointerUpEvent += OnPointerUp; } void Destroy() { if (_virtualJoystick != null) { _virtualJoystick.PointerDownEvent -= OnPointerDown; _virtualJoystick.SetDirEvent -= OnSetDir; _virtualJoystick.StopSetDirEvent -= OnStopSetDir; _virtualJoystick.DragEvent -= OnDrag; _virtualJoystick.PointerUpEvent -= OnPointerUp; } } private void Hide() { _canvasGroup.alpha = _hideAlpha; } private void Show() { _canvasGroup.alpha = 1; } private void ResetPos() { _bgRectTrans.transform.position = _bgDefaultPos; _handleRectTrans.localPosition = Vector3.zero; } private void LimitHandlePos(Vector2 pos) { if (Vector2.Distance(pos, (Vector2)_bgRectTrans.transform.position) > _handleAndBgMaxDistance) { pos = (Vector2)_bgRectTrans.transform.position + (pos - (Vector2)_bgRectTrans.transform.position).normalized * _handleAndBgMaxDistance; } _handleRectTrans.position = pos; } #region 关注joystick的一些事件 private void OnPointerDown(Vector2 bgPos, Vector2 handlePos) { _bgRectTrans.position = bgPos; LimitHandlePos(handlePos); Show(); } private void OnSetDir(Vector2 dir) { if (IsInHide()) { Show(); } bool IsInHide() { return _canvasGroup.alpha == _hideAlpha; } } private void OnStopSetDir() { // Hide(); // ResetPos(); } private void OnDrag(Vector2 handlePos) { LimitHandlePos(handlePos); } private void OnPointerUp() { Hide(); ResetPos(); } #endregion }}用于测试的代码
InputMgr.cs
监听虚拟摇杆的事件(SetDirEvent、StopSetDirEvent),控制Player移动。
using System.Collections;using System.Collections.Generic;using UnityEngine;using VirtualJoystick;public class InputMgr : MonoBehaviour{ private Player _player; private VirtualJoystick.VirtualJoystick _virtualJoystick; void Start() { Init(); } private void OnDestroy() { Destroy(); } private void Init() { if (_virtualJoystick != null) { _virtualJoystick.SetDirEvent += OnSetMoveDir; _virtualJoystick.StopSetDirEvent += OnStopSetDir; } } private void Destroy() { if (_virtualJoystick != null) { _virtualJoystick.SetDirEvent -= OnSetMoveDir; _virtualJoystick.StopSetDirEvent -= OnStopSetDir; } } private void OnSetMoveDir(Vector2 dir) { _player?.Move(dir); } private void OnStopSetDir() { _player?.StopMove(); }}
Player.cs
外界(InputMgr)调用其Move方法控制其移动。
using System.Collections;using System.Collections.Generic;using UnityEngine;public class Player : MonoBehaviour{ private Vector2 _moveDir; private float _moveSpeed = 3f; void Start() { Init(); } void Update() { if (_moveDir != Vector2.zero) { transform.forward = new Vector3(_moveDir.x, 0, _moveDir.y); transform.Translate(transform.forward * _moveSpeed * Time.deltaTime, Space.World); } } private void Init() { _moveDir = Vector2.zero; } public void Move(Vector2 dir) { _moveDir = dir.normalized; } public void StopMove() { _moveDir = Vector2.zero; }}
页:
[1]