首先,新建一个RuleManager脚本,写下各类棋子的走棋规则,先贴上脚本
public class RuleManager : MonoBehaviour { /// <summary> /// 将的走棋规则 /// </summary> /// <returns></returns> public static bool moveJiang(int selectedId, int row, int col, int destoryId) { /* * 1.首先目标位置在九宫格内 * 2.移动的步长是一个格子 * 3.将和帅不准在同一直线上直接对面(中间无棋子),如一方已先占据位置,则另一方必须回避,否则就算输了 */ if (destoryId != -1 && StoneManager.s[destoryId]._type == StoneManager.Stone.TYPE.JIANG) return moveChe(selectedId, row, col, destoryId); if (col < 3 || col > 5) return false; if (ToolManager.IsBottomSide(selectedId)) { if (row < 7) return false; } else { if (row > 2) return false; } int row1 = ToolManager.yToRow(StoneManager.s[selectedId]._y); int col1 = ToolManager.xToCol(StoneManager.s[selectedId]._x); int d = ToolManager.Relation(row1, col1, row, col); if (d != 1 && d != 10) return false; return true; } /// <summary> /// 士的走棋规则 /// </summary> /// <returns></returns> public static bool moveShi(int selectedId, int row, int col, int destoryId) { /* * 1.目标位置在九宫格内 * 2.只许沿着九宫中的斜线行走一步(方格的对角线) */ if (ToolManager.IsBottomSide(selectedId)) { if (row < 7) return false; } else { if (row > 2) return false; } if (col < 3 || col > 5) return false; int row1 = ToolManager.yToRow(StoneManager.s[selectedId]._y); int col1 = ToolManager.xToCol(StoneManager.s[selectedId]._x); int d = ToolManager.Relation(row1, col1, row, col); if (d != 11) return false; return true; } /// <summary> /// 相的走棋规则 /// </summary> /// <returns></returns> public static bool moveXiang(int selectedId, int row, int col, int destoryId) { /* * 1.目标位置不能越过河界走入对方的领地 * 2.只能斜走(两步),可以使用汉字中的田字形象地表述:田字格的对角线,即俗称象(相)走田字 * 3.当象(相)行走的路线中,及田字中心有棋子时(无论己方或者是对方的棋子),则不允许走过去,俗称:塞象(相)眼。 */ int row1 = ToolManager.yToRow(StoneManager.s[selectedId]._y); int col1 = ToolManager.xToCol(StoneManager.s[selectedId]._x); int d = ToolManager.Relation(row1, col1, row, col); if (d != 22) return false; int rEye = (row + row1) / 2; int cEye = (col + col1) / 2; if (ToolManager.GetStoneId(rEye, cEye) != -1) return false; if (ToolManager.IsBottomSide(selectedId)) { if (row < 4) return false; } else { if (row > 5) return false; } return true; } /// <summary> /// 车的走棋规则 /// </summary> /// <returns></returns> public static bool moveChe(int selectedId, int row, int col, int destoryId) { /* * 1.每行一步棋可以上、下直线行走(进、退);左、右横走 * 2.中间不能隔棋子 * 3.行棋步数不限 */ int row1 = ToolManager.yToRow(StoneManager.s[selectedId]._y); int col1 = ToolManager.xToCol(StoneManager.s[selectedId]._x); int ret = ToolManager.GetStoneCountAtLine(row1, col1, row, col); if (ret == 0) return true; return false; } /// <summary> /// 马的走棋规则 /// </summary> /// <returns></returns> public static bool moveMa(int selectedId, int row, int col, int destoryId) { /* * 1.马走日字(斜对角线) * 2.可以将马走日分解为:先一步直走(或一横)再一步斜走 * 3.如果在要去的方向,第一步直行处(或者横行)有别的棋子挡住,则不许走过去(俗称:蹩马腿) */ int row1 = ToolManager.yToRow(StoneManager.s[selectedId]._y); int col1 = ToolManager.xToCol(StoneManager.s[selectedId]._x); int d = ToolManager.Relation(row1, col1, row, col); if (d != 12 && d != 21) return false; if (d == 12) { if (ToolManager.GetStoneId(row1, (col + col1) / 2) != -1) return false; } else { if (ToolManager.GetStoneId((row + row1) / 2, col1) != -1) return false; } return true; } /// <summary> /// 炮的走棋规则 /// </summary> /// <returns></returns> public static bool movePao(int selectedId, int row, int col, int destoryId) { /* * 1.此棋的行棋规则和车(車)类似,横平、竖直,只要前方没有棋子的地方都能行走 * 2.但是,它的吃棋规则很特别,必须跳过一个棋子(无论是己方的还是对方的)去吃掉对方的一个棋子。俗称:隔山打炮 */ int row1 = ToolManager.yToRow(StoneManager.s[selectedId]._y); int col1 = ToolManager.xToCol(StoneManager.s[selectedId]._x); int ret = ToolManager.GetStoneCountAtLine(row1, col1, row, col); if (destoryId != -1) { if (ret == 1) return true; } else { if (ret == 0) return true; } return false; } /// <summary> /// 兵的走棋规则 /// </summary> /// <returns></returns> public static bool moveBing(int selectedId, int row, int col, int destoryId) { /* * 1.在没有过河界前,此棋每走一步棋只许向前直走一步(不能后退) * 2.过了河界之后,每行一步棋可以向前直走,或者横走(左、右)一步,但也是不能后退的 */ int row1 = ToolManager.yToRow(StoneManager.s[selectedId]._y); int col1 = ToolManager.xToCol(StoneManager.s[selectedId]._x); int d = ToolManager.Relation(row1, col1, row, col); if (d != 1 && d != 10) return false; if (ToolManager.IsBottomSide(selectedId)) { if (row > row1) return false; if (row1 >= 5 && row == row1) return false; } else { if (row < row1) return false; if (row1 <= 4 && row == row1) return false; } return true; } }
先说明下传入的四个参数
selectedId是当前选择的棋子的ID,col和row分别是棋子要移动到的目标位置的行列坐标,destoryId是当前选择的棋子所可能会吃掉的棋子的ID,即目标位置上的棋子,若目标位置上没有棋子则destoryId为-1;
拿“将”的规则举个例子:
1.若将帅之间没有隔着棋子,则可以像“车”一样直接过去杀死对方的将;
2.将只能待在九宫格内,故他所移动的目标位置只能在第3列与第5列之间;若将在下方,则不能小于第7行,若在上放,则不能大于第2行(列:0-8;行:0-9);
3.因为他只能上下左右移动一个格子,设他左右移动dr,上下移动dc,则可以设d=dr*10+dc;那么将所移动的d只能是10或者1;
另外,在象棋中还有“拐马脚”、“塞象眼,可以通过GetStoneId求“马脚”、“象眼”处是否有棋子;而车和炮的直行,可以通过GetStoneCountAtLine求两个位置之间有多少个棋子,若车或炮与目标位置之间没有棋子,则可以直行,特别的,若目标位置有一个棋子,并且炮到这个棋子之间的位置上刚好有一个棋子,那么就可以“隔山打炮”了
再附上ToolsManager的代码
public class ToolManager : MonoBehaviour { /// <summary> /// 工具类:将坐标x的值转换为所在的列数 /// </summary> /// <param name="x"></param> /// <returns></returns> public static int xToCol(float x) { int col = (int)(x / 0.51f); col = col + 4; return col; } /// <summary> /// 工具类:将坐标y的值转换为所在的行数 /// </summary> /// <param name="y"></param> /// <returns></returns> public static int yToRow(float y) { int row; if (y > 0) { row=(int)(y/0.51f); row = 4 - row; } else { row = (int)(y / 0.51f); row = 5 - row; } return row; } /// <summary> /// 工具类:将所在的列数转换为对应的x坐标 /// </summary> /// <param name="col"></param> /// <returns></returns> public static float colToX(int col) { float x; col = col - 4; x = col * 0.51f; return x; } /// <summary> /// 工具类:将所在的行数转换为对应的y坐标(因浮点数计算存在不精确问题,故先乘100,计算后再除100) /// </summary> /// <param name="row"></param> /// <returns></returns> public static float rowToY(int row) { float y; if (row < 5) { row = 4 - row; y =(float) (row * 51 + 21); y = y / 100; } else { row = 5 - row; y = (float)(row * 51 - 31); y = y / 100; } return y; } /// <summary> /// 计算选中的棋子的位置和要移动的位置之间的位置关系 /// </summary> /// <param name="row1"></param> /// <param name="col1"></param> /// <param name="row"></param> /// <param name="col"></param> /// <returns></returns> public static int Relation(int row1, int col1, int row, int col) { return Mathf.Abs(row1 - row) * 10 + Mathf.Abs(col1 - col); } /// <summary> /// 工具类:通过行列数来判断该位置上是否有棋子,若有则返回棋子的ID,若没有则返回-1 /// </summary> /// <param name="rEye"></param> /// <param name="cEye"></param> /// <returns></returns> public static int GetStoneId(int rEye, int cEye) { float x = ToolManager.colToX(cEye); float y = ToolManager.rowToY(rEye); for (int i = 0; i < 32; ++i) { if (x == StoneManager.s[i]._x && y == StoneManager.s[i]._y && StoneManager.s[i]._dead == false) return i; } return -1; } /// <summary> /// 工具类:通过x、y的坐标来判断该位置上是否有棋子,若有则返回棋子的ID,若没有则返回-1 /// </summary> /// <param name="rEye"></param> /// <param name="cEye"></param> /// <returns></returns> public static int GetStoneId(float x, float y) { for (int i = 0; i < 32; ++i) { if (x == StoneManager.s[i]._x && y == StoneManager.s[i]._y && StoneManager.s[i]._dead == false) return i; } return -1; } /// <summary> /// 工具类:判断要移动的棋子是否在棋盘的下方 /// </summary> /// <param name="selectedId"></param> /// <returns></returns> public static bool IsBottomSide(int selectedId) { if (StoneManager.s[selectedId]._initY < 0) { return true; } else { return false; } } /// <summary> /// 工具类:判断两个位置所连成的一条直线上有多少个棋子 /// </summary> /// <param name="row1"></param> /// <param name="col1"></param> /// <param name="row"></param> /// <param name="col"></param> /// <returns></returns> public static int GetStoneCountAtLine(int row1, int col1, int row2, int col2) { int ret = 0; if (row1 != row2 && col1 != col2) return -1; if (row1 == row2 && col1 == col2) return -1; if (row1 == row2) { int min = col1 < col2 ? col1 : col2; int max = col1 < col2 ? col2 : col1; for (int col = min + 1; col < max; ++col) { if (GetStoneId(row1, col) != -1) ++ret; } } else { int min = row1 < row2 ? row1 : row2; int max = row1 < row2 ? row2 : row1; for (int row = min + 1; row < max; ++row) { if (GetStoneId(row, col1) != -1) ++ret; } } return ret; } }
为了代码简洁方便使用,于是用了这样的代码结构,对于内存方面缺乏研究,故不知道如此使用是否占用过多内存,个人认为代码的简洁易懂应该已经做得不错了,但在内存优化方面还有所欠缺
