> 文章列表 > 【Unity3D】魔方

【Unity3D】魔方

【Unity3D】魔方

1 需求实现

         绘制魔方 中基于OpenGL ES 实现了魔方的绘制,实现较复杂,本文基于 Unity3D 实现了 2 ~ 10 阶魔方的整体旋转和局部旋转,详细需求如下:

  • 用户通过选择魔方阶数,渲染指定阶数的魔方,并且可以自动打乱魔方
  • 用户通过 Scroll 或 Ctrl + Scroll,控制魔方放大和缩小
  • 用户通过 Drag 空白处(或 Ctrl + Drag,或 Alt + Drag,或右键 Drag,或 Ctrl + 右键 Drag,或方向键),控制魔方整体旋转
  • 用户通过 Drag 魔方相邻的两个方块,实现旋转该层(局部旋转);
  • 从魔方界面点击返回按钮,可以返回到选择阶数界面;
  • 用户每进行一次局部旋转,记步加 1;
  • 显示计时器

        选择阶数界面如下:

        魔方界面如下:

        本文完整代码资源见→基于 Unity3D 的 2 ~ 10 阶魔方实现。

2 原理介绍

2.1 渲染原理

        在 Hierarchy 窗口新建一个空对象,重命名为 Cube,在 Cube 下创建 6 个 Quad 对象,分别重命名为 Left (x = -0.5)、Right (x = 0.5)、Down (y = -0.5)、Up (y = 0.5)、Back (z = -0.5)、Forward (z = 0.5),调整位置和旋转角度,使得它们围成一个小立方体,将 Cube 拖拽到 Assets 窗口作为预设体。

        在创建一个 order 阶魔方时,新建一个空对象,重命名为 Rubik,复制 order^3 个 Cube 作为 Rubik 的子对象,调整所有 Cube 的位置使其拼成魔方结构,根据立方体和方块位置,为每个方块设置纹理图片,如下:

        说明:对于任意小方块 Square,Square.forward 始终指向小立方体中心,该结论在旋转层检测中会用到。 

2.2 整体旋转原理

        通过调整相机前进和后退,控制魔方放大和缩小;通过调整相机的位置和姿态,使得相机绕魔方旋转,实现魔方整体旋转。详情见缩放、平移、旋转场景。

2.3 旋转层检测原理

        1)旋转层编码

        旋转层旋转轴层序两个变量决定,本文定义了 RubikLayer 类描述旋转层,里面包含 axis (旋转轴)和 seq (层序)两个变量。axis 取值有 0、1、2,分别表示 x 轴、y 轴、z 轴,seq 取值有 0 ~ (order - 1),order 表示魔方阶数。

        2)旋转轴检测

        假设屏幕射线检测到的两个相邻方块分别为 square1、square2。

  • 如果 square1 与 square2 在同一个小立方体里,square1.forward 与 square2.forward 叉乘的向量就是旋转轴方向向量;
  • 如果 square1 与 square2 在相邻小立方体里,square1.forward 与 (square1.position - square2.position) 叉乘的向量就是旋转轴方向向量;

        假设叉乘后的向量的单位向量为 crossDir,我们将 crossDir 与每个坐标轴的单位方向向量(假设为 axisDir)进行点乘,如果 Dot(crossDir, axisDir) > 0.99(夹角小于 8°),就选取该轴作为旋转轴,如果每个轴的点乘结果都小于 0.99,说明屏幕射线拾取的两个方块不在同一旋转层,舍弃局部旋转。

        3)层序检测

         坐标分量与层序的映射关系如下,其中 order 为魔方阶数,seq 为层序,pos 为坐标分量,cubeSide 为小立方体的边长。由于频繁使用到 pos 与 seq 的映射,建议将 0 ~ order 层的层序 seq 对应的 pos 存储在数组中,方便快速查找。

         square1 与 square2 在旋转轴方向上的坐标分量一致,假设为 pos(如果旋转轴是 x 轴,pos 指 square1 或 square1 中心坐标的 x 值),由上述公式就可以推导出层序 seq。

2.4 局部旋转原理

        1)待旋转的小立方体检测

        对于每个小立方体,使用数组 loc[] 存储了小立方体在 x、y、z 轴方向上的层序,每次旋转结束后,根据小立方体的中心坐标可以重写计算出 loc 数组。

        假设检测到的旋转轴为 axis,旋转层为 seq,所有 loc[axis] 等于 seq 的小立方体都是需要旋转的小立方体。

        2)局部旋转

        在 Rubik 对象下创建一个空对象,重命名为 RotateLayer,将 RotateLayer 移至坐标原点,旋转角度全部置 0。

        将处于旋转层的小立方体的 parent 都设置为 RotateLayer,对 RotateLayer 进行旋转,旋转结束后,将这些小立方体的 parent 重置为 Rubik,RotateLayer 的旋转角度重置为 0,根据小立方体中心的 position 更新 loc 数组。

2.5 魔方打乱原理

        选择阶数后,渲染一个已经拼好的魔方,随机选择一个旋转轴和旋转层序,随机旋转 90° 的整数倍,实现打乱魔方。随机旋转次数建议不少于 order^3 次,约束下限和上限分别为:100、400。

        为了保持用户玩魔方的习惯(如:多数用户习惯蓝色中心朝上,绿色中心朝下),建议随机旋转时不要旋转中间层,如:3 阶魔方的第 2 层,4 阶魔方的第 2 层,5 阶魔方的第 3 层。

3 代码实现

3.1 代码结构

3.2 魔方结构

        Rubik.cs

using UnityEngine;/** 魔方*/
public class Rubik
{public Transform self; // 魔方对象private static RubikInfo rubikInfo; // 魔方信息private Cube[][][] cubes; // 立方体public Rubik(Transform self){this.self = self;Init();}public static void SetRubikInfo(RubikInfo rubikInfo){ // 设置魔方信息Rubik.rubikInfo = rubikInfo;}public static RubikInfo Info(){ // 获取魔方信息return rubikInfo;}public void Rotate(RubikLayer rubikLayer, float angle){ // 旋转魔方}private void Init(){ // 初始化if (rubikInfo == null){return;}int index = 0;cubes = new Cube[rubikInfo.order][][];for (int i = 0; i < cubes.Length; i++){cubes[i] = new Cube[rubikInfo.order][];for (int j = 0; j < cubes[i].Length; j++){cubes[i][j] = new Cube[rubikInfo.order];for (int k = 0; k < cubes[i][j].Length; k++){Transform cube = GameObject.Instantiate(RubikRes.CUBE_PREFAB).transform;cubes[i][j][k] = new Cube(cube, index++);cube.SetParent(self);}}}}public Cube[] GetRotateCubes(RubikLayer rubikLayer){ // 获取旋转的小立方体int index = 0;Cube[] rotateCubes = new Cube[rubikInfo.order2];for (int i = 0; i < cubes.Length; i++){for (int j = 0; j < cubes[i].Length; j++){for (int k = 0; k < cubes[i][j].Length; k++){if (cubes[i][j][k].loc[rubikLayer.axis] == rubikLayer.seq){rotateCubes[index++] = cubes[i][j][k];}}}}return rotateCubes;}public void Destroy(){ // 销毁魔方if (cubes == null){return;}for (int i = 0; i < cubes.Length; i++){for (int j = 0; j < cubes[i].Length; j++){for (int k = 0; k < cubes[i][j].Length; k++){cubes[i][j][k].Destroy();}}}cubes = null;self = null;}
}

        Cube.cs

