美文网首页unity常用方法
Unity中,做一个通过转动来移动的方块

Unity中,做一个通过转动来移动的方块

作者: 全新的饭 | 来源:发表于2022-03-11 17:02 被阅读0次

    需求

    一个长宽高为任意值的方块,可控制其向前、后、左、右通过翻转的方式移动。
    示意图如下:


    旋转移动的方块.gif

    思路

    image.png
    接受移动方向的输入

    提供一个Rotate方法,要求外界传入要移动的方向(前、后、左、右)。

    因为单次移动是一个过程而非瞬时,所以使用一个队列_rotateDirs将外界输入存储起来。
    该队列中的元素是移动方向,每执行完一次单次移动后,尝试从前述队列中获取新的移动方向:

    1. 若获取不到,说明已执行完所有的移动指令,则停止
    2. 获取到新的移动方向后,向该方向执行一次单次移动。
    单次移动

    是一个过程:外界传入该过程所需时间rotateTime。
    根据移动方向,准备所需数据

    1. 旋转时的参考点:假设当前位置是(x, y, z)
    • 向前:(x, y-h/2, z+w/2)
      向后:(x, y-h/2, z-w/2)
      向左:(x-l/2, y-h/2, z)
      向右:(x+l/2, y-h/2, z)
    1. 旋转轴
    2. 旋转角度

    每次移动完成后,需确保transform的位置和旋转值变为准确的目标值
    位置:假设变化前是(x, y, z),则变化后应是

    • 向前:(x, w/2, z + (w/2 + h/2))
      向后:(x, w/2, z - (w/2 + h/2))
      向左:(x -(l/2 + h/2), l/2, z)
      向右:(x +(l/2 + h/2), l/2, z)

    旋转:直接四舍五入取整

    更新当前的长宽高的值:长宽高的值用于计算旋转时参考点的位置、旋转后的新位置。
    每次旋转后 l w h 的变化

    • 前:w h 互换
      后:w h 互换
      左:l h 互换
      右:l h 互换

    代码

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class RotatableCuboid : MonoBehaviour
    {
        [SerializeField]
        private float _l = 3;
        [SerializeField]
        private float _w = 2;
        [SerializeField]
        private float _h = 5;
    
        private IEnumerator _rotateCoroutine;
        private Queue<RotateDir> _rotateDirs;
        [SerializeField]
        private float _rotateTime = 0.5f;
    
        void Start()
        {
            Init();
        }
    
        void Update()
        {
            if (Input.GetKeyDown(KeyCode.W))
            {
                Rotate(RotateDir.Forward);
            }
            else if (Input.GetKeyDown(KeyCode.S))
            {
                Rotate(RotateDir.Backward);
            }
            else if (Input.GetKeyDown(KeyCode.A))
            {
                Rotate(RotateDir.Left);
            }
            else if (Input.GetKeyDown(KeyCode.D))
            {
                Rotate(RotateDir.Right);
            }
        }
    
        void OnDestroy() 
        {
            Destroy();
        }
    
        private void Init()
        {
            _rotateCoroutine = null;
            _rotateDirs = new Queue<RotateDir>();
        }
        private void Destroy()
        {
            if (_rotateCoroutine != null)
            {
                StopCoroutine(_rotateCoroutine);
                _rotateCoroutine = null;
            }
    
            _rotateDirs.Clear();
            _rotateDirs = null;
        }
    
        private void Rotate(RotateDir dir)
        {
            _rotateDirs.Enqueue(dir);
            if (_rotateCoroutine == null)
            {
                _rotateCoroutine = RotateCoroutine(_rotateTime);
                StartCoroutine(_rotateCoroutine);
            }
        }
    
        private Vector3 GetNextPos(RotateDir dir)
        {
            Vector3 pos = transform.position;
            switch (dir)
            {
                case RotateDir.Forward:
                    pos = new Vector3(pos.x, _w / 2, pos.z + (_w / 2 + _h / 2));
                    break;
                case RotateDir.Backward:
                    pos = new Vector3(pos.x, _w / 2, pos.z - (_w / 2 + _h / 2));
                    break;
                case RotateDir.Left:
                    pos = new Vector3(pos.x - (_l / 2 + _h / 2), _l / 2, pos.z);
                    break;
                case RotateDir.Right:
                    pos = new Vector3(pos.x + (_l / 2 + _h / 2), _l / 2, pos.z);
                    break;
                default:
                    break;
            }
            return pos;
        }
    
        private void ExchangeValue<T>(ref T a, ref T b)
        {
            T temp;
            temp = a;
            a = b;
            b = temp;
        }
    
        private IEnumerator RotateCoroutine(float rotateTime)
        {
            while (_rotateDirs.Count > 0)
            {
               var dir = _rotateDirs.Dequeue();
    
                Vector3 newPos = GetNextPos(dir);
                Vector3 point = Vector3.zero;
                Vector3 axis = Vector3.right;
                float angle = 0;
                switch (dir)
                {
                    case RotateDir.Forward:
                        point = new Vector3(transform.position.x, transform.position.y - _h / 2, transform.position.z + _w / 2);
                        axis = Vector3.right;
                        angle = 90;
                        break;
                    case RotateDir.Backward:
                        point = new Vector3(transform.position.x, transform.position.y - _h / 2, transform.position.z - _w / 2);
                        axis = Vector3.right;
                        angle = -90;
                        break;
                    case RotateDir.Left:
                        point = new Vector3(transform.position.x - _l / 2, transform.position.y - _h / 2, transform.position.z);
                        axis = Vector3.forward;
                        angle = 90;
                        break;
                    case RotateDir.Right:
                        point = new Vector3(transform.position.x + _l / 2, transform.position.y - _h / 2, transform.position.z);
                        axis = Vector3.forward;
                        angle = -90;
                        break;
                    default:
                        break;
                }
    
                int targetTimes = (int)(rotateTime / Time.deltaTime);
                int times = 0;
                float perAngle = angle / targetTimes;
                while (times < targetTimes)
                {
                    transform.RotateAround(point, axis, perAngle);
                    times++;
                    yield return null;
                }
    
                transform.position = newPos;
                transform.eulerAngles = new Vector3(Round(transform.eulerAngles.x), Round(transform.eulerAngles.y), Round(transform.eulerAngles.z));
    
                switch (dir)
                {
                    case RotateDir.Forward:
                        ExchangeValue(ref _w, ref _h);
                        break;
                    case RotateDir.Backward:
                        ExchangeValue(ref _w, ref _h);
                        break;
                    case RotateDir.Left:
                        ExchangeValue(ref _l, ref _h);
                        break;
                    case RotateDir.Right:
                        ExchangeValue(ref _l, ref _h);
                        break;
                    default:
                        break;
                }
            }
    
            if (_rotateCoroutine != null)
            {
                StopCoroutine(_rotateCoroutine);
                _rotateCoroutine = null;
            }
        }
    
        private int Round(float value)
        {
            int v = ((int)(value * 10)) % 10;
    
            int ret = v >= 5 ? (int)value + 1 : (int)value;
            return ret;
        }
    }
    
    public enum RotateDir
    { 
        None,
        Forward,
        Backward,
        Left,
        Right
    }
    

    相关文章

      网友评论

        本文标题:Unity中,做一个通过转动来移动的方块

        本文链接:https://www.haomeiwen.com/subject/jofhdrtx.html