找回密码
 立即注册
查看: 370|回复: 0

Unity中的虚拟摇杆,用于触屏游戏的角色移动控制

[复制链接]
发表于 2022-2-22 12:33 | 显示全部楼层 |阅读模式
示意


虚拟摇杆.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;        [SerializeField, Header("背景图的RectTrans,用于限制实际的操作区域")]        private RectTransform _bgRectTrans;        [SerializeField]        private VirtualJoystickAppearance _virtualJoystickAppearance;        private Vector2 HandlePos { get { return _virtualJoystickAppearance.HandlePos; } }        [SerializeField, Header("限制背景图中心点的位置")]        private int _limitRangeOffset = 0;        [SerializeField, Header("两点距离多远时,认为停止设置方向")]        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    {        [SerializeField]        private VirtualJoystick _virtualJoystick;        [SerializeField]        private RectTransform _bgRectTrans;        public Vector2 BgPos{ get { return _bgRectTrans.position; } }        [SerializeField]        private RectTransform _handleRectTrans;        public Vector2 HandlePos        {             get { return _handleRectTrans.position; }        }        [SerializeField]        private CanvasGroup _canvasGroup;        [SerializeField, Range(0, 1)]        private float _hideAlpha = 0.2f;        private Vector2 _bgDefaultPos;        [SerializeField]        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{    [SerializeField]    private Player _player;    [SerializeField]    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;    [SerializeField]    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;    }}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-16 23:57 , Processed in 0.090621 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表