using UnityEngine;/** 小立方体* squares的索引对应的面: 0-left, 1-right, 2-down, 3-up, 4-back, 5-forward*/
public class Cube
{// 小立方体的位置序号(以魔方的左下后为坐标原点, 向右、向上、向前分别为x轴、y轴、z轴, 小立方体的边长为单位刻度)public int[] loc;public Transform self; // 小立方体对象private const int COUNT = 6; // 面数private int id; // 唯一标识, 根据初始位置进行编码private Transform[] squares; // 每个面的方块private Texture[] textures; // 每个面的纹理图片public Cube(Transform self, int id){this.self = self;this.id = id;GetSquars();GetLoc();GetPos();GetTextures();}public void UpdateLoc(){ // 更新小立方体的位置序号loc = Rubik.Info().GetSeq(self.position);}public void Destroy(){ // 销毁对象if (squares == null){return;}for (int i = 0; i < COUNT; i++){GameObject.Destroy(squares[i].gameObject);}squares = null;GameObject.Destroy(self.gameObject);self = null;}private void GetSquars(){ // 获取方块squares = new Transform[COUNT];for (int i = 0; i < COUNT; i++){squares[i] = self.GetChild(i);}}private void GetLoc(){ // 获取小立方体的初始位置(以魔方的左下后为坐标原点, 向右、向上、向前分别为x轴、y轴、z轴, 小立方体的边长为单位刻度)loc = new int[3];loc[0] = id % Rubik.Info().order2 % Rubik.Info().order;loc[1] = id % Rubik.Info().order2 / Rubik.Info().order;loc[2] = id / Rubik.Info().order2;self.name = "Cube-" + loc[0] + "_" + loc[1] + "_" + loc[2];}private void GetPos(){ // 小立方体的中心坐标self.localScale = Vector3.one * Rubik.Info().cubeSide;self.position = Rubik.Info().GetPos(loc);}private void GetTextures(){ // 获取纹理textures = new Texture[COUNT];for (int i = 0; i < COUNT; i++){textures[i] = RubikRes.INSET_TEXTURE;}for(int i = 0; i < COUNT; i++){int axis = i / 2;if (loc[axis] == 0 && i == axis * 2 || loc[axis] == Rubik.Info().order - 1 && i == axis * 2 + 1){textures[i] = RubikRes.TEXTURES[i];}squares[i].GetComponent<Renderer>().material.mainTexture = textures[i];}}
}

3.3 魔方信息

        RubikRes.cs

using UnityEngine;/** 魔方资源*/
public class RubikRes
{// 小立方体预设体public static readonly GameObject CUBE_PREFAB = Resources.Load<GameObject>("Prefabs/Cube"); // 魔方外部小方块的纹理public static readonly Texture[] TEXTURES = new Texture[] {Resources.Load<Texture>("Textures/red"), // 0-leftResources.Load<Texture>("Textures/orange"), // 1-rightResources.Load<Texture>("Textures/green"), // 2-downResources.Load<Texture>("Textures/blue"), // 3-upResources.Load<Texture>("Textures/white"), // 4-backResources.Load<Texture>("Textures/yellow"), // 5-forward};// 魔方内部小方块的纹理public static readonly Texture INSET_TEXTURE = Resources.Load<Texture>("Textures/inside");
}

        RubikInfo.cs

using UnityEngine;/** 魔方信息* 依赖于魔方阶数和尺寸的相关固定信息, 避免重复计算, 节省性能*/
public class RubikInfo
{public int order = 3; // 魔方阶数public int order2 = 9; // 魔方阶数的平方public float size = 3f; // 魔方尺寸public float cubeSide = 1f; // 魔方中小立方体的边长public float halfCubeSide = 0.5f; // 魔方中小立方体的半边长public float quaCubeSide = 0.25f; // 魔方中小立方体的四分之一边长public float offset = 1f; // 位置与坐标的偏移public int shuffle = 27; // 打乱魔方需要旋转的次数public int centerSeq = 1; // 中心序号private float[] seqPos; // 某轴(x、y或z轴)方向序列的位置public RubikInfo() {}public RubikInfo(int order){this.order = order;InitOtherValue();}public RubikInfo(int order, float size){this.order = order;this.size = size;InitOtherValue();}public float GetPos(int seq){ // 获取seq对应的posreturn seqPos[seq];}public Vector3 GetPos(int[] seq){ // 获取seq对应的posreturn new Vector3(seqPos[seq[0]], seqPos[seq[1]], seqPos[seq[2]]);}public int GetSeq(float pos){ // 获取pos对应的seqint seq1 = (int) (pos / cubeSide + offset);float len1 = float.MaxValue;if (seq1 >=0 && seq1 < order){len1 = Mathf.Abs(seqPos[seq1] - pos);}int seq2 = seq1 + 1;float len2 = float.MaxValue;if (seq2 >=0 && seq2 < order){len2 = Mathf.Abs(seqPos[seq2] - pos);}if (len1 < len2 && len1 < quaCubeSide){return seq1;}if (len2 < len1 && len2 < quaCubeSide){return seq2;}return -1;}public int[] GetSeq(Vector3 pos){ // 获取pos对应的seqint[] seq = new int[3];for (int i = 0; i < 3; i++){seq[i] = GetSeq(pos[i]);}return seq;}private void InitOtherValue(){ // 初始化其他变量order2 = order * order;cubeSide = size / order;halfCubeSide = cubeSide / 2;quaCubeSide = halfCubeSide / 2;shuffle = Mathf.Min(Mathf.Max(order * order2, 100), 400);centerSeq = order / 2;GetSeqPos();}private void GetSeqPos(){ // 获取序列位置offset = (order - 1) / 2f;seqPos = new float[order];for (int i = 0; i < order; i++){seqPos[i] = (i - offset) * cubeSide;}}
}

        RubikLayer.cs

using System;/** 魔方层 / 旋转层* 由旋转轴和层序决定*/
public class RubikLayer
{public int axis; // 旋转轴, 0-x, 1-y, 2-zpublic int seq; // 层序private static int lastAxis = -1; // 上一次的随机旋转轴public RubikLayer() {}public RubikLayer(int axis,  int seq) {this.axis = axis;this.seq = seq;}public bool Equals(RubikLayer other){ // 判断两个旋转层是否相等return other != null && axis == other.axis && seq == other.seq;}public static RubikLayer GetRandomRubikLayer(Random rnd){RubikLayer rubikLayer = new RubikLayer();int axis = rnd.Next(0, 3);while (axis == lastAxis){axis = rnd.Next(0, 3);}lastAxis = rubikLayer.axis;rubikLayer.axis = axis;rubikLayer.seq = rnd.Next(0, Rubik.Info().order);while (rubikLayer.seq == Rubik.Info().centerSeq){rubikLayer.seq = rnd.Next(0, Rubik.Info().order);}return rubikLayer;}
}

3.4 魔方启动器

        RubikStarter.cs

using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
/** 魔方启动器* 负责启动魔方、更新步数、更新时间*/
public class RubikStarter : MonoBehaviour
{private static RubikStarter instance; // 实体private Rubik rubik; // 魔方private Button backButton; // 返回按钮private Text timeText; // 时间文本private Text stepText; // 步数文本private int time; // 时间private int step = 0; // 步数private WaitForSeconds waitForSeconds;public static RubikStarter Instance(){ // 获取实例return instance;}public void IncreaseStep(){ // 增加步数step++;stepText.text = step.ToString();}private void Awake(){instance = this;gameObject.layer = LayerMask.NameToLayer("Rubik");rubik = new Rubik(transform);backButton = GameObject.Find("Canvas/Back").GetComponent<Button>();timeText = GameObject.Find("Canvas/Time").GetComponent<Text>();stepText = GameObject.Find("Canvas/Step").GetComponent<Text>();backButton.onClick.AddListener(OnBack);waitForSeconds = new WaitForSeconds(1);}private void Start(){RotateController.Instance()?.SetRubik(rubik);RotateController.Instance()?.Shuffle();StartCoroutine(UpdateTime());}private IEnumerator UpdateTime(){ // 更新时间while(true){time += 1;int hour = time / 3600;int minus = time % 3600 / 60;int second = time % 60;timeText.text = hour.ToString("00") + ":" + minus.ToString("00") + ":" + second.ToString("00");yield return waitForSeconds;}}private void OnBack(){ // 返回按钮事件回调if (rubik != null){rubik.Destroy();}SceneManager.LoadScene("Select");}
}

        说明:RubikStarter 脚本挂在 Rubik 对象上。 

3.5 整体旋转

        CameraController.cs

