这一节分析下具体实体类的定义和实现,下面是这个游戏的实体类结构:
游戏物体的运动就是不断创建画面和销毁画面的过程,原理和播放录象带一样。所以我定义了一个基类,里面定义了子类共同的成员,由于方法具体实现都一样,所以在基类里的Draw和Crear方法并没有定义为抽象方法,反而具体实现了它们!子类继承后就不需要具体实现了。
CommonMethod.cs
- /// <summary>
- /// 这里定义了一些蛇类和食物类中共同用到的函数和基本属性数据,供子类继承
- /// </summary>
- public class CommonMethod
- {
- public Point Point { get; set; }
- public void Draw(Graphics g, Pen p, SolidBrush s,Point point)
- {
- g.DrawRectangle(p, (11 * point.X - 9), (11 * point.Y - 9), 10, 10);
- g.FillRectangle(s, (11 * point.X - 8), (11 * point.Y - 8), 10 - 1, 10 - 1);
- }
- public void Crear(Graphics g, SolidBrush s, Point point)
- {
- g.FillRectangle(s, (11 * point.X - 9), (11 * point.Y - 9), 10 + 1, 10 + 1);
- }
- }
(上面的"10",为游戏地图小方格的大小,游戏地图就是由N*M个小方格组成的,根据爱好自己定义了)
蛇类,除了继承基类之外,还有属于自己的成员。比如,蛇可以爬行而食物是静止的。
蛇由坐标集组成List<Point>,故蛇的爬行就是改边蛇的坐标点,我考虑到两种实现爬行的方法:
方法一:遍历所有List<Point>,让后一个坐标点等于前一个坐标点,蛇头为运动方向的一个新坐标,这样蛇就可以爬行了;
for (int i = 蛇身.Count - 1; i >= 1; i--)
{
蛇身[i] = 蛇身[i - 1];
}
.......
蛇身[0]=new Point(新坐标);
方法二:蛇的爬行就是画头和消尾的过程,所以只需要改变蛇头坐标和删掉尾坐标即可,如果吃到食物,不用去尾,这样蛇自然就变长了。
void Move()
{
蛇身.Insert(0, 新坐标);
if(吃到食物)
......
else
蛇身.Remove(尾坐标);
}
对比两种方法,方法二效率明显高于方法一,方法一每次蛇爬行都要重新画整个蛇身,有时会导致画面很闪烁(没双缓冲);方法二只需画蛇头和消蛇尾就可以了。
Snake.cs
- public enum SnakeDirection
- {
- Up, Down, Left, Right
- }
- public enum SnakeState
- {
- Normal = 50, Fast = 30, Low = 200
- }
- public class Snake:CommonMethod
- {
- public List<Point> SnakePoints{get;set;}
- public SnakeDirection SnakeDirection { get; set; }
- public SnakeState SnakeState { get; set; }
- //活动场地
- public int backWidth;
- public int backHeight;
- //构造函数
- public Snake(int snakeLengh, SnakeDirection direction,int w,int h)
- {
- SnakePoints = new List<Point>();
- //缺省蛇长度
- for (int i = snakeLengh; i > 0; i--)
- {
- SnakePoints.Add(new Point(i,5));
- }
- this.SnakeDirection = direction;
- this.backWidth = w;
- this.backHeight = h;
- }
- //是否可以爬行,如果为真,则把新坐标添加进蛇身并充当蛇头
- public bool IsCanMove(ref Point point)
- {
- point.X = SnakePoints[0].X;
- point.Y = SnakePoints[0].Y;
- switch (SnakeDirection)
- {
- case SnakeDirection.Up:
- if (point.Y == 1)
- return false;
- else
- point.Y -= 1;
- break;
- case SnakeDirection.Down:
- if (point.Y ==backHeight/10-3)
- return false;
- else
- point.Y += 1;
- break;
- case SnakeDirection.Left:
- if (point.X == 1)
- return false;
- else
- point.X -= 1;
- break;
- case SnakeDirection.Right:
- if (point.X ==backWidth/10-2)
- return false;
- else
- point.X += 1;
- break;
- }
- SnakePoints.Insert(0, point);
- return true;
- }
- //画整个蛇身
- public void DrawSnake(Graphics g)
- {
- foreach (Point point in SnakePoints)
- {
- base.Draw(g, new Pen(Color.Black), new SolidBrush(Color.YellowGreen),point);
- }
- }
- //画蛇头
- public void DrawHead(Graphics g)
- {
- base.Draw(g, new Pen(Color.Black), new SolidBrush(Color.YellowGreen),Point);
- }
- }
食物类就比较简单了
Food.cs
- /// <summary>
- /// 定义食物类型,以后扩展用,目前这个版本没用到
- /// </summary>
- public enum FoodType
- {
- Normal,Fast,Slow,Long
- }
- public class Food:CommonMethod
- {
- public FoodType FoodType { get; set; }
- public void DrawFood(Graphics g)
- {
- base.Draw(g, new Pen(Color.Black), new SolidBrush(Color.Orange),Point);
- }
- }