unity 简单实现三阶魔方游戏

unity 简单实现三阶魔方游戏

魔方体验地址

工程文件免费下载

实现思路

一、魔方的旋转

三阶魔方由26个方块与 9个旋转轴组成。旋转轴旋转时带动在其控制范围的方块旋转。

旋转轴如何带动方块旋转?

把旋转轴控制范围内的方块设置成旋转轴的子物体,旋转轴旋转时便会带动子物体旋转。

如何得到旋转轴控制范围内的方块?

这里运用的unity提供的一个API方法Collider.bounds.Intersects,另一个包围盒是否与该包围盒交叠。即为每一个方块和旋转轴添加Collider,从而判断方块是否为旋转轴控制范围内的方块。

在这里插入图片描述

方块添加Collider

在这里插入图片描述

旋转轴添加Collider

二、魔方的控制

使用鼠标拖拽魔方旋转如下图

旋转轴的确定

确定的规律

  1. 每一个旋转轴的旋转方向是固定的;
  2. 每一个方块都有三个旋转轴控制。
旋转轴的确定

用鼠标点击魔方的两个点,即鼠标点击点和鼠标抬起点,来做判断依据。两点与中心点组成的面的法线即是旋转轴的旋转法线。

如图,A点是点击的第一个点,B点是点击的第二个点,O点是原点。

OAB组成的面就是的法线就是 旋转轴的旋转法线。

旋转方向的确定

如上图叉乘 O A ⃗ \vec{OA} OA × \times × O B ⃗ \vec{OB} OB 得到法线如果是正值正向旋转,为负值逆向旋转。

   public void ToRotate(List<AxisControl> axisControls)
    {
        Vector3 vector =Vector3.Normalize( Vector3.Cross(FirstPoint - transform.position, SecondPoint - transform.position));//向量叉乘
        int length = axisControls.Count;
        for (int i = 0; i < length; i++)
        {
            if (axisControls[i].rotationFront.ToString() == ReturnRotate(vector))
            {
                axisControls[i].ToRotate(front);
                break;
            }
        }
    }
    int front = 1;
    string ReturnRotate( Vector3 vector)
    {

        float nx = Mathf.Abs(vector.x);
        float ny = Mathf.Abs(vector.y);
        float nz = Mathf.Abs(vector.z);

        if (nx> ny&& nx> nz)
        {
            vector.x = vector.x > 0 ? front = 1 : front = -1;
            return "X";
        }
        else if (ny > nx && ny > nz)
        {
            vector.y = vector.y > 0 ? front = 1 : front = -1;
            return "Y";
        }
      else  
        {
            vector.z = vector.z > 0 ? front = 1 : front = -1;
            return "Z";
        }
    }

完整代码

方块的控制脚本(绑到每个方块上)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class CubeControl : MonoBehaviour
{
  public   List<AxisControl> AxisControls;
    private Collider _collider;

    public Collider Collider
    {
        get
        {
            if (_collider == null)
            {
                _collider = gameObject.GetComponent<Collider>();
            }
            return _collider;
        }
    }
    RubikManager rubikManager;

    public RubikManager RubikManager
    {
        get
        {
            if (rubikManager == null)
            {
                rubikManager = RubikManager.Instance;
            }
            return rubikManager;
        }
    }

  
    // Start is called before the first frame update
    void Start()
    {
        AddEventTrigger();
    }

    public void AddEventTrigger()
    {
        gameObject.AddComponent<EventTrigger>();
        addEventTrigger(transform, EventTriggerType.PointerUp, UpCude);
        addEventTrigger(transform, EventTriggerType.PointerDown, DownCude);
    }
    /// <summary>
    /// event事件绑定的方法
    /// </summary>
    /// <param name="insObject">事件物体</param>
    /// <param name="eventType">事件类型</param>
    /// <param name="myFunction">事件需要的回调方法</param>
    public void addEventTrigger(Transform insObject, EventTriggerType eventType, UnityEngine.Events.UnityAction<BaseEventData> myFunction)//泛型委托
    {
        EventTrigger trigger = insObject.GetComponent<EventTrigger>();
        EventTrigger.Entry entry = new EventTrigger.Entry();
        entry.eventID = eventType;
        entry.callback.AddListener(myFunction);
        trigger.triggers.Add(entry);
    }
   
    void DownCude(BaseEventData data)
    {
        if (Input.GetMouseButtonDown(0))
        {
            RubikManager.FirstPoint = RayPiont();
        }
    }
    Vector3 front = Vector3.zero;
    void UpCude(BaseEventData data)
    {
        if (!RubikManager.isOperation&& Input.GetMouseButtonUp(0))
        {
      
        Vector3 rayPiont = RayPiont();
        RubikManager.SecondPoint = rayPiont;
        List<AxisControl> axisControls = new List<AxisControl>();
        int length = AxisControls.Count;
         
                RubikManager.ToRotate(AxisControls);
                RubikManager.isOperation = true;
        }
    }

    Vector3 RayPiont()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit raycastHit;
        if (Physics.Raycast(ray,out raycastHit))
        {
            return raycastHit.point;
        }
        else
        {
            return Vector3.zero;
        }
    }
    public void SetParent(Transform transformSelf)
    {
        transform.parent = transformSelf;
    }
}