using UnityEngine;/** 相机控制(整体旋转)* 缩放场景: Scroll/CtrlScroll* 旋转场景: Drag空白处/CtrlDrag/AltDrag/RightDrag/CtrlRightDrag/方向键*/
public class CameraController : MonoBehaviour 
{private Transform cam; // 相机private void Awake() {cam = Camera.main.transform;}private void Update(){ScaleScene();RotateScene();}private void ScaleScene() { // 缩放场景if (GlobalEvent.IsOnlyScrollEvent() || GlobalEvent.IsCtrlScrollEvent()){cam.position += cam.forward * GlobalEvent.scroll * 3;}}private void RotateScene() { // 旋转场景(Drag空白处/CtrlDrag/AltDrag/RightDrag/CtrlRightDrag/方向键)if (!(GlobalEvent.IsOnlyDragEvent(0) && GlobalEvent.target == null)&& !GlobalEvent.IsCtrlDragEvent(0) && !GlobalEvent.IsAltDragEvent(0)&& !GlobalEvent.IsOnlyDragEvent(1) && !GlobalEvent.IsCtrlDragEvent(1)&& !GlobalEvent.IsDireKeyEvent()){return;}Vector2 offset = GetRotateOffset();if (!offset.Equals(Vector2.zero)){cam.RotateAround(Vector3.zero, Vector3.up, offset.x); // 相机绕旋转中心水平旋转cam.RotateAround(Vector3.zero, cam.right, offset.y); // 相机绕旋转中心竖直旋转}}private Vector2 GetRotateOffset(){ // 获取旋转偏移量Vector2 offset = Vector2.zero;if (Mathf.Abs(GlobalEvent.mouseMove.x) > float.Epsilon || Mathf.Abs(GlobalEvent.mouseMove.y) > float.Epsilon){offset = new Vector2(GlobalEvent.mouseMove.x * 5, -GlobalEvent.mouseMove.y * 5);}else if (Mathf.Abs(GlobalEvent.direMove.x) > float.Epsilon || Mathf.Abs(GlobalEvent.direMove.y) > float.Epsilon){offset = new Vector2(GlobalEvent.direMove.x * 1.5f, -GlobalEvent.direMove.y * 1.5f);}if (!offset.Equals(Vector2.zero)){ // 当相机仰视观察魔方时, 水平旋转方向相反offset.x *= Mathf.Sign(Vector3.Dot(Vector3.up, cam.up));}return offset;}
}

        说明:CameraController 脚本挂在相机对象上。 

3.6 局部旋转

        RotateController.cs

using UnityEngine;/** 旋转控制器(局部旋转)*/
public class RotateController : MonoBehaviour
{private static RotateController instance; // 实例private Rubik rubik; // 魔方private Transform rotateLayer; // 旋转层对象private Cube[] rotateCubes; // 需要旋转的小立方体private Transform startSquare; // 开始滑动的方块private Transform endSquare; // 结束滑动的方块private RubikLayer rubikLayer; // 魔方层private float angle; // 旋转角度private Vector2 moveDire; // 滑动方向private System.Random random;public static RotateController Instance(){ // 获取实例return instance;}public void SetRubik(Rubik rubik){ // 设置魔方对象this.rubik = rubik;}public void Shuffle(){ // 打乱魔方for (int i = 0; i < Rubik.Info().shuffle; i++){rubikLayer = RubikLayer.GetRandomRubikLayer(random);angle = random.Next(-1, 3) * 90;GetRotateCubes();Rotate(angle);UpdateLoc();ResetRotateCubes();rubikLayer = null;rotateCubes = null;angle = 0;}}private void Awake(){instance = this;rotateLayer = gameObject.transform;random = new System.Random();}private void Update(){if (GlobalEvent.eventType == MyEventType.BeginDrag && IsHitRubik()){startSquare = GlobalEvent.hitObj.transform;}else if (GlobalEvent.eventType == MyEventType.Drag){if (startSquare != null && rubikLayer == null && IsHitRubik()){endSquare = GlobalEvent.hitObj.transform;rubikLayer = RotateHelper.GetLayer(startSquare, endSquare);GetRotateCubes();if (rubikLayer != null){moveDire = RotateHelper.GetPositiveScreenMoveDir(startSquare.position, endSquare.position, -startSquare.forward, rubikLayer.axis);}}else if (rubikLayer != null){angle += Vector2.Dot(GlobalEvent.mouseMove, moveDire) * 12;Rotate(angle);}}else if (GlobalEvent.eventType == MyEventType.EndDrag && rubikLayer != null){FinalRotate(angle);UpdateLoc();ResetRotateCubes();RubikStarter.Instance()?.IncreaseStep();startSquare = null;endSquare = null;rubikLayer = null;rotateCubes = null;angle = 0;}}private void Rotate(float angle){ // 旋转Vector3 axis, rotateStartDir, rotateEndDir;GetRotateInfo(out axis, out rotateStartDir, out rotateEndDir);float currAngle = Vector3.SignedAngle(rotateStartDir, rotateEndDir, axis);rotateLayer.RotateAround(Vector3.zero, axis, angle - currAngle);}private void FinalRotate(float angle){ // 拖拽结束后, 更新旋转角度, 使得旋转层对齐魔方angle = RotateHelper.GetNearAngle(angle);Rotate(angle);}private void UpdateLoc(){ // 更新小立方体的位置序号if (rotateCubes != null){for (int i = 0; i < rotateCubes.Length; i++){rotateCubes[i].UpdateLoc();}}}private void GetRotateInfo(out Vector3 axis, out Vector3 rotateStartDir, out Vector3 rotateEndDir){ // 获取旋转轴if (rubikLayer.axis == 0){axis = Vector3.right;rotateStartDir = Vector3.up;rotateEndDir = rotateLayer.up;}else if (rubikLayer.axis == 1){axis = Vector3.up;rotateStartDir = Vector3.forward;rotateEndDir = rotateLayer.forward;}else{axis = Vector3.forward;rotateStartDir = Vector3.right;rotateEndDir = rotateLayer.right;}}private void GetRotateCubes(){ // 获取需要旋转的小立方体if (rubikLayer != null){rotateCubes = rubik.GetRotateCubes(rubikLayer);for (int i = 0; i < rotateCubes.Length; i++){rotateCubes[i].self.SetParent(rotateLayer);}}}private void ResetRotateCubes(){ // 恢复旋转的立方体if (rotateCubes != null){for (int i = 0; i < rotateCubes.Length; i++){rotateCubes[i].self.SetParent(rubik.self);}}rotateLayer.rotation = Quaternion.identity;}private bool IsHitRubik(){ // 屏幕射线是否投射到魔方上if (GlobalEvent.hitObj == null || GlobalEvent.hitObj.layer != LayerMask.NameToLayer("Rubik")){return false;}return true;}
}

        说明:RotateController 挂在 RotateLayer 对象上。

        RotateHelper.cs

