该华容道游戏由VS2005制作,编程语言为C#。游戏规则是要将图中标记为0的块移动到最下方中间。方块用Button表示,通过鼠标拖曳来移动。
下面是制作方法简介:
1. 首先要建立一个Block类表示每一个方块。
华容道游戏中的方块有四种:正方形大块,正方形小块,长方形竖块,长方形横块。因此用一个BlockType枚举表示这四种块。枚举值One表示小正方形,TwoH表示横长方形,TwoV表示竖长方形,Four表示大正方形。
每一个方块的基本数据,除了方块的类型以外还有其左上角的坐标(坐标的概念参见编辑窗体截图),一旦确定方块类型和左上角的坐标后,就可以确定一个块了。左上角坐标用一个System.Drawing.Point类型的属性Location表示。
Block类的List<Point> GetPoints()方法返回一个该方块所占据的所有坐标位置的集合。通过方块类型和左上角的坐标就可以确定一个方块所占据的所有坐标位置。
Block类的bool IsValid()方法可以判定这个方块是否在游戏区域内,如果有任何部分出界了就返回false。这同样可以通过方块类型和左上角坐标判定。
Block类的bool Intersects(Block b)方法判定一个方块是否和另外一个方块有交叉部分。如果有交叉部分则返回true。通过获取两个块各自所占据的点,判定是否有交集就可以了。
另外在Block类中重写Equals方法和GetHashCode方法,方便以后使用。
附:Block类的所有代码:
——————Block.cs——————
using
System;
using
System.Collections.Generic;
using
System.Text;
using
System.Drawing;
namespace
HuaRongDao
{
///<summary>
///
块类型
///</summary>
public enum BlockType
{
///<summary>
/// 1*1
的块
///</summary>
One,
///<summary>
/// 2*1
的块
///</summary>
TwoH,
///<summary>
/// 1*2
的块
///</summary>
TwoV,
///<summary>
/// 2*2
的块
///</summary>
Four
}
///<summary>
///
块类
///</summary>
public class Block
{
///<summary>
///
块类型
///</summary>
public readonly BlockType BType;
///<summary>
///
块左上角位置
///</summary>
public Point Location;
///<summary>
///
创建一个块
///</summary>
///<param name="location">
左上角位置
</param>
///<param name="blockType">
块类型
</param>
public Block(Point location, BlockType blockType)
{
this.Location = location;
this.BType = blockType;
}
///<summary>
///
从给定块复制一个相同的块
///</summary>
///<param name="block">
给定要复制的块
</param>
public Block(Block block)
{
this.Location = block.Location;
this.BType = block.BType;
}
///<summary>
///
获取块中所有点
///</summary>
///<returns></returns>
public List<Point> GetPoints()
{
List<Point> pList = new List<Point>();
if (BType == BlockType.One)
{
pList.Add(Location);
}
else if (BType == BlockType.TwoH)
{
pList.Add(Location);
pList.Add(new Point(Location.X + 1, Location.Y));
}
else if (BType == BlockType.TwoV)
{
pList.Add(Location);
pList.Add(new Point(Location.X, Location.Y + 1));
}
else if (BType == BlockType.Four)
{
pList.Add(Location);
pList.Add(new Point(Location.X + 1, Location.Y));
pList.Add(new Point(Location.X, Location.Y + 1));
pList.Add(new Point(Location.X + 1, Location.Y + 1));
}
return pList;
}
///<summary>
///
块中是否包含某个点
///</summary>
///<param name="point">
点
</param>
///<returns>
是否包含
</returns>
public bool Contains(Point point)
{
return GetPoints().Contains(point);
}
///<summary>
///
是否和另一个块交叉
///</summary>
///<param name="block">
另一个块
</param>
///<returns></returns>
public bool Intersects(Block block)
{
List<Point> myPoints = this.GetPoints();
List<Point> otherPoints = block.GetPoints();
foreach (Point p in otherPoints)
{
if (myPoints.Contains(p)) { return true; }
}
return false;
}
///<summary>
///
块是否在界限内
///</summary>
public bool IsValid(int width, int height)
{
List<Point> points = GetPoints();
foreach (Point p in points)
{
if (p.X < 0 || p.X >= width || p.Y < 0 || p.Y >= height)
{
return false;
}
}
return true;
}
///<summary>
///
判断两个块是否有相同的位置和大小
///</summary>
///<param name="obj"></param>
///<returns></returns>
public override bool Equals(object obj)
{
if (obj == null || !(obj is Block)) { return false; }
Block block = (Block)obj;
return (Location == block.Location && BType == block.BType);
}
public override int GetHashCode()
{
return Location.GetHashCode() + BType.GetHashCode();
}
}
}
2. 建立游戏控制类Game
华容道通过移动方块游戏,首先定义一个移动方向枚举:
public
enum Direction { Up, Down, Left, Right }
Game类首先包含场地的宽度和高度,在华容道中宽度为4各,高度为5格:
public
const int Width = 4; public const int Height = 5;
Game类中包含一个块的集合,表示游戏中所有的方块:
public
List<Block> Blocks = new List<Block>();
其中下标为0的方块即表示要最终移出的块。
Game类中还有表示结束点(即要移出的方块左上角坐标最终要到达的位置)的属性:
private
Point finishPoint = new Point(1, 3);
Game类的AddBlock方法和RemoveBlock方法用于向集合中添加和移除方块,可用于编辑游戏:
AddBlock方法添加一个方块,要判断新添加的方块是否已经在集合中,是否在界内,以及是否和任何已在集合中的方块有交叉部分。都符合条件的才允许添加:
public
bool AddBlock(Block block)
{
if (Blocks.Contains(block)) { return false; }
if (!block.IsValid(Width,Height)) { return false; }
foreach (Block b in Blocks)
{
if (b.Intersects(block)) { return false; }
}
Blocks.Add(block);
return true;
}
RemoveBlock方法比较简单,判断一下是否存在该方块再从集合中移除即可。代码略。
Game类最重要的是移动方块的方法MoveBlock:
public bool MoveBlock(Block block, Direction direction)
{
if (!Blocks.Contains(block)) { throw new Exception("
非此游戏中的块!
"
); }
Point orgLocation = block.Location; //
记录原来位置
switch (direction) //
试移动
{
case Direction.Up:
block.Location.Y--;
break;
case Direction.Down:
block.Location.Y++;
break;
case Direction.Left:
block.Location.X--;
break;
case Direction.Right:
block.Location.X++;
break;
}
//
判断是否需要回滚
bool moveOK = true; //
可以移动
if (!block.IsValid(Width, Height)) { moveOK = false; }
else
{
foreach (Block b in Blocks)
{
if (b != block && block.Intersects(b))
{
moveOK = false;
break;
}
}
}
if (!moveOK) { block.Location = orgLocation; } //
回滚
return moveOK;
}
根据这段代码可以看出,MoveBlock所做的是:将要移动的方块先朝制定方向移动,然后判断该方块是否出界,是否与其他方块有交叉,如果是则再将其移回原位,否则保留移动后状态。
另外重载的
bool
MoveBlock(int blockId, Direction direction)则接受一个方块编号和方向作为参数,移动方块集合中下标为blockId的方块
最后Game类提供了GameWin方法,判断游戏是否胜利。至此Game类也基本完成。
3. WinForm部分
这无非是根据Game对象中的Blocks集合,在窗体加载时在一个容器控件中动态添加一些大小形状各异的Button作为方块的表示。比较难一点的是鼠标拖曳移动方块的过程(注:其中有一些BlockSize*2/3的部分可能不是很好理解,其实这是拖曳的距离达到一定时再移动方块,试过一下大概2/3的距离手感比较合适):
const
int BlockSize = 50; //
游戏中块的显示大小
Point
mouseDownPoint; //
鼠标按下的位置
bool
mouseDown; //
标记鼠标是否按下
void
btn_MouseMove(object sender, MouseEventArgs e)
{
if (!mouseDown) { return; }
Button btn = (Button)sender;
Block block = (Block)(btn.Tag);
int moveH = e.X - mouseDownPoint.X;
int moveV = e.Y - mouseDownPoint.Y;
if (moveH >= BlockSize * 2 / 3)
{
game.MoveBlock(block, Direction.Right);
}
else if (moveH <= -BlockSize * 2 / 3)
{
game.MoveBlock(block, Direction.Left);
}
else if (moveV >= BlockSize * 2 / 3)
{
game.MoveBlock(block, Direction.Down);
}
else if (moveV <= -BlockSize * 2 / 3)
{
game.MoveBlock(block, Direction.Up);
}
else { return; }
UpdateButtonLocation(btn);
if (game.GameWin())
{
MessageBox.Show("
游戏胜利!
"
);
}
}
void btn_MouseUp(object sender, MouseEventArgs e)
{
mouseDown = false;
}
void btn_MouseDown(object sender, MouseEventArgs e)
{
mouseDownPoint = e.Location;
mouseDown = true;
}
///<summary>
///
更新按钮位置(和其对应的块位置相同)
///</summary>
///<param name="btn">
要更新的按钮
</param>
private void UpdateButtonLocation(Button btn)
{
Block block = (Block)(btn.Tag);
btn.Left = block.Location.X * BlockSize;
btn.Top = block.Location.Y * BlockSize;
btn.Refresh();
}
至此华容道游戏基本制作就完毕了,当然单是这样似乎没多大意思,所以在之后还要尝试自动解题的功能。先看一下界面,游戏要求0号方块从中间走出来。
源代码请见http://www.itcast.net/course/play/9438
<<如果您想和我交流,请点击和我成为好友>>