旋转轴的控制方法(绑在旋转轴上)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using FrameWorkSong;


public class AxisControl : MonoBehaviourSimplify
{
    public enum RotationFront
    {
        X, Y,Z
    }
    public List<CubeControl> Cubes;
    public RotationFront rotationFront;
     Collider _collider;
    RubikManager rubikManager;
    Vector3 vector = Vector3.zero;
    public RubikManager RubikManager
    {
        get
        {
            if (rubikManager==null)
            {
                rubikManager = RubikManager.Instance;
            }
            return rubikManager;
        }
    }

    public Collider Collider
    {
        get
        {
            if (_collider==null)
            {
                _collider = gameObject.GetComponent<Collider>();
            }
            return _collider;
        } }


    // Start is called before the first frame update
    void Start()
    {
        Delay(1, () => { FindSelfCube(); });
        vector = transform.eulerAngles;
    }

    public void FindSelfCube()
    {
        Cubes = new List<CubeControl>() ;
        List<CubeControl> allCubeControls = RubikManager.CubeControls;
        int length = allCubeControls.Count;
        for (int i = 0; i < length; i++)
        {
            if (Collider.bounds.Intersects(allCubeControls[i].Collider.bounds))//得到旋转轴控制范围内的方块
            {
                Cubes.Add(allCubeControls[i]);
                allCubeControls[i].AxisControls.Add(this);
            }
        }
    }

    public override void OnBeforeDestroy()
    {
        
    }
    public void ToRotate(int front)
    {
        int length = Cubes.Count;
        for (int i = 0; i < length; i++)
        {
            Cubes[i].SetParent(transform);
        }
       
        if (rotationFront== RotationFront.X)
        {
             vector += (front* new Vector3(90, 0, 0));
            if (vector.x>=360)
            {
                vector = Vector3.zero;
            }
         
           
        }
        if (rotationFront == RotationFront.Y)
        {
            vector += (front * new Vector3(0, 90, 0));
            if (vector.y>= 360)
            {
                vector = Vector3.zero;
            }

        }
        if (rotationFront == RotationFront.Z)
        {
            vector += (front * new Vector3(0, 0, 90));
            if (vector.z >= 360)
            {
                vector = Vector3.zero;
            }
        }
        DoRotate(transform, vector, 0.5f,()=> { RubikManager.Reset(); });
    }
}

魔方的总控

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using FrameWorkSong;
using System;

public class RubikManager : Singleton<RubikManager>
{
    public List<AxisControl> AxisControls;
    public List<CubeControl> CubeControls;


    public Vector3 FirstPoint;
    public Vector3 SecondPoint;

    public override void OnBeforeDestroy() { }
    public bool isOperation = false;