using UnityEngine;/** 旋转助手* 分担了RotateController的部分功能*/
public class RotateHelper
{public static RubikLayer GetLayer(Transform square1, Transform square2){ // 获取两个方块所处的魔方图层if (square1 == null || square2 == null || square1 == square2){return null;}int axis = GetAxis(square1, square2);if (axis < 0){return null;}int seq = Rubik.Info().GetSeq(square1.position[axis]);if (seq >= 0){return new RubikLayer(axis, seq);}return null;}public static float GetNearAngle(float angle){ // 获取离angle较近的90度整数倍度数if (Mathf.Abs(angle) < 45 || Mathf.Abs(angle) > 315){return 0;}else if (Mathf.Abs(angle) < 135){return Mathf.Sign(angle) * 90;}else if (Mathf.Abs(angle) < 225){return 180;}return Mathf.Sign(angle) * 270;}public static Vector3 GetAxis(int axis){ // 根据轴编号获取对应的轴switch(axis){case 0:return Vector3.right;case 1:return Vector3.up;case 2:return Vector3.forward;}return Vector3.zero;}public static Vector2 GetPositiveScreenMoveDir(Vector3 pos1, Vector3 pos2, Vector3 forward, int axis){ // 获取正向屏幕移动方向向量Vector2 scrPos1 = Camera.main.WorldToScreenPoint(pos1);Vector2 scrPos2 = Camera.main.WorldToScreenPoint(pos2);Vector2 dir = (scrPos2 - scrPos1).normalized;Vector3 axisDir = GetAxis(axis);if (Vector3.Dot(Vector3.Cross(forward, pos2 - pos1), axisDir) < 0){return -dir;}return dir;}private static int GetAxis(Transform square1, Transform square2){ // 获取square1和square2的共同轴Vector3 dire = Vector3.zero;if (square1.parent == square2.parent){dire = Vector3.Cross(square1.forward, square2.forward).normalized;}else{dire = Vector3.Cross(square1.forward, square1.position - square2.position).normalized;}if (Mathf.Abs(Vector3.Dot(dire, Vector3.right)) > 0.99f){return 0;}if (Mathf.Abs(Vector3.Dot(dire, Vector3.up)) > 0.99f){return 1;}if (Mathf.Abs(Vector3.Dot(dire, Vector3.forward)) > 0.99f){return 2;}return -1;}
}

3.7 选择阶数

        LoadRubik.cs

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;/** 加载魔方(选择阶数)*/
public class LoadRubik : MonoBehaviour
{private Transform orderParent; // 阶数父对象private Button[] orderButtons; // 阶数按钮private void Awake(){orderParent = GameObject.Find("Canvas/Select/Order").transform;orderButtons = orderParent.GetComponentsInChildren<Button>();for (int i = 0; i < orderButtons.Length; i++){int order = i + 2;orderButtons[i].onClick.AddListener(() => {OnClick(order);});}}private void OnClick(int order) {Rubik.SetRubikInfo(new RubikInfo(order));SceneManager.LoadScene("Rubik");}
}

        说明:LoadRubik 脚本挂在第一个场景的 “Canvas / Select / Order” 对象上。

3.8 事件模块

        GlobalEvent.cs

using UnityEngine;/** 全局事件* 事件类型的获取, 建议从这里获取, 不要直接调用Input.GetXXX方法获取事件*/
public class GlobalEvent
{public static MyEventType eventType = MyEventType.None; // 事件类型public static MyKeyCode keyCode = MyKeyCode.None; // 按键编码public static Vector3 mousePos; // 当前鼠标位置public static Vector2 mouseMove; // 相比上一帧, 鼠标的偏移量(取值-1~1)public static Vector2 mouseScreenMove; // 相比上一帧, 鼠标的在屏幕上的偏移量public static Vector2 direMove; // 方向按键或A、D、W、S按键变化量public static Vector3 hitPoint; // 屏幕射线碰撞到的物体表面的点public static GameObject hitObj; // 屏幕射线碰撞到的物体public static float scroll; // 滑轮滑动刻度public static GameObject target; // 事件作用的目标对象public static GameObject lastTarget; // 上一帧事件作用的目标对象public static bool IsOnlyDragEvent(int flag){ // 是否是拖拽事件(单事件, flag取值0,1,2, 分别表示左键、右键、中键)if (!EventTypeUtils.IsOnlyDragEvent(eventType)){return false;}if (flag == 0 && EventTypeUtils.IsLeftDragEvent(eventType)){return true;}if (flag == 1 && EventTypeUtils.IsRightDragEvent(eventType)){return true;}if (flag == 2 && EventTypeUtils.IsMiddleDragEvent(eventType)){return true;}return false;}public static bool IsCtrlDragEvent(int flag){ // 是否是拖拽事件(Ctrl复合事件, flag取值0,1,2, 分别表示左键、右键、中键)if (!EventTypeUtils.IsCtrlDragEvent(eventType)){return false;}if (flag == 0 && EventTypeUtils.IsLeftDragEvent(eventType)){return true;}if (flag == 1 && EventTypeUtils.IsRightDragEvent(eventType)){return true;}if (flag == 2 && EventTypeUtils.IsMiddleDragEvent(eventType)){return true;}return false;}public static bool IsAltDragEvent(int flag){ // 是否是拖拽事件(Alt复合事件, flag取值0,1,2, 分别表示左键、右键、中键)if (!EventTypeUtils.IsAltDragEvent(eventType)){return false;}if (flag == 0 && EventTypeUtils.IsLeftDragEvent(eventType)){return true;}if (flag == 1 && EventTypeUtils.IsRightDragEvent(eventType)){return true;}if (flag == 2 && EventTypeUtils.IsMiddleDragEvent(eventType)){return true;}return false;}public static bool IsOnlyScrollEvent(){ // 是否是缩放事件(单事件)return EventTypeUtils.IsOnlyScrollEvent(eventType);}public static bool IsCtrlScrollEvent(){ // 是否是缩放事件(Ctrl复合事件)return EventTypeUtils.IsCtrlScrollEvent(eventType);}public static bool IsAltScrollEvent(){ // 是否是缩放事件(Alt复合事件)return EventTypeUtils.IsAltScrollEvent(eventType);}public static bool IsDireKeyEvent(){ // 方向键事件(上下左右箭头或W、S、A、D按键)return EventTypeUtils.IsDireKeyEvent(eventType, keyCode);}
}

