网站的暑期开发一个月 基本啥都没干 捣鼓XNA 做了一个3D的连连看
缘由是强化班的暑期实践课题,选了连连看来做。MFC太古老, 直接上XNA。平面没新意,3D才好玩。哈哈~~
这个算是自己做的最大的也是最完整的一个软件了,斗胆发到首页,要拍砖的可以把砖送给我自己拍。。。
开发环境 Visual Studio 2008 + XNA Game Studio 3.1 (顺带 3DS Max 2010 和 Adobe的一些工具)
软件命名 Zodiac 因为是十二星座风格的
半成品软件可以去csdn下载 http://download.csdn.net/source/1495350
源代码点此下载 需要装XNA Game Studio 3.1额...
自认为做的还是很华丽丽的说~~~看看界面:
关于算法 平面的连连看算法没有仔细研究 直接想了下3D的算法
Code
/// <summary>
/// 连连看抽象单元定义
/// </summary>
public struct LLKBox
{
/// <summary>
/// 指示盒子是否被选中
/// </summary>
public bool IsSelected { get; set; }
/// <summary>
/// 表示盒子的贴图索引
/// </summary>
public int Index { get; set; }
/// <summary>
/// 指示盒子是否被建议
/// </summary>
public bool IsSuggested { get; set; }
/// <summary>
/// 盒子的世界坐标
/// </summary>
public Matrix ModelWorldTransform { get; set; }
/// <summary>
/// 盒子是否被删除
/// </summary>
public bool IsDeleted { get; set; }
平面和3D的规则是一样的:从一个方块按xy(z)方向不超过两个弯无障碍到达另外一个方块
整个大的连连看的定义是一个三维数组,不过由于个人喜好一般定义一维数组转换。假设一个连连看边长为N,即N*N*N大小,定义一维数组LLKBox[] boxes,长度为(N+2)*(N+2)*(N+2),这里声明的数组显示边长为N+2,可以理解为将整个连连看包裹在一个空心的方块中,以便于寻路操作。(不过也有不好的地方,就是大大的增大了数组长度。)
Code
#region Translation
/// <summary>
/// 将三维坐标换算为一维坐标
/// </summary>
/// <param name="x">x坐标</param>
/// <param name="y">y坐标</param>
/// <param name="z">z坐标</param>
/// <returns>对应的一维坐标</returns>
internal int ThreeToOne(int x, int y, int z)
{
return MatrixLength * MatrixLength * z + MatrixLength * y + x;
}
/// <summary>
/// 将一维坐标换算为三维坐标
/// </summary>
/// <param name="w">要换算的一味坐标</param>
/// <param name="x">换算后的x坐标</param>
/// <param name="y">换算后的y坐标</param>
/// <param name="z">换算后的z坐标</param>
internal void OneToThree(int w, out int x, out int y, out int z)
{
z = w / (MatrixLength * MatrixLength);
y = (w / MatrixLength) % MatrixLength;
x = w % MatrixLength;
}
连连看的算法基本可以分为两种:递归与非递归(貌似废话)。
简要介绍下递归思想:要使从box1到box2连通,那么从box1向xyz某一方向前进一格(暂且定义为box3),box3和box2必定要连通。如此将box1与box2连通的问题化为box3与box2连通的问题。按此递归直到box3=box1,递归完成。
采用递归方法寻路有些类似于深度优先搜索,在此使用递归虽然代码长度较短但性能不济,即使加上方向权重与自动剪枝,也不能在本质上解决性能问题,况且该方法不能解决找最短路径的问题,故在此舍弃使用。
非递归思想则是对两个方块在空间中的位置做分析,不同的情况使用不同的方法判断连通,并可以找到最短路径。
以下详细介绍非递归思想的算法。
两个方块在空间中的位置可以分为三种情况:
x(y z)轴方向上 |
X(Y Z)平行平面内 但不在一直线 |
空间中(不再一直线也不在一个面中) |
|
|
|
对三种情况分别作讨论
Code
/// <summary>
/// 计算两个盒子是否能够消去
/// </summary>
/// <param name="box1">盒子1的索引</param>
/// <param name="box2">盒子2的索引</param>
/// <returns>消去的盒子之间的路径 若无法消去则返回null</returns>
private List<int> Expurgate(int box1, int box2)
{
int x1, y1, z1, x2, y2, z2;
OneToThree(box1, out x1, out y1, out z1);
OneToThree(box2, out x2, out y2, out z2);
List<int> path;
List<int> path1;
List<int> path2;
List<int> path3;
bool d1 = true, d2 = true, d3 = true, d4 = true;
x(y z)轴方向上 (仅对x轴方向的情况说明,y z轴方向同x轴)
示意图:
首先从Box1(x1, y, z)直线遍历到Box2(x2, y, z) (图中黑色箭头表示)
若之间都为空,则两者连通且最短路即为两者的直接连线,无拐点,若不连通,进入下一个判断。
将box1,box2按照+y –y +z -z四个方向开始平移。如朝+y方向分别平移一格得到box1’(x1, y+1, z) box2’(x2, y+1, z),依次判断box1’ box2’是否为空,box1’与box1是否连通,box2’与box2是否连通,box1’与box2’是否连通,若全部满足,则已找到一条最短通路,两个拐点,若都不满足,则无通路。判断结束
Code
//In Line X
if (x1 != x2 && y1 == y2 && z1 == z2)
{
//Check If In One Line
if ((path = CheckInLineX(y1, z1, x1, x2)) != null)
return MergeOne(path, box1, box2);
//Check If In One Face
for (int i = 1; i < MatrixLength; i++)
{
if (d1 && y1 + i < MatrixLength && boxes[ThreeToOne(x1, y1 + i, z1)].IsDeleted && boxes[ThreeToOne(x2, y1 + i, z1)].IsDeleted)
if ((path1 = CheckInLineX(y1 + i, z1, x1, x2)) != null)
if ((path2 = CheckInLineY(x1, z1, y1, y1 + i)) != null)
if ((path3 = CheckInLineY(x2, z1, y1, y1 + i)) != null)
return MergeThree(path1, path2, path3, ThreeToOne(x1, y1 + i, z1), ThreeToOne(x2, y1 + i, z1), box1, box2);
else
d1 = false;
else
d1 = false;
if (d2 && y1 - i >= 0 && boxes[ThreeToOne(x1, y1 - i, z1)].IsDeleted && boxes[ThreeToOne(x2, y1 - i, z1)].IsDeleted)
if ((path1 = CheckInLineX(y1 - i, z1, x1, x2)) != null)
if ((path2 = CheckInLineY(x1, z1, y1, y1 - i)) != null)
if ((path3 = CheckInLineY(x2, z1, y1, y1 - i)) != null)
return MergeThree(path1, path2, path3, ThreeToOne(x1, y1 - i, z1), ThreeToOne(x2, y1 - i, z1), box1, box2);
else
d2 = false;
else
d2 = false;
if (d3 && z1 + i < MatrixLength && boxes[ThreeToOne(x1, y1, z1 + i)].IsDeleted && boxes[ThreeToOne(x2, y1, z1 + i)].IsDeleted)
if ((path1 = CheckInLineX(y1, z1 + i, x1, x2)) != null)
if ((path2 = CheckInLineZ(x1, y1, z1, z1 + i)) != null)
if ((path3 = CheckInLineZ(x2, y1, z1, z1 + i)) != null)
return MergeThree(path1, path2, path3, ThreeToOne(x1, y1, z1 + i), ThreeToOne(x2, y1, z1 + i), box1, box2);
else
d3 = false;
else
d3 = false;
if (d4 && z1 - i >= 0 && boxes[ThreeToOne(x1, y1, z1 - i)].IsDeleted && boxes[ThreeToOne(x2, y1, z1 - i)].IsDeleted)
if ((path1 = CheckInLineX(y1, z1 - i, x1, x2)) != null)
if ((path2 = CheckInLineZ(x1, y1, z1, z1 - i)) != null)
if ((path3 = CheckInLineZ(x2, y1, z1, z1 - i)) != null)
return MergeThree(path1, path2, path3, ThreeToOne(x1, y1, z1 - i), ThreeToOne(x2, y1, z1 - i), box1, box2);
else
d4 = false;
else
d4 = false;
}
return null;
X(Y Z)平行平面内 但不在一直线 (仅对X平行平面的情况说明,Y Z平面同X平面)
示意图1:
计算得到Box1(x, y1, z1) Box2(x, y2, z2)所在矩形的另外两个顶点Box3(x, y1, z2) Box4(x, y2, z1),依次判断这两条路是否连通。若有一条路连通则找到最短路,一个拐点,若无,进入下个判断。
示意图2:
将某一条红色线段朝矩形内平移(分别平移y方向与z方向),分别计算两个中间节点是否为空与3条路是否连通,若都满足则找到一条通路且找到最短路,两个拐点,若无,进入下一个判断
示意图3:
将某一条红色线段朝举行外平移(+y –y +z -z方向分别平移),分别计算两个中间节点是否为空与3条路是否连通,若都满足则找到一条通路且找到最短路,两个拐点,若无,则两个方块之间无通路。判断结束。
Code
//In Face X
if (x1 == x2 && y1 != y2 && z1 != z2)
{
if (boxes[ThreeToOne(x1, y1, z2)].IsDeleted)
if ((path1 = CheckInLineY(x1, z2, y1, y2)) != null)
if ((path2 = CheckInLineZ(x1, y1, z1, z2)) != null)
return MergeTwo(path1, path2, ThreeToOne(x1, y1, z2), box1, box2);
if (boxes[ThreeToOne(x1, y2, z1)].IsDeleted)
if ((path1 = CheckInLineY(x1, z1, y1, y2)) != null)
if ((path2 = CheckInLineZ(x1, y2, z1, z2)) != null)
return MergeTwo(path1, path2, ThreeToOne(x1, y2, z1), box1, box2);
int minY = y1 < y2 ? y1 : y2;
int maxY = y1 < y2 ? y2 : y1;
int minZ = z1 < z2 ? z1 : z2;
int maxZ = z1 < z2 ? z2 : z1;
for (int i = minY + 1; i < maxY; i++)
if (boxes[ThreeToOne(x1, i, z1)].IsDeleted && boxes[ThreeToOne(x1, i, z2)].IsDeleted)
if ((path1 = CheckInLineZ(x1, i, z1, z2)) != null)
if ((path2 = CheckInLineY(x1, z1, y1, i)) != null)
if ((path3 = CheckInLineY(x1, z2, i, y2)) != null)
return MergeThree(path1, path2, path3, ThreeToOne(x1, i, z1), ThreeToOne(x1, i, z2), box1, box2);
for (int i = minZ + 1; i < maxZ; i++)
if (boxes[ThreeToOne(x1, y1, i)].IsDeleted && boxes[ThreeToOne(x1, y2, i)].IsDeleted)
if ((path1 = CheckInLineY(x1, i, y1, y2)) != null)
if ((path2 = CheckInLineZ(x1, y1, z1, i)) != null)
if ((path3 = CheckInLineZ(x1, y2, i, z2)) != null)
return MergeThree(path1, path2, path3, ThreeToOne(x1, y1, i), ThreeToOne(x1, y2, i), box1, box2);
for (int i = 1; i < MatrixLength; i++)
{
if (d1 && maxY + i < MatrixLength && boxes[ThreeToOne(x1, maxY + i, z1)].IsDeleted && boxes[ThreeToOne(x2, maxY + i, z2)].IsDeleted)
if ((path1 = CheckInLineZ(x1, maxY + i, z1, z2)) != null)
if ((path2 = CheckInLineY(x1, z1, y1, maxY + i)) != null)
if ((path3 = CheckInLineY(x1, z2, y2, maxY + i)) != null)
return MergeThree(path1, path2, path3, ThreeToOne(x1, maxY + i, z1), ThreeToOne(x1, maxY + i, z2), box1, box2);
else
d1 = false;
else
d1 = false;
if (d2 && minY - i >= 0 && boxes[ThreeToOne(x1, minY - i, z1)].IsDeleted && boxes[ThreeToOne(x2, minY - i, z2)].IsDeleted)
if ((path1 = CheckInLineZ(x1, minY - i, z1, z2)) != null)
if ((path2 = CheckInLineY(x1, z1, y1, minY - i)) != null)
if ((path3 = CheckInLineY(x1, z2, y2, minY - i)) != null)
return MergeThree(path1, path2, path3, ThreeToOne(x1, minY - i, z1), ThreeToOne(x1, minY - i, z2), box1, box2);
else
d2 = false;
else
d2 = false;
if (d3 && maxZ + i < MatrixLength && boxes[ThreeToOne(x1, y1, maxZ + i)].IsDeleted && boxes[ThreeToOne(x1, y2, maxZ + i)].IsDeleted)
if ((path1 = CheckInLineY(x1, maxZ + i, y1, y2)) != null)
if ((path2 = CheckInLineZ(x1, y1, z1, maxZ + i)) != null)
if ((path3 = CheckInLineZ(x1, y2, z2, maxZ + i)) != null)
return MergeThree(path1, path2, path3, ThreeToOne(x1, y1, maxZ + i), ThreeToOne(x1, y2, maxZ + i), box1, box2);
else
d3 = false;
else
d3 = false;
if (d4 && minZ - i >= 0 && boxes[ThreeToOne(x1, y1, minZ - i)].IsDeleted && boxes[ThreeToOne(x2, y1, minZ - i)].IsDeleted)
if ((path1 = CheckInLineY(x1, minZ - i, y1, y2)) != null)
if ((path2 = CheckInLineZ(x1, y1, z1, minZ - i)) != null)
if ((path3 = CheckInLineZ(x1, y2, z2, minZ - i)) != null)
return MergeThree(path1, path2, path3, ThreeToOne(x1, y1, minZ - i), ThreeToOne(x2, y1, minZ - i), box1, box2);
else
d4 = false;
else
d4 = false;
}
return null;
空间中(不再一直线也不在一个面中)
此种情况其实比较简单 判断方便。直接计算出Box1和Box2作为对角线定点的长方体的各个顶点和各条边,看能否找到一条通路即可。
Code
//In 3D
if (x1 != x2 && y1 != y2 && z1 != z2)
{
List<int>[] l = new List<int>[12];
if (boxes[ThreeToOne(x1, y1, z2)].IsDeleted)
l[0] = CheckInLineZ(x1, y1, z1, z2);
else
l[0] = null;
if (boxes[ThreeToOne(x1, y2, z1)].IsDeleted)
l[1] = CheckInLineY(x1, z1, y1, y2);
else
l[1] = null;
if (boxes[ThreeToOne(x2, y1, z1)].IsDeleted)
l[2] = CheckInLineX(y1, z1, x1, x2);
else
l[2] = null;
if (boxes[ThreeToOne(x2, y1, z2)].IsDeleted)
l[3] = CheckInLineY(x2, z2, y1, y2);
else
l[3] = null;
if (boxes[ThreeToOne(x2, y2, z1)].IsDeleted)
l[4] = CheckInLineZ(x2, y2, z1, z2);
else
l[4] = null;
if (boxes[ThreeToOne(x1, y2, z2)].IsDeleted)
l[5] = CheckInLineX(y2, z2, x1, x2);
else
l[5] = null;
if (l[0] != null && l[3] != null)
if ((l[6] = CheckInLineX(y1, z2, x1, x2)) != null)
return MergeThree(l[0], l[3], l[6], ThreeToOne(x1, y1, z2), ThreeToOne(x2, y1, z2), box1, box2);
if (l[2] != null && l[3] != null)
if ((l[7] = CheckInLineZ(x2, y1, z1, z2)) != null)
return MergeThree(l[2], l[3], l[7], ThreeToOne(x2, y1, z1), ThreeToOne(x2, y1, z2), box1, box2);
if (l[2] != null && l[4] != null)
if ((l[8] = CheckInLineY(x2, z1, y1, y2)) != null)
return MergeThree(l[2], l[4], l[8], ThreeToOne(x2, y1, z1), ThreeToOne(x2, y2, z1), box1, box2);
if (l[1] != null && l[4] != null)
if ((l[9] = CheckInLineX(y2, z1, x1, x2)) != null)
return MergeThree(l[1], l[4], l[9], ThreeToOne(x1, y2, z1), ThreeToOne(x2, y2, z1), box1, box2);
if (l[1] != null && l[5] != null)
if ((l[10] = CheckInLineZ(x1, y2, z1, z2)) != null)
return MergeThree(l[1], l[5], l[10], ThreeToOne(x1, y2, z1), ThreeToOne(x1, y2, z2), box1, box2);
if (l[0] != null && l[5] != null)
if ((l[11] = CheckInLineY(x1, z2, y1, y2)) != null)
return MergeThree(l[0], l[5], l[11], ThreeToOne(x1, y1, z2), ThreeToOne(x1, y2, z2), box1, box2);
最后 Over~~~
return null;
}
稍稍计算时间复杂度,控制在了平方范围内…还好啊…就是代码长度有点长…比较郁闷…
还有好多功能没有开发出来,比如提示之类的。提示是一件比较麻烦的事情,有空还得再想想…