    // Start is called before the first frame update
    void Start()
    {
        GameObject[] AllCubes = GameObject.FindGameObjectsWithTag("Cube");

        int cubeLength = AllCubes.Length;
        for (int i = 0; i < cubeLength; i++)
        {
            CubeControls.Add(AllCubes[i].GetComponent<CubeControl>());
        }

        GameObject[] AllAxis = GameObject.FindGameObjectsWithTag("Axis");
        int axisLength = AllAxis.Length;
        for (int j = 0; j < axisLength; j++)
        {
            AxisControls.Add(AllAxis[j].GetComponent<AxisControl>());
        }
    }
   public void ToRotate(List<AxisControl> axisControls)
    {
        Vector3 vector =Vector3.Normalize( Vector3.Cross(FirstPoint - transform.position, SecondPoint - transform.position));
        int length = axisControls.Count;
        for (int i = 0; i < length; i++)
        {
            if (axisControls[i].rotationFront.ToString() == ReturnRotate(vector))
            {
                axisControls[i].ToRotate(front);
                break;
            }
        }
    }
    int front = 1;
    string ReturnRotate( Vector3 vector)
    {

        float nx = Mathf.Abs(vector.x);
        float ny = Mathf.Abs(vector.y);
        float nz = Mathf.Abs(vector.z);

        if (nx> ny&& nx> nz)
        {
            vector.x = vector.x > 0 ? front = 1 : front = -1;
            return "X";
        }
        else if (ny > nx && ny > nz)
        {
            vector.y = vector.y > 0 ? front = 1 : front = -1;
            return "Y";
        }
      else  
        {
            vector.z = vector.z > 0 ? front = 1 : front = -1;
            return "Z";
        }
    }
    //重置
    public void Reset()
    {
        int Count = CubeControls.Count;
        for (int j = 0; j < Count; j++)
        {
            CubeControls[j].AxisControls.Clear();
        }
        int length = AxisControls.Count;
        for (int i = 0; i < length; i++)
        {
            AxisControls[i].FindSelfCube();
        }
        isOperation = false;
    }
}

单例

using UnityEngine;
namespace FrameWorkSong
{

    public abstract class Singleton <T>: MonoBehaviourSimplify where T:Singleton<T>
    {
        protected static T mInstance=null;
        public static T Instance
        {
            get
            {
                if (mInstance == null)
                {
                    mInstance = FindObjectOfType<T>();
                    if (mInstance==null)
                    {
                        var instanceName = typeof(T).Name;
                        var instanceObj = GameObject.Find(instanceName);
                        if (!instanceObj)
                        {
                            instanceObj= new GameObject(instanceName);
                        }
                        mInstance = instanceObj.AddComponent<T>();
                        DontDestroyOnLoad(instanceObj);
                        Debug.LogFormat("创建新的{0}单例实体", instanceName);
                    }
                    else
                    {
                        Debug.LogFormat("已经有单例实体");
                    }
                }
                return mInstance;
            }
        }
        protected virtual void onDestroy()
        {
            mInstance = null;
        }
    }
}

延时功能

using System;
using System.Collections;
using UnityEngine;
namespace FrameWorkSong
{
    public partial class MonoBehaviourSimplify : MonoBehaviour
    {
        /// <summary>
        /// 清理缓存
        /// </summary>
        protected void ReleaseMemory()
        {
            Resources.UnloadUnusedAssets();
            GC.Collect();
        }
        protected string GetNowTime()
        {
            return DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");
        }
        /// <summary>
        /// 延时功能
        /// </summary>
        /// <param name="seconds">延迟时间</param>
        /// <param name="onFinished">调用方法</param>
        public void Delay(float seconds, Action onFinished)
        {
            StartCoroutine(DelayCoroutione(seconds, onFinished));
        }
        private IEnumerator DelayCoroutione(float seconds, Action onFinished)
        {
            yield return new WaitForSeconds(seconds);
            onFinished();
        }
    }
}

旋转方法

using System;
using System.Collections;
using UnityEngine;
namespace FrameWorkSong
{
    public partial class MonoBehaviourSimplify
    {
/// <summary>
        /// 旋转
        /// </summary>
        /// <param name="transformSelf"></param>
        /// <param name="target"></param>
        /// <param name="time"></param>
        /// <param name="id"></param>
        /// <param name="action"></param>
        public void DoRotate(Transform transformSelf, Quaternion target, float time, Action action = null, string id = null)
        {
            IEnumerator coroutine;
            coroutine = DoRotateIE(transformSelf, target, time, action);
            StartCoroutine(coroutine);
            if (id != null)
            {
                keyValueCoroutine.AddData(id, coroutine);
            }
        }
        public void DoRotate(Transform transformSelf, Vector3 target, float time, Action action = null, string id = null)
        {
            
            DoRotate(transformSelf, Quaternion.Euler(target), time, action, id);
        }
        IEnumerator DoRotateIE(Transform transformSelf, Quaternion targetQua, float time, Action action = null)
        {
            Quaternion formQua = transformSelf.rotation;
            float t = 0;
            while (t < 1)
            {
                t += Time.deltaTime / time;
                t = t > 1 ? 1 : t;
                transform.rotation = Quaternion.Lerp(formQua, targetQua, t);
                yield return null;
            }
            transform.rotation = targetQua;
            if (action != null)
            {
                action();
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/dxs1990/article/details/128551273