        EventDetector.cs

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;/** 事件检测器*/
public class EventDetector : MonoBehaviour 
{private const float PAUSE_SCROLL = 0.5f; // 滑轮的最小时间间隔将被识别为滑轮结束事件private List<RaycastResult> raycastResults = new List<RaycastResult>(); // 检测鼠标是否悬浮在UI上private PointerEventData eventData; // UI事件检测数据private Dictionary<MyKeyCode, KeyCode[]> keysMap; // 按键字典private Vector3 hitPoint; // 射线检测碰撞点位置private MyEventType eventType; // 事件类型private MyKeyCode keyCode; // 按键编码private GameObject hitObj; // 射线检测到的物体private Vector2 mouseMove; // 相比上一帧, 鼠标的偏移量private Vector2 direMove; // 方向按键或A、D、W、S按键变化量private float scroll; // 滑轮滑动刻度private float lastScrollTime; // 上次滑轮时间(用于识别结束滑轮)private bool isContinueEvent = false; // 是否是可延续的事件(拖拽、滑轮、单击、悬浮事件)private void Awake(){EventTypeUtils.Init();keysMap = KeyUtils.GetKeyMap();eventData = new PointerEventData(EventSystem.current);}private void Update(){ResetStatus();PreSetValue();DetectContinueEvent();DetectDragEvent();DetectClickEvent();DetectScrollEvent();DetectKeyEvent();DetectHorverEvent();SetGlobalEvent();}private void ResetStatus(){ // 重置状态eventType = MyEventType.None;keyCode = MyKeyCode.None;hitObj = null;mouseMove = Vector2.zero;scroll = 0;isContinueEvent = false;}private void PreSetValue(){ // 预设变量mouseMove = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")); // -1~1之间direMove = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")); // -1~1之间float hor = Input.GetAxis("Horizontal");float ver = Input.GetAxis("Vertical");GetHoverObject();}private void DetectContinueEvent(){ // 检测延续事件(如果是拖拽事件, 会延续)if (GlobalEvent.eventType == MyEventType.None || EventTypeUtils.IsKeyEvent(GlobalEvent.eventType)||  EventTypeUtils.IsHorverEvent(GlobalEvent.eventType)){ // 空事件、按键事件、Horver事件不需要延续return;}if (EventTypeUtils.IsBeginDragEvent(GlobalEvent.eventType) || EventTypeUtils.IsBeginScrollEvent(GlobalEvent.eventType)){ // Begin Drag -> Drag, Begin Scroll -> ScrollisContinueEvent = true;eventType = EventStatusUtils.GetNextEvent(GlobalEvent.eventType);return;}if (EventTypeUtils.IsDraggingEvent(GlobalEvent.eventType)){isContinueEvent = true;if (EventTypeUtils.IsLeftDragEvent(GlobalEvent.eventType) && Input.GetMouseButton(0) && !Input.GetMouseButtonUp(0)|| EventTypeUtils.IsRightDragEvent(GlobalEvent.eventType) && Input.GetMouseButton(1) && !Input.GetMouseButtonUp(1)|| EventTypeUtils.IsMiddleDragEvent(GlobalEvent.eventType) && Input.GetMouseButton(2) && !Input.GetMouseButtonUp(2)){ // Drag -> End Drag, Scroll -> End ScrolleventType = GlobalEvent.eventType;return;}eventType = EventStatusUtils.GetNextDragEvent(GlobalEvent.eventType); // Dragreturn;}if (EventTypeUtils.IsScrollingEvent(GlobalEvent.eventType)){isContinueEvent = true;scroll = Input.GetAxis("Mouse ScrollWheel");if (Mathf.Abs(scroll) < float.Epsilon && Time.realtimeSinceStartup - lastScrollTime > PAUSE_SCROLL){ // Scroll -> End ScrolleventType = EventStatusUtils.GetNextScrollEvent(GlobalEvent.eventType);return;}if (Mathf.Abs(scroll) > float.Epsilon){lastScrollTime = Time.realtimeSinceStartup;}eventType = GlobalEvent.eventType; // Scrollreturn;}}private void DetectDragEvent(){ // 检查拖拽事件if (eventType != MyEventType.None){return;}bool clicked = Input.GetMouseButton(0) || Input.GetMouseButton(1) || Input.GetMouseButton(2);if (clicked && (Mathf.Abs(mouseMove.x) > 0.1f || Mathf.Abs(mouseMove.y) > 0.1f)){if (Input.GetMouseButton(0)){if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)){eventType = MyEventType.BeginCtrlDrag;}else if (Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt)){eventType = MyEventType.BeginAltDrag;}else{eventType = MyEventType.BeginDrag;}}else if (Input.GetMouseButton(1)){if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)){eventType = MyEventType.BeginCtrlRightDrag;}else if (Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt)){eventType = MyEventType.BeginAltRightDrag;}else{eventType = MyEventType.BeginRightDrag;}}else if (Input.GetMouseButton(2)){if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)){eventType = MyEventType.BeginCtrlMiddleDrag;}else if (Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt)){eventType = MyEventType.BeginAltMiddleDrag;}else{eventType = MyEventType.BeginMiddleDrag;}}}}private void DetectClickEvent(){ // 检查单击事件if (eventType != MyEventType.None){return;}if (EventTypeUtils.IsClickEvent(GlobalEvent.eventType)){if (EventTypeUtils.IsClickDownEvent(GlobalEvent.eventType)){isContinueEvent = true;eventType = EventStatusUtils.GetNextClickEvent(GlobalEvent.eventType);return;}if (EventTypeUtils.IsClickingEvent(GlobalEvent.eventType)){if (Input.GetMouseButtonUp(0) || Input.GetMouseButtonUp(1) || Input.GetMouseButtonUp(2)){isContinueEvent = true;eventType = EventStatusUtils.GetNextClickEvent(GlobalEvent.eventType);return;}if (Input.GetMouseButton(0) || Input.GetMouseButton(1) || Input.GetMouseButton(2)){isContinueEvent = true;eventType = GlobalEvent.eventType;return;}}}if (Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1) || Input.GetMouseButtonDown(2)){if (Input.GetMouseButtonDown(0)){if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)){eventType = MyEventType.CtrlClickDown;}else if (Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt)){eventType = MyEventType.AltClickDown;}else{eventType = MyEventType.ClickDown;}}else if (Input.GetMouseButtonDown(1)){if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)){eventType = MyEventType.CtrlRightClickDown;}else if (Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt)){eventType = MyEventType.AltRightClickDown;}else{eventType = MyEventType.RightClickDown;}}else if (Input.GetMouseButtonDown(2)){if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)){eventType = MyEventType.CtrlMiddleClickDown;}else if (Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt)){eventType = MyEventType.AltMiddleClickDown;}else{eventType = MyEventType.MiddleClickDown;}}}}private void DetectScrollEvent(){ // 检查滑轮事件if (eventType != MyEventType.None){return;}scroll = Input.GetAxis("Mouse ScrollWheel");if (Mathf.Abs(scroll) > float.Epsilon){lastScrollTime = Time.realtimeSinceStartup;if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)){eventType = MyEventType.BeginCtrlScroll;}else if (Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt)){eventType = MyEventType.BeginAltScroll;}else{eventType = MyEventType.BeginScroll;}}}private void DetectKeyEvent(){ // 检测按键事件if (eventType != MyEventType.None){return;}foreach (var entry in keysMap){bool find = true;foreach (var keyCode in entry.Value){if (!Input.GetKey(keyCode)){find = false;break;}}if (find){KeyCode lastKey = entry.Value[entry.Value.Length - 1];if (Input.GetKeyDown(lastKey)){eventType = MyEventType.PressKeyDown;}else if (Input.GetKeyUp(lastKey)){eventType = MyEventType.PressKeyUp;}else{eventType = MyEventType.PressKey;}keyCode = entry.Key;break;}}}private void DetectHorverEvent(){ // 检测Horver事件if (eventType != MyEventType.None || GlobalEvent.target == null && hitObj == null){return;}if (GlobalEvent.target == hitObj){eventType = MyEventType.Horver;}else{eventType = MyEventType.EnterHorver;}}private void SetGlobalEvent(){ // 设置全局事件GlobalEvent.eventType = eventType;GlobalEvent.keyCode = keyCode;GlobalEvent.mouseScreenMove = Input.mousePosition - GlobalEvent.mousePos;GlobalEvent.mousePos = Input.mousePosition;GlobalEvent.mouseMove = mouseMove;GlobalEvent.direMove = direMove;GlobalEvent.hitPoint = hitPoint;GlobalEvent.hitObj = hitObj;GlobalEvent.scroll = scroll;GlobalEvent.lastTarget = GlobalEvent.target;if (!isContinueEvent){GlobalEvent.target = hitObj;}}private void GetHoverObject(){ // 获取鼠标悬浮的对象hitObj = GetHoverUIObject();if (hitObj == null){hitObj = GetHover3DObject();}}private GameObject GetHoverUIObject(){ // 获取鼠标悬浮的UI对象raycastResults.Clear();eventData.position = Input.mousePosition;if (EventSystem.current != null){EventSystem.current.RaycastAll(eventData, raycastResults);}if (raycastResults.Count > 0){hitPoint = raycastResults[0].worldPosition;return raycastResults[0].gameObject;}return null;}private GameObject GetHover3DObject(){ // 获取鼠标悬浮的3D对象RaycastHit hitInfo;Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);if (Physics.Raycast(ray, out hitInfo)){hitPoint = hitInfo.point;return hitInfo.collider.gameObject;}return null;}
}

        说明:EventDetector 脚本挂在相机对象上。

