unity常用方法

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

2022-03-11  本文已影响0人  全新的饭

需求

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


旋转移动的方块.gif

思路

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

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

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

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

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

  1. 旋转时的参考点:假设当前位置是(x, y, z)
  1. 旋转轴
  2. 旋转角度

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

旋转:直接四舍五入取整

更新当前的长宽高的值:长宽高的值用于计算旋转时参考点的位置、旋转后的新位置。
每次旋转后 l w 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
}
上一篇下一篇

猜你喜欢

热点阅读