已经过了大半年了,当初我想实现的魔方游戏自己复原也已经实现了,便对与魔方的状态表示当时也是一知半解,对之前的魔方状态法是不满意的,前几天有了思路就推导了出来, 不过没有之前踩的坑,也不会有现在的结论。
这个魔方状态表示法,不以整体为推导法,而是以魔方块为导论,很早之前我就知道了一个结论,一个魔方块有24种状态(三阶魔方),但是不知道为何不知,现在大差不差,就以魔方块而言,有棱块与角块:
棱块:2种状态(0或1),12个可移动位置
角块:3种状态(0或1或2),8个可移动位置
中心块状态没太大意义。
三阶状态读取以实现,Demo源码请看:GitHub - 10555gff/mofanCubeStata
注:此结论可任意选择F面,得到的读取结果同样适用,也可用于2-多阶魔方的状态读取
注要看MofanRestore这文件,主要用到的代码是这一段
using System;
using UnityEngine;
public class MofanRestore : MonoBehaviour
{
public static MofanRestore Instance = null;
//当前读取的魔方块
Transform curCubetransform;
int curMoIndex;
string str;
string mofanColor;
private void Start()
{
MofanRestore.Instance = this;
}
public string MofanStata(string mofanName)
{
int n= int.Parse(mofanName.Substring(1)) - 1;
curCubetransform = GameManager.Instance.cubePrefabList[n].transform;
mofanColor = mofanReColor(mofanName);
//区别棱块与角块
switch (mofanColor.Length)
{
case 2:
curMoIndex = mofancubeStat();
return string.Format("{0}{1}{2}{3}", str[0], mofanColor[curMoIndex], str[1], mofanColor[curMoIndex ^ 1]);
case 3:
string tmpStr = swapStr();
return string.Format("{0}{1}{2}{3}{4}{5}", str[0], mofanColor[curMoIndex], str[1], tmpStr[0], str[2], tmpStr[1]);
}
return null;
}
//点乘判断,用得1或-1,返回0
int reAbs(Vector3 v1, Vector3 v2)
{
return (int)Mathf.Abs(Vector3.Dot(v1, v2)) ^ 1;
}
#region 棱块判断块
//用点乘判断是棱块状态
int mofancubeStat()
{
Vector3 v3 = reInitVector();
Vector3 v3fu = isCenter() ? this.transform.forward : this.transform.up;
return reAbs(v3, v3fu);
}
//中间棱块与其它棱块分别定义初始轴
Vector3 reInitVector()
{
switch (curCubetransform.name)
{
//中间层定义初始轴方向
case "M20":
case "M2":
case "M26":
case "M8":
return curCubetransform.forward;
//其它层定义初始轴方向
default:
return curCubetransform.up;
}
}
//棱块的位置,是位置!!! 是否在中间层
bool isCenter()
{
str = pType(curCubetransform.position.ToString());
if (str[0] != 'F' && str[0] != 'B') return false;
return true;
}
#endregion
#region 角块判断块
//用点乘判断是角块状态
int mofancubeSamStat()
{
Vector3 v1 = curCubetransform.up;
Vector3 v2 = curCubetransform.forward;
Vector3 v3 = curCubetransform.right;
Vector3 vtup = this.transform.up;
//依次对比
if (reAbs(v1, vtup) == 0)
{
//未旋转
return 0;
}else if (reAbs(v2, vtup) == 0)
{
//顺时钟旋转
return 1;
}
else if (reAbs(v3, vtup) == 0)
{
//逆时钟旋转
return 2;
}
return -1;
}
string swapStr()
{
str = pType(curCubetransform.position.ToString());
curMoIndex = mofancubeSamStat();
char a = mofanColor[(curMoIndex + 1) % 3];
char b = mofanColor[(curMoIndex + 2) % 3];
//判断某个角块所处位置,同一个角块,不同位置有着两种不同状态
switch (str)
{
case "UFR":
case "DFL":
case "DBR":
case "UBL":
return isEn() ? $"{a}{b}" : $"{b}{a}";
case "DFR":
case "UFL":
case "UBR":
case "DBL":
return isEn() ? $"{b}{a}" : $"{a}{b}";
}
return " ";
}
//判断相反,旁边全是敌人,对面才是友军
bool isEn()
{
switch (curCubetransform.name) {
case "M21":
case "M9":
case "M1":
case "M25":
return true;
case "M3":
case "M27":
case "M19":
case "M7":
return false;
}
return false;
}
#endregion
//总20个位置,位置转成可读字符编号
public string pType(string s)
{
switch (s)
{
#region 棱块
//顶棱
case "(0.0, 1.0, -1.0)":
return "UF";
case "(1.0, 1.0, 0.0)":
return "UR";
case "(0.0, 1.0, 1.0)":
return "UB";
case "(-1.0, 1.0, 0.0)":
return "UL";
//底棱
case "(0.0, -1.0, -1.0)":
return "DF";
case "(1.0, -1.0, 0.0)":
return "DR";
case "(0.0, -1.0, 1.0)":
return "DB";
case "(-1.0, -1.0, 0.0)":
return "DL";
//前侧棱
case "(1.0, 0.0, -1.0)":
return "FR";
case "(-1.0, 0.0, -1.0)":
return "FL";
//后侧棱
case "(1.0, 0.0, 1.0)":
return "BR";
case "(-1.0, 0.0, 1.0)":
return "BL";
#endregion
#region 角块
//顶角块
case "(1.0, 1.0, -1.0)":
return "UFR";
case "(-1.0, 1.0, -1.0)":
return "UFL";
case "(1.0, 1.0, 1.0)":
return "UBR";
case "(-1.0, 1.0, 1.0)":
return "UBL";
//底角块
case "(1.0, -1.0, -1.0)":
return "DFR";
case "(-1.0, -1.0, -1.0)":
return "DFL";
case "(1.0, -1.0, 1.0)":
return "DBR";
case "(-1.0, -1.0, 1.0)":
return "DBL";
#endregion
default:
return "";
}
}
//魔方块编号转对应颜色
public string mofanReColor(string mofanName)
{
switch (mofanName)
{
#region 中心块
case "M11":
return "绿";
case "M15":
return "黄";
case "M5":
return "红";
case "M23":
return "橙";
case "M17":
return "蓝";
case "M13":
return "白";
#endregion
#region 角块
case "M21":
return "黄绿橙";
case "M3":
return "黄绿红";
case "M27":
return "黄蓝橙";
case "M9":
return "黄蓝红";
case "M19":
return "白绿橙";
case "M1":
return "白绿红";
case "M25":
return "白蓝橙";
case "M7":
return "白蓝红";
#endregion
#region 棱块
case "M20":
return "绿橙";
case "M2":
return "绿红";
case "M26":
return "蓝橙";
case "M8":
return "蓝红";
case "M12":
return "黄绿";
case "M24":
return "黄橙";
case "M6":
return "黄红";
case "M18":
return "黄蓝";
case "M10":
return "白绿";
case "M22":
return "白橙";
case "M4":
return "白红";
case "M16":
return "白蓝";
#endregion
}
return "";
}
internal string readMofanOrder(string v)
{
throw new NotImplementedException();
}
internal string readMofanFixed(string v, bool isHaveColour)
{
throw new NotImplementedException();
}
}
现在开始推:
1、建立魔方基准
给两个已复原一模一样的魔方,如何确认这两个魔方的状态相同,即两个魔方可以重叠在一起,这样就要确保两个魔方的方位是相同的,如何保证方位是相同,那就基准,如果基准是相同的,那么两个魔方状态相同,这里用到的是魔方的F面与U面为基准,若两个魔方的F面和U面方位相同,则两个魔方状态相同。
同理,要想表示魔方的状态,必然要建立一个基准,则以魔方为原点坐标,魔方的绿色面为F面,魔方的黄色面为U面为基准。
当基准建立好后,还要确立魔方块的读取符号的先后顺序,类型于运算符的优先级,这个也很重要,则以 U/D 与 F/B 与 R/L (从左到右)依次降级。
2、已复原魔方状态表示
按照上面的基准,则可表示为:
F U L R B D
绿 黄 红 橙 蓝 白
---------------------------------------------------------------------------------------------------------------------------------
棱块: 对应的符号:
绿橙、绿红、蓝橙、蓝红 FR、FL、BR、BL
黄绿、黄橙、黄红、黄蓝 UF、UR、UL、UB
白绿、白橙、白红、白蓝 DF、DR、DL、DB
---------------------------------------------------------------------------------------------------------------------------------
角块: 对应的符号:
黄绿橙、黄绿红、黄蓝橙、黄蓝红 UFR、UFL、UBR、UBL
白绿橙、白绿红、白蓝橙、白蓝红 DFR、DFL、DBR、DBL
3、单个魔方块的状态表示
当上述的基准完成后,就可以尝试着去表示魔方块的状态了。
一个棱块有2种状态,一个角块有3种状态,那以绿红棱块为例去推导,在绿红棱块在已复原的位置,可以出现两种状态,一种是绿红(对应上面)、另一种是红绿,设绿红为状态0,那么红绿就是状态1,经过推导,得出以下结论:
当棱块在中间层时------》向F或B面开枪,击中棱块的某种颜色
当棱块在顶或底层时------》向U或D面开枪,击中棱块的某种颜色
这棱块的某种颜色是同一种颜色。
举例:
绿橙棱块,击中绿色为状态0,橙色为状态1
F绿L橙棱块,在F面开枪,则击中绿色,状态则为0
F橙L绿棱块,在F面开枪,则击中橙色,状态则为1
U绿F橙棱块,在U面开枪,则击中绿色,状态则为0
U橙F绿棱块,在U面开枪,则击中橙色,状态则为1
即绿橙为状态0,橙绿为状态1。
这个时候可能就有人说了,什么就状态0和状态1了,有没有科学根据的。
不过这个确实有,它所确认状态依靠的是之前建立的基准,就像是二维坐标有X轴与Y轴,就可以表示一个点,这里类似如此,还是以绿橙棱块为例,它的绿色面作法向量,当绿橙棱块在中间层时,让它的法向量与F面法向量作比较,若平行则为状态0,垂直相交则为状态1;当绿橙棱块在顶或底层时,让它的法向量与U面法向量作比较,若平行则为状态0,否则为状态1(垂直)。
用的就是它的相对方向的关系,有点局部坐标的意思。
---------------------------------------------------------------------------------------------------------------------------------
当角块在顶或底层时------》向U或D面开枪,击中棱块的某种颜色
这棱块的某种颜色对应着魔方块的某种状态
举例:
黄绿橙角块,击中黄色为状态0,绿色为状态1,橙色为状态2
---------------------------------------------------------------------------------------------------------------------------------
4、结论的使用
随机抽取一个棱块黄红色,假设这个魔方是游戏引擎创建出来的,若知道这个黄红色魔方块在UF位置,该如何读取此魔方块的状态。
前面第二节可知,黄红对应状态为0,红黄状态为1,再用第三个点,当棱块在顶或底层时------》向U或D面开枪,击中棱块的某种颜色,在U面开枪击中黄色,则状态为0,它的黄色面法向量与U面法向量平行。
位置是UF,状态为0,状态为0对应则为黄红,则魔方块状态为U黄F红。
---------------------------------------------------------------------------------------------------------------------------------
再抽取一个棱块绿红色:
前面第二节可知,绿红对应状态为0,红绿状态为1,击中颜色为红色,读出状态为1,在位置处于FR,状态为1,则对应红绿,则魔方块状态为F红R绿。
这个方法可以在确认基准后,将魔方的状态转换为位置加一个状态数,就可以表示魔方块的状态,很简明的读取到魔方的状态信息;基准也很容易换,只要将F/B与U/D与L/R对应好颜色即可。