        MyEventType.cs

using System.Collections.Generic;/** 自定义事件类型*/
public enum MyEventType
{ // 事件类型None = 0, // 空事件// 鼠标单击事件(单事件)ClickDown = 1, // 鼠标按下(左键)Click = 2, // 鼠标点击(左键)ClickUp = 3, // 鼠标抬起(左键)RightClickDown = 4, // 鼠标按下(右键)RightClick = 5, // 鼠标点击(右键)RightClickUp = 6, // 鼠标抬起(右键)MiddleClickDown = 7, // 鼠标按下(中键)MiddleClick = 8, // 鼠标点击(中键)MiddleClickUp = 9, // 鼠标抬起(中键)// 鼠标单击事件(混合Ctrl按键事件)CtrlClickDown = 10, // Ctrl+鼠标按下(左键)CtrlClick = 11, // Ctrl+鼠标点击(左键)CtrlClickUp = 12, // Ctrl+鼠标抬起(左键)CtrlRightClickDown = 13, // Ctrl+鼠标按下(右键)CtrlRightClick = 14, // Ctrl+鼠标点击(右键)CtrlRightClickUp = 15, // Ctrl+鼠标抬起(右键)CtrlMiddleClickDown = 16, // Ctrl+鼠标按下(中键)CtrlMiddleClick = 17, // Ctrl+鼠标点击(中键)CtrlMiddleClickUp = 18, // Ctrl+鼠标抬起(中键)// 鼠标单击事件(混合Alt按键事件)AltClickDown = 20, // Alt+鼠标按下(左键)AltClick = 21, // Alt+鼠标点击(左键)AltClickUp = 22, // Alt+鼠标抬起(左键)AltRightClickDown = 23, // Alt+鼠标按下(右键)AltRightClick = 24, // Alt+鼠标点击(右键)AltRightClickUp = 25, // Alt+鼠标抬起(右键)AltMiddleClickDown = 26, // Alt+鼠标按下(中键)AltMiddleClick = 27, // Alt+鼠标点击(中键)AltMiddleClickUp = 28, // Alt+鼠标抬起(中键)
//--------------------------------------------------------------------------------------// 鼠标拖拽事件(单事件)BeginDrag = 30, // 开始拖拽(左键)Drag = 31, // 拖拽中(左键)EndDrag = 32, // 结束拖拽(左键)BeginRightDrag = 33, // 开始拖拽(右键)RightDrag = 34, // 拖拽中(右键)EndRightDrag = 35, // 结束拖拽(右键)BeginMiddleDrag = 36, // 开始拖拽(中键)MiddleDrag = 37, // 拖拽中(中键)EndMiddleDrag = 38, // 结束拖拽(中键)// 鼠标拖拽事件(混合Ctrl按键事件)BeginCtrlDrag = 40, // 开始Ctrl拖拽(左键)CtrlDrag = 41, // Ctrl拖拽中(左键)EndCtrlDrag = 42, // 结束Ctrl拖拽(左键)BeginCtrlRightDrag = 43, // 开始Ctrl拖拽(右键)CtrlRightDrag = 44, // Ctrl拖拽中(右键)EndCtrlRightDrag = 45, // Ctrl结束拖拽(右键)BeginCtrlMiddleDrag = 46, // 开始Ctrl拖拽(中键)CtrlMiddleDrag = 47, // Ctrl拖拽中(中键)EndCtrlMiddleDrag = 48, // 结束Ctrl拖拽(中键)// 鼠标拖拽事件(混合Alt按键事件)BeginAltDrag = 50, // 开始Alt拖拽(左键)AltDrag = 51, // Alt拖拽中(左键)EndAltDrag = 52, // 结束Alt拖拽(左键)BeginAltRightDrag = 53, // 开始Alt拖拽(右键)AltRightDrag = 54, // Alt拖拽中(右键)EndAltRightDrag = 55, // Alt结束拖拽(右键)BeginAltMiddleDrag = 56, // 开始Alt拖拽(中键)AltMiddleDrag = 57, // Alt拖拽中(中键)EndAltMiddleDrag = 58, // 结束Alt拖拽(中键)
//--------------------------------------------------------------------------------------// 滑轮事件BeginScroll = 60, // 开始滑轮Scroll = 61, // 滑轮中EndScroll = 62, // 结束滑轮BeginCtrlScroll = 63, // 开始Ctrl滑轮CtrlScroll = 64, // Ctrl滑轮中EndCtrlScroll = 65, // 结束Ctrl滑轮BeginAltScroll = 66, // 开始Alt滑轮AltScroll = 67, // Alt滑轮中EndAltScroll = 68, // 结束Alt滑轮
//--------------------------------------------------------------------------------------// 按键事件PressKeyDown = 70, // 按下按键PressKey = 71, // 按住按键PressKeyUp = 72, // 抬起按键
//--------------------------------------------------------------------------------------// horver事件EnterHorver = 80, // 鼠标进入悬浮Horver = 81, // 鼠标悬浮中LeftHorver = 82, // 鼠标离开悬浮
}

        MyKeyCode.cs

using System.Collections.Generic;
using UnityEngine;/** 自定义按键编码*/
public enum MyKeyCode
{None = 0, // 无按键事件// 方向键A = 1,D = 2,W = 3, S = 4,UpArrow = 5,DownArrow = 6,LeftArrow = 7,RightArrow = 8,// 单键(字母)B = 10,C = 11,E = 12,F = 13,Q = 14,R = 15,T = 16,V = 17,X = 18,Z = 19,// 单键(其他)ESC = 30,Delete = 31,Space = 32,Backspace = 33,Ctrl = 34,Shift = 35,Alt = 36,// 双键CtrlA = 50,CtrlC = 51,CtrlD = 52,CtrlF = 53,CtrlS = 54,CtrlV = 55,CtrlX = 56,CtrlZ = 57,CtrlSpace = 58,// 三键CtrlShiftZ = 70,
}/** 按键映射*/
public class KeyUtils
{private static Dictionary<MyKeyCode, KeyCode[]> keysMap;public static Dictionary<MyKeyCode, KeyCode[]> GetKeyMap(){if (keysMap != null){return keysMap;}keysMap = new Dictionary<MyKeyCode, KeyCode[]>();keysMap.Add(MyKeyCode.CtrlShiftZ, new KeyCode[] {KeyCode.LeftControl, KeyCode.LeftShift, KeyCode.Z});keysMap.Add(MyKeyCode.CtrlA, new KeyCode[] {KeyCode.LeftControl, KeyCode.A});keysMap.Add(MyKeyCode.CtrlC, new KeyCode[] {KeyCode.LeftControl, KeyCode.C});keysMap.Add(MyKeyCode.CtrlD, new KeyCode[] {KeyCode.LeftControl, KeyCode.D});keysMap.Add(MyKeyCode.CtrlF, new KeyCode[] {KeyCode.LeftControl, KeyCode.F});keysMap.Add(MyKeyCode.CtrlS, new KeyCode[] {KeyCode.LeftControl, KeyCode.S});keysMap.Add(MyKeyCode.CtrlV, new KeyCode[] {KeyCode.LeftControl, KeyCode.V});keysMap.Add(MyKeyCode.CtrlX, new KeyCode[] {KeyCode.LeftControl, KeyCode.X});keysMap.Add(MyKeyCode.CtrlZ, new KeyCode[] {KeyCode.LeftControl, KeyCode.Z});keysMap.Add(MyKeyCode.CtrlSpace, new KeyCode[] {KeyCode.LeftControl, KeyCode.Space});keysMap.Add(MyKeyCode.A, new KeyCode[] {KeyCode.A});keysMap.Add(MyKeyCode.B, new KeyCode[] {KeyCode.B});keysMap.Add(MyKeyCode.C, new KeyCode[] {KeyCode.C});keysMap.Add(MyKeyCode.D, new KeyCode[] {KeyCode.D});keysMap.Add(MyKeyCode.E, new KeyCode[] {KeyCode.E});keysMap.Add(MyKeyCode.F, new KeyCode[] {KeyCode.F});keysMap.Add(MyKeyCode.Q, new KeyCode[] {KeyCode.Q});keysMap.Add(MyKeyCode.R, new KeyCode[] {KeyCode.R});keysMap.Add(MyKeyCode.S, new KeyCode[] {KeyCode.S});keysMap.Add(MyKeyCode.T, new KeyCode[] {KeyCode.T});keysMap.Add(MyKeyCode.V, new KeyCode[] {KeyCode.V});keysMap.Add(MyKeyCode.W, new KeyCode[] {KeyCode.W});keysMap.Add(MyKeyCode.X, new KeyCode[] {KeyCode.X});keysMap.Add(MyKeyCode.Z, new KeyCode[] {KeyCode.Z});keysMap.Add(MyKeyCode.UpArrow, new KeyCode[] {KeyCode.UpArrow});keysMap.Add(MyKeyCode.DownArrow, new KeyCode[] {KeyCode.DownArrow});keysMap.Add(MyKeyCode.LeftArrow, new KeyCode[] {KeyCode.LeftArrow});keysMap.Add(MyKeyCode.RightArrow, new KeyCode[] {KeyCode.RightArrow});keysMap.Add(MyKeyCode.ESC, new KeyCode[] {KeyCode.Escape});keysMap.Add(MyKeyCode.Delete, new KeyCode[] {KeyCode.Delete});keysMap.Add(MyKeyCode.Space, new KeyCode[] {KeyCode.Space});keysMap.Add(MyKeyCode.Backspace, new KeyCode[] {KeyCode.Backspace});keysMap.Add(MyKeyCode.Ctrl, new KeyCode[] {KeyCode.LeftControl});keysMap.Add(MyKeyCode.Shift, new KeyCode[] {KeyCode.LeftShift});keysMap.Add(MyKeyCode.Alt, new KeyCode[] {KeyCode.LeftAlt});return keysMap;}
}

