开发环境
Vs 2015 + EasyX
需求分析
- 蛇能上下左右移动
- 蛇能吃食物
- 能判断蛇的死亡
主要的类
蛇
蛇的长度,每节蛇的坐标,蛇移动的方向;
蛇初始化,移动,改变方向,吃食物,画蛇,蛇是否死亡
食物
食物的坐标,食物是否被吃掉;
初始化食物,新的食物,画食物;
因为蛇吃食物时需要知道食物的坐标,所以需要获得食物坐标的方法;
因为蛇吃食物后需要改变食物是否被吃的状态,所以需要获得食物坐标的方法;
因为需要根据食物是否被吃来确定是否需要新食物,所以需要获得食物状态的方法;
主函数
首先应初始化蛇和食物,直接用构造函数初始化即可,然后在一个死循环中完成游戏逻辑,每一个循环为一个游戏帧;
游戏逻辑为:首先判断是否有键盘按下,若有蛇改变方向,若没有蛇按原方向运动,并绘制蛇和食物,然后执行蛇吃食物方法,此方法中若吃了食物则改变食物状态,根据食物状态判断是否产生新的食物。
实现
头文件
头文件主要完成蛇和食物的定义
#ifndef _GREEDYSNAKE_H
#define _GREEDYSNAKE_H
#include <stdlib.h>
#include <time.h>
#include <graphics.h>
#include <conio.h>
//方向枚举
enum Ch { up = 72, down = 80, left = 75, right = 77 };
//坐标结构体
struct Coor
{
int x;
int y;
};
class Food
{
private:
Coor fzb;//食物的坐标
bool isEat;//食物有没有吃到食物
public:
Food();
void NewFood();
Coor GetZb();
bool GetIsEat();
void SetIsEat(bool b);
void DrawFood();//画食物
};
Food::Food()
{
fzb.x = rand() % 64 * 10;
fzb.y = rand() % 48 * 10;
isEat = false;
}
void Food::NewFood()
{
fzb.x = rand() % 64 * 10;
fzb.y = rand() % 48 * 10;
isEat = false;
}
Coor Food::GetZb()
{
return fzb;
}
bool Food::GetIsEat()
{
return isEat;
}
void Food::SetIsEat(bool b)
{
isEat = b;
}
void Food::DrawFood()
{
roundrect(fzb.x, fzb.y, fzb.x + 10, fzb.y + 10, 10, 10);
}
//蛇类
class Snake
{
private:
int n;//蛇当前的节数
Ch ch;//方向
Coor szb[100];//蛇的每一节坐标,100为蛇的最大长度
public:
Snake();
void DrawSnake();//画蛇
void MoveSnake();//移动蛇
void ChangeCh();//改变方向
void EatFood(Food &food);//吃食物
void GameOver();//判断游戏是否结束
};
Snake::Snake()
{
/*mciSendString("play 甩葱歌.mp3 alias BGM", 0, 0, 0);
mciSendString("play BGM repeat", 0, 0, 0);*/
n = 3;
ch = right;
szb[0].x = 20;
szb[0].y = 200;
szb[1].x = 10;
szb[1].y = 200;
szb[2].x = 0;
szb[2].y = 200;
}
void Snake::DrawSnake()
{
cleardevice();//清除窗口
for (int i = 0; i < n; i++)
{
rectangle(szb[i].x, szb[i].y, szb[i].x + 10, szb[i].y + 10);
}
}
void Snake::MoveSnake()
{
//移动蛇的思路是后面一节蛇的坐标继承前面一节蛇的坐标
for (int i = n - 1; i > 0; i--)
{
szb[i] = szb[i - 1];
}
//蛇头的移动与方向有关
switch (ch)
{
case up:
szb[0].y -= 10;
break;
case down:
szb[0].y += 10;
break;
case left:
szb[0].x -= 10;
break;
case right:
szb[0].x += 10;
break;
}
}
void Snake::ChangeCh()
{
int move;
move = _getch();
switch (move)
{
case up:
if (ch != down)
ch = up;
break;
case down:
if (ch != up)
ch = down;
break;
case right:
if (ch != left)
ch = right;
break;
case left:
if (ch != right)
ch = left;
break;
}
}
void Snake::EatFood(Food &food)//若想改变food的成员变量用引用
{
if (szb[0].x == food.GetZb().x && szb[0].y == food.GetZb().y)
{
n++;
food.SetIsEat(true);
}
}
//判断游戏结束
void Snake::GameOver()
{
//蛇头超出边界
if (szb[0].x < 0 || szb[0].x>640 || szb[0].y < 0 || szb[0].y>480)
{
closegraph();//关闭窗口
exit(0);//结束进程
}
//蛇头与蛇身相撞
for (int i = n - 1; i > 0; i--)
{
if (szb[0].x == szb[i].x && szb[0].y == szb[i].y)
{
closegraph();//关闭窗口
exit(0);//结束进程
}
}
}
#endif
源文件
源文件主要执行的是游戏帧
#include "GreedySnake.h"
int main()
{
srand((unsigned int)time(NULL));//初始化随机数种子
initgraph(640, 480);//初始化一个窗口,大小为640*480
Snake snake;
Food food;
while (1)//游戏环节为一个死循环
{
if(!_kbhit())//_kbhit检测有无按键,若一直没有按键则蛇按原方向一直移动
{
snake.MoveSnake();//移动蛇
snake.DrawSnake();//画蛇
food.DrawFood();//画食物
snake.EatFood(food);//吃食物
if (food.GetIsEat())//若食物被吃了则创建食物
food.NewFood();//创建食物
snake.GameOver();//判断游戏是否结束
Sleep(100);//休眠100毫秒
}
else
//检测到按键
snake.ChangeCh();//改变方向
}
return 0;
}
注意点
- 方向的枚举
enum Ch { up = 72, down = 80, left = 75, right = 77 };
- 蛇的移动是将前一节的坐标给后一节
- 检测有无按键的函数