魔方体验地址
工程文件免费下载
三阶魔方由26个方块与 9个旋转轴组成。旋转轴旋转时带动在其控制范围的方块旋转。
把旋转轴控制范围内的方块设置成旋转轴的子物体,旋转轴旋转时便会带动子物体旋转。
这里运用的unity提供的一个API方法Collider.bounds.Intersects,另一个包围盒是否与该包围盒交叠。即为每一个方块和旋转轴添加Collider,从而判断方块是否为旋转轴控制范围内的方块。
使用鼠标拖拽魔方旋转如下图
确定的规律
用鼠标点击魔方的两个点,即鼠标点击点和鼠标抬起点,来做判断依据。两点与中心点组成的面的法线即是旋转轴的旋转法线。
如图,A点是点击的第一个点,B点是点击的第二个点,O点是原点。
OAB组成的面就是的法线就是 旋转轴的旋转法线。
如上图叉乘 O A ⃗ \vec{OA} OA × \times × O B ⃗ \vec{OB} OB得到法线如果是正值正向旋转,为负值逆向旋转。
public void ToRotate(List 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 AxisControls;
private Collider _collider;
public Collider Collider
{
get
{
if (_collider == null)
{
_collider = gameObject.GetComponent();
}
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();
addEventTrigger(transform, EventTriggerType.PointerUp, UpCude);
addEventTrigger(transform, EventTriggerType.PointerDown, DownCude);
}
///
/// event事件绑定的方法
///
/// 事件物体
/// 事件类型
/// 事件需要的回调方法
public void addEventTrigger(Transform insObject, EventTriggerType eventType, UnityEngine.Events.UnityAction myFunction)//泛型委托
{
EventTrigger trigger = insObject.GetComponent();
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 axisControls = new List();
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 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();
}
return _collider;
} }
// Start is called before the first frame update
void Start()
{
Delay(1, () => { FindSelfCube(); });
vector = transform.eulerAngles;
}
public void FindSelfCube()
{
Cubes = new List() ;
List 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
{
public List AxisControls;
public List 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());
}
GameObject[] AllAxis = GameObject.FindGameObjectsWithTag("Axis");
int axisLength = AllAxis.Length;
for (int j = 0; j < axisLength; j++)
{
AxisControls.Add(AllAxis[j].GetComponent());
}
}
public void ToRotate(List 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 : MonoBehaviourSimplify where T:Singleton
{
protected static T mInstance=null;
public static T Instance
{
get
{
if (mInstance == null)
{
mInstance = FindObjectOfType();
if (mInstance==null)
{
var instanceName = typeof(T).Name;
var instanceObj = GameObject.Find(instanceName);
if (!instanceObj)
{
instanceObj= new GameObject(instanceName);
}
mInstance = instanceObj.AddComponent();
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
{
///
/// 清理缓存
///
protected void ReleaseMemory()
{
Resources.UnloadUnusedAssets();
GC.Collect();
}
protected string GetNowTime()
{
return DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");
}
///
/// 延时功能
///
/// 延迟时间
/// 调用方法
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
{
///
/// 旋转
///
///
///
///
///
///
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();
}
}
}
}