        EventTypeUtils.cs

using System.Collections.Generic;/** 事件类型工具类*/
public class EventTypeUtils
{private static SortedSet<MyEventType> clickDownEvent; // 点击按下事件private static SortedSet<MyEventType> clickingEvent; // 点击中事件private static SortedSet<MyEventType> clickUpEvent; // 点击抬起事件private static SortedSet<MyEventType> beginDragEvent; // 开始拖拽事件private static SortedSet<MyEventType> draggingEvent; // 拖拽进行中事件private static SortedSet<MyEventType> endDragEvent; // 拖拽结束事件public static void Init(){clickDownEvent = new SortedSet<MyEventType>(new MyEventType[] {MyEventType.ClickDown, MyEventType.CtrlClickDown, MyEventType.AltClickDown,MyEventType.RightClickDown, MyEventType.CtrlRightClickDown, MyEventType.AltRightClickDown,MyEventType.MiddleClickDown, MyEventType.CtrlMiddleClickDown, MyEventType.AltMiddleClickDown});clickingEvent = new SortedSet<MyEventType>(new MyEventType[] {MyEventType.Click, MyEventType.CtrlClick, MyEventType.AltClick,MyEventType.RightClick, MyEventType.CtrlRightClick, MyEventType.AltRightClick,MyEventType.MiddleClick, MyEventType.CtrlMiddleClick, MyEventType.AltMiddleClick});clickUpEvent = new SortedSet<MyEventType>(new MyEventType[] {MyEventType.ClickUp, MyEventType.CtrlClickUp, MyEventType.AltClickUp,MyEventType.RightClickUp, MyEventType.CtrlRightClickUp, MyEventType.AltRightClickUp,MyEventType.MiddleClickUp, MyEventType.CtrlMiddleClickUp, MyEventType.AltMiddleClickUp});beginDragEvent = new SortedSet<MyEventType>(new MyEventType[] {MyEventType.BeginDrag, MyEventType.BeginCtrlDrag, MyEventType.BeginAltDrag,MyEventType.BeginRightDrag, MyEventType.BeginCtrlRightDrag, MyEventType.BeginAltRightDrag,MyEventType.BeginMiddleDrag, MyEventType.BeginCtrlMiddleDrag, MyEventType.BeginAltMiddleDrag});draggingEvent = new SortedSet<MyEventType>(new MyEventType[] {MyEventType.Drag, MyEventType.CtrlDrag, MyEventType.AltDrag,MyEventType.RightDrag, MyEventType.CtrlRightDrag, MyEventType.AltRightDrag,MyEventType.MiddleDrag, MyEventType.CtrlMiddleDrag, MyEventType.AltMiddleDrag});endDragEvent = new SortedSet<MyEventType>(new MyEventType[] {MyEventType.EndDrag, MyEventType.EndCtrlDrag, MyEventType.EndAltDrag,MyEventType.EndRightDrag, MyEventType.EndCtrlRightDrag, MyEventType.EndAltRightDrag,MyEventType.EndMiddleDrag, MyEventType.EndCtrlMiddleDrag, MyEventType.EndAltMiddleDrag});}// 点击事件--------------------------------------------------------------------------------------public static bool IsClickEvent(MyEventType eventType){ // 是否是点击事件return eventType >= MyEventType.ClickDown && eventType <= MyEventType.MiddleClickUp|| eventType >= MyEventType.CtrlClickDown && eventType <= MyEventType.CtrlMiddleClickUp|| eventType >= MyEventType.AltClickDown && eventType <= MyEventType.AltMiddleClickUp;}public static bool IsOnlyClickEvent(MyEventType eventType){ // 是否是点击事件(单事件)return eventType >= MyEventType.ClickDown && eventType <= MyEventType.MiddleClickUp;}public static bool IsCtrlClickEvent(MyEventType eventType){ // 是否是Ctrl点击事件return eventType >= MyEventType.CtrlClickDown && eventType <= MyEventType.CtrlMiddleClickUp;}public static bool IsAltClickEvent(MyEventType eventType){ // 是否是Alt点击事件return eventType >= MyEventType.AltClickDown && eventType <= MyEventType.AltMiddleClickUp;}public static bool IsLeftClickEvent(MyEventType eventType){ // 是否是左键点击事件return eventType >= MyEventType.ClickDown && eventType <= MyEventType.ClickUp|| eventType >= MyEventType.CtrlClickDown && eventType <= MyEventType.CtrlClickUp|| eventType >= MyEventType.AltClickDown && eventType <= MyEventType.AltClickUp;}public static bool IsRightClickEvent(MyEventType eventType){ // 是否是右键点击事件return eventType >= MyEventType.RightClickDown && eventType <= MyEventType.RightClickUp|| eventType >= MyEventType.CtrlRightClickDown && eventType <= MyEventType.CtrlRightClickUp|| eventType >= MyEventType.AltRightClickDown && eventType <= MyEventType.AltRightClickUp;}public static bool IsMiddleClickEvent(MyEventType eventType){ // 是否是中键点击事件return eventType >= MyEventType.MiddleClickDown && eventType <= MyEventType.MiddleClickUp|| eventType >= MyEventType.CtrlMiddleClickDown && eventType <= MyEventType.CtrlMiddleClickUp|| eventType >= MyEventType.AltMiddleClickDown && eventType <= MyEventType.AltMiddleClickUp;}public static bool IsClickDownEvent(MyEventType eventType){ // 是否是点击按下事件return clickDownEvent.Contains(eventType);}public static bool IsClickingEvent(MyEventType eventType){ // 是否是点击进行中事件return clickingEvent.Contains(eventType);}public static bool IsClickUpEvent(MyEventType eventType){ // 是否是点击抬起事件return clickUpEvent.Contains(eventType);}// 拖拽事件--------------------------------------------------------------------------------------public static bool IsDragEvent(MyEventType eventType){ // 是否是拖拽事件return eventType >= MyEventType.BeginDrag && eventType <= MyEventType.EndMiddleDrag|| eventType >= MyEventType.BeginCtrlDrag && eventType <= MyEventType.EndCtrlMiddleDrag|| eventType >= MyEventType.BeginAltDrag && eventType <= MyEventType.EndAltMiddleDrag;}public static bool IsOnlyDragEvent(MyEventType eventType){ // 是否是拖拽事件(单事件)return eventType >= MyEventType.BeginDrag && eventType <= MyEventType.EndMiddleDrag;}public static bool IsCtrlDragEvent(MyEventType eventType){ // 是否是Ctrl拖拽事件return eventType >= MyEventType.BeginCtrlDrag && eventType <= MyEventType.EndCtrlMiddleDrag;}public static bool IsAltDragEvent(MyEventType eventType){ // 是否是Alt拖拽事件return eventType >= MyEventType.BeginAltDrag && eventType <= MyEventType.EndAltMiddleDrag;}public static bool IsLeftDragEvent(MyEventType eventType){ // 是否是左键拖拽事件return eventType >= MyEventType.BeginDrag && eventType <= MyEventType.EndDrag|| eventType >= MyEventType.BeginCtrlDrag && eventType <= MyEventType.EndCtrlDrag|| eventType >= MyEventType.BeginAltDrag && eventType <= MyEventType.EndAltDrag;}public static bool IsRightDragEvent(MyEventType eventType){ // 是否是右键拖拽事件return eventType >= MyEventType.BeginRightDrag && eventType <= MyEventType.EndRightDrag|| eventType >= MyEventType.BeginCtrlRightDrag && eventType <= MyEventType.EndCtrlRightDrag|| eventType >= MyEventType.BeginAltRightDrag && eventType <= MyEventType.EndAltRightDrag;}public static bool IsMiddleDragEvent(MyEventType eventType){ // 是否是中键拖拽事件return eventType >= MyEventType.BeginMiddleDrag && eventType <= MyEventType.EndMiddleDrag|| eventType >= MyEventType.BeginCtrlMiddleDrag && eventType <= MyEventType.EndCtrlMiddleDrag|| eventType >= MyEventType.BeginAltMiddleDrag && eventType <= MyEventType.EndAltMiddleDrag;}public static bool IsBeginDragEvent(MyEventType eventType){ // 是否是开始拖拽事件return beginDragEvent.Contains(eventType);}public static bool IsDraggingEvent(MyEventType eventType){ // 是否是拖拽进行中事件return draggingEvent.Contains(eventType);}public static bool IsEndDragEvent(MyEventType eventType){ // 是否是结束拖拽事件return endDragEvent.Contains(eventType);}// 滑轮事件--------------------------------------------------------------------------------------public static bool IsScrollEvent(MyEventType eventType){ // 是否是滑轮事件return eventType >= MyEventType.BeginScroll && eventType <= MyEventType.EndAltScroll;}public static bool IsOnlyScrollEvent(MyEventType eventType){ // 是否是滑轮事件(单事件)return eventType >= MyEventType.BeginScroll && eventType <= MyEventType.EndScroll;}public static bool IsCtrlScrollEvent(MyEventType eventType){ // 是否是Ctrl滑轮事件return eventType >= MyEventType.BeginCtrlScroll && eventType <= MyEventType.EndCtrlScroll;}public static bool IsAltScrollEvent(MyEventType eventType){ // 是否是Alt滑轮事件return eventType >= MyEventType.BeginAltScroll && eventType <= MyEventType.EndAltScroll;}public static bool IsBeginScrollEvent(MyEventType eventType){ // 是否是开始滑轮事件return eventType == MyEventType.BeginScroll || eventType == MyEventType.BeginCtrlScroll || eventType == MyEventType.BeginAltScroll;}public static bool IsScrollingEvent(MyEventType eventType){ // 是否是滑轮进行中事件return eventType == MyEventType.Scroll || eventType == MyEventType.CtrlScroll || eventType == MyEventType.AltScroll;}public static bool IsEndScrollEvent(MyEventType eventType){ // 是否是结束滑轮事件return eventType == MyEventType.EndScroll || eventType == MyEventType.EndCtrlScroll || eventType == MyEventType.EndAltScroll;}// 按键事件--------------------------------------------------------------------------------------public static bool IsKeyEvent(MyEventType eventType){ // 是否是按键事件return eventType >= MyEventType.PressKeyDown && eventType <= MyEventType.PressKeyUp;}public static bool IsDireKeyEvent(MyEventType eventType, MyKeyCode keyCode){ // 方向键事件(上下左右箭头或W、S、A、D按键)return IsKeyEvent(eventType) && keyCode >= MyKeyCode.A && keyCode <= MyKeyCode.RightArrow;}// 悬浮事件--------------------------------------------------------------------------------------public static bool IsHorverEvent(MyEventType eventType){ // 是否是鼠标悬浮事件return eventType >= MyEventType.EnterHorver && eventType <= MyEventType.LeftHorver;}
}

