jquave 发表于 2022-2-22 12:33

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]
查看完整版本: Unity中的虚拟摇杆,用于触屏游戏的角色移动控制