        EventStatusUtils.cs

/** 事件状态切换工具类*/
public class EventStatusUtils
{public static MyEventType GetNextEvent(MyEventType eventType){ // 获取下一个事件if (EventTypeUtils.IsClickEvent(eventType)){return GetNextClickEvent(eventType);}if (EventTypeUtils.IsDragEvent(eventType)){return GetNextDragEvent(eventType);}if (EventTypeUtils.IsScrollEvent(eventType)){return GetNextScrollEvent(eventType);}return MyEventType.None;}public static MyEventType GetNextClickEvent(MyEventType eventType){ // 获取下一个点击事件if (EventTypeUtils.IsClickDownEvent(eventType)){switch(eventType) {case MyEventType.ClickDown:return MyEventType.Click;case MyEventType.CtrlClickDown:return MyEventType.CtrlClick;case MyEventType.AltClickDown:return MyEventType.AltClick;case MyEventType.RightClickDown:return MyEventType.RightClick;case MyEventType.CtrlRightClickDown:return MyEventType.CtrlRightClick;case MyEventType.AltRightClickDown:return MyEventType.AltRightClick;case MyEventType.MiddleClickDown:return MyEventType.MiddleClick;case MyEventType.CtrlMiddleClickDown:return MyEventType.CtrlMiddleClick;case MyEventType.AltMiddleClickDown:return MyEventType.AltMiddleClick;}}if (EventTypeUtils.IsClickingEvent(eventType)){switch(eventType) {case MyEventType.Click:return MyEventType.ClickUp;case MyEventType.CtrlClick:return MyEventType.CtrlClickUp;case MyEventType.AltClick:return MyEventType.AltClickUp;case MyEventType.RightClick:return MyEventType.RightClickUp;case MyEventType.CtrlRightClick:return MyEventType.CtrlRightClickUp;case MyEventType.AltRightClick:return MyEventType.AltRightClickUp;case MyEventType.MiddleClick:return MyEventType.MiddleClickUp;case MyEventType.CtrlMiddleClick:return MyEventType.CtrlMiddleClickUp;case MyEventType.AltMiddleClick:return MyEventType.AltMiddleClickUp;}}return MyEventType.None;}public static MyEventType GetNextDragEvent(MyEventType eventType){ // 获取下一个拖拽事件if (EventTypeUtils.IsBeginDragEvent(eventType)){switch(eventType) {case MyEventType.BeginDrag:return MyEventType.Drag;case MyEventType.BeginCtrlDrag:return MyEventType.CtrlDrag;case MyEventType.BeginAltDrag:return MyEventType.AltDrag;case MyEventType.BeginRightDrag:return MyEventType.RightDrag;case MyEventType.BeginCtrlRightDrag:return MyEventType.CtrlRightDrag;case MyEventType.BeginAltRightDrag:return MyEventType.AltRightDrag;case MyEventType.BeginMiddleDrag:return MyEventType.MiddleDrag;case MyEventType.BeginCtrlMiddleDrag:return MyEventType.CtrlMiddleDrag;case MyEventType.BeginAltMiddleDrag:return MyEventType.AltMiddleDrag;}}if (EventTypeUtils.IsDraggingEvent(eventType)){switch(eventType) {case MyEventType.Drag:return MyEventType.EndDrag;case MyEventType.CtrlDrag:return MyEventType.EndCtrlDrag;case MyEventType.AltDrag:return MyEventType.EndAltDrag;case MyEventType.RightDrag:return MyEventType.EndRightDrag;case MyEventType.CtrlRightDrag:return MyEventType.EndCtrlRightDrag;case MyEventType.AltRightDrag:return MyEventType.EndAltRightDrag;case MyEventType.MiddleDrag:return MyEventType.EndMiddleDrag;case MyEventType.CtrlMiddleDrag:return MyEventType.EndCtrlMiddleDrag;case MyEventType.AltMiddleDrag:return MyEventType.EndAltMiddleDrag;}}return MyEventType.None;}public static MyEventType GetNextScrollEvent(MyEventType eventType){ // 获取下一个滑轮事件switch(eventType) {case MyEventType.BeginScroll:return MyEventType.Scroll;case MyEventType.BeginCtrlScroll:return MyEventType.CtrlScroll;case MyEventType.BeginAltScroll:return MyEventType.AltScroll;case MyEventType.Scroll:return MyEventType.EndScroll;case MyEventType.CtrlScroll:return MyEventType.EndCtrlScroll;case MyEventType.AltScroll:return MyEventType.EndAltScroll;}return MyEventType.None;}
}

4 运行效果