?? 一、 ? 头文件的包含
#include<stdio.h> #include<graphics.h>//easyx图形库的头文件包含 #include<string.h> #include<conio.h> #include<time.h> #include<stdlib.h>
? 二、 自定义数据类型的创建
??? 在这里我们需要对我们所需要的游戏元素(蛇和食物)进行定义。我采用了画矩形的方式来表示蛇和食物。
??? 们的矩形和窗口的坐标表示如图所示,

?要用easyx画出矩形,我们就需要知道矩形的左上角坐标和右下角坐标,坐标就有x,y这两个属性,因此我们就用一个结构体去封装坐标。在创建蛇和食物的时候我们就要清楚蛇和食物在我们的游戏中有哪些动作和属性,这里我就一普通的贪吃蛇的蛇和食物的属性为例:蛇的属性就有,它的最大节数,蛇的每一节都有自己的坐标,蛇的方向,食物的属性就有:食物的坐标,食物的分数,食物的存在标记
具体代码如下:
typedef struct xy { ?? ?int x; ?? ?int y; }mysnake,myfood;//起别名
struct Snake { ?? ?mysnake xy[1000];//蛇的最大长度 ?? ?int pich;//蛇的当前节数 ?? ?char postion;//蛇的方向 }snake;//变量名
struct food { ?? myfood xy;//食物的坐标位置 ?? ?int point;//食物的分数 ?? ?int flag;//食物存在的标记 }food;//变量名 enum snakepostion{right=77,left=75,up=72,down=80};//枚举方向
三、游戏逻辑的实现
????? 贪吃蛇 的游戏逻辑无非就是,蛇的移动,蛇吃掉食物然后重新生成食物,吃掉食物后玩家游戏分数的增加,蛇的判死。
(一)初始化蛇
???? 在前面我们已经定义了蛇的变量(snake),这里我们就要对蛇的属性进行初始化。我在这里简单的写,就是开始游戏时,蛇就有三节的长度,让蛇的方向向右。具体代码如下:
void initsnake() { ?? ?snake.xy[2].x =100; ?? ?snake.xy[2].y =100;
?? ?snake.xy[1].x = 110; ?? ?snake.xy[1].y = 100;
?? ?snake.xy[0].x =120; ?? ?snake.xy[0].y = 100;
?? ?snake.pich = 3; ?? ?snake.postion = right; ?? ? ?? ?for (int i = 3; i < 1000; i++) ?? ?{ ?? ??? ?snake.xy[i].x = -120; ?? ??? ?snake.xy[i].y = -100; ?? ?}
}
(二)画蛇
???? 就是把对应坐标的矩形输出在窗口上,具体代码如下:
void drawsnake() { ?? ? ?? ?for (int i = 0; i <snake.pich; i++) ?? ?{ ?? ??? ? ?? ??? ? ?? ??? ?setlinecolor(BLACK);//设置矩形的边框颜色为黑色 ?? ??? ?setfillcolor(GREEN);//设置矩形的填充颜色为绿色 ?? ??? ?fillrectangle(snake.xy[i].x, snake.xy[i].y, snake.xy[i].x + 10, snake.xy[i].y + 10); ?? ??? ? ?? ?} ?? ? }
运行结果如下:

(三)移动蛇
?? 蛇的移动分为两部分,当蛇移动时,除了蛇头其余的每一节的坐标都会变为前一节的坐标。而蛇头的移动取决于蛇的方向。具体代码如下:
void movesnake() { ?? ?//除第一节外其他节的移动情况 ?? ?for (int i = snake.pich-1; i>0; i--) ?? ?{ ?? ??? ?snake.xy[i].x = snake.xy[i-1].x; ?? ??? ?snake.xy[i].y = snake.xy[i-1].y; ?? ?} ?? ?//第一节的处理 ?? ?switch (snake.postion) ?? ?{ ?? ?case right: ?? ??? ?snake.xy[0].x += 10; ?? ??? ?break; ?? ?case left: ?? ??? ?snake.xy[0].x -= 10; ?? ??? ?break; ?? ?case up: ?? ??? ?snake.xy[0].y -= 10; ?? ??? ?break; ?? ?case down: ?? ??? ?snake.xy[0].y += 10; ?? ??? ?break; ?? ?default: ?? ??? ?break; ?? ?} }
运行结果如下:

?
?
(四)按键操作
???? 游戏进行的过程中,需要实现,游戏于用户的交互,这里我们采用回显输入来完成交互。按键操作实现蛇的方向的改变和游戏的暂定和开始。具体代码如下:
void keydown() {
?? ?if (_kbhit()) ?? ?{ ?? ??? ?char c = _getch(); ?? ??? ?switch (c) ?? ??? ?{ ?? ??? ?case right: ?? ??? ??? ?if (snake.postion != left) ?? ??? ??? ??? ?snake.postion = right; ?? ??? ??? ?break; ?? ??? ?case left: ?? ??? ??? ?if (snake.postion != right) ?? ??? ??? ??? ?snake.postion = left; ?? ??? ??? ?break; ?? ??? ?case up: ?? ??? ??? ?if (snake.postion != down) ?? ??? ??? ??? ?snake.postion = up; ?? ??? ??? ?break; ?? ??? ?case down: ?? ??? ??? ?if (snake.postion != up) ?? ??? ??? ??? ?snake.postion = down; ?? ??? ??? ?break; ?? ??? ?case ' ': ?? ??? ??? ?while (1) ?? ??? ??? ?{ ?? ??? ??? ??? ?char d = _getch(); ?? ??? ??? ??? ?if (d == ' ') ?? ??? ??? ??? ??? ?break;
?? ??? ??? ?} ?? ??? ?default: ?? ??? ??? ?break;
?? ??? ?}
?? ?} }
运行结果如下:

?
?
(五)初始化食物
??? 食物的初始化同蛇的初始化类似,需要对食物的属性进行初始化。因为食物出现的位置是随机的,所以我们在这里使用随机数来确定食物的坐标,同时将食物的分数置为0,食物的存在标记置为1(及食物存在)。具体代码如下:
void initfood() { ?? ?srand((unsigned int)time(NULL)); ?? ?food.xy.y = rand() % 59*10; ?? ?food.xy.x = rand() % 79*10; ?? ?food.flag = 1; ?? ?food.point = 0; }
(六)画食物
?? 画食物同画蛇一样,先判断食物是否存在,若食物不存在则重新产生食物的坐标,按照食物的对应坐标将矩形显示在窗口上。同时将食物的分数输出在窗口上。具体代码如下
void drawfood() { ?? ?srand((unsigned int)time(NULL)); ?? ?if (food.flag == 0) ?? ?{ ?? ??? ?food.xy.y = rand() % 59*10; ?? ??? ?food.xy.x = rand() % 79*10; ?? ??? ?food.flag = 1; ?? ??? ? ?? ?} ?? ??? ?setfillcolor(RED); ?? ??? ?fillrectangle(food.xy.x, food.xy.y, food.xy.x + 10, food.xy.y + 10); ?? ??? ?settextcolor(BLUE); ?? ??? ?char gamepoint[20]="0"; ?? ??? ?sprintf(gamepoint, "%d", food.point); ?? ??? ?outtextxy(670, 0, "分数:"); ?? ??? ?outtextxy(720, 0, gamepoint); }
运行结果如下:

?
?
(七)蛇吃食物
?? 当蛇头的坐标等于食物的坐标时,将蛇的节数增加一节,食物的分数增加10 ,食物的存在标记置为0(及食物不存在)。具体代码如下
void eatfood() { ?? ?if (snake.xy[0].x == food.xy.x&&snake.xy[0].y == food.xy.y) ?? ?{ ?? ??? ?snake.pich++; ?? ??? ?food.flag = 0; ?? ??? ?food.point += 10; ?? ?}?? }
运行结果如下:

?
?
?
(八)蛇的判死
??? 当蛇头的坐标等于窗口边界的坐标的时候或者蛇头坐标等于蛇其余节数的某一节的坐标的的时候,提示用户蛇死亡,游戏失败。具体代码如下
int snakedie() { ?? ?HWND hwnd = GetHWnd();//获取窗口句柄 ?? ?if (snake.xy[0].x +10< 0 || snake.xy[0].y+10<0 || snake.xy[0].x ==800 || snake.xy[0].y ==600) ?? ?{ ?? ??? ?MessageBox(hwnd, "你撞墙了", "游戏结束", MB_OK); ?? ??? ?return 1; ?? ?} ?? ?for (int i = 1; i < snake.pich; i++) ?? ?{
?? ??? ?if (snake.xy[i].x == snake.xy[0].x&&snake.xy[i].y == snake.xy[0].y) ?? ??? ?{ ?? ??? ??? ?MessageBox(hwnd, "你咬到自己了", "游戏结束", MB_OK); ?? ??? ??? ?return 1; ?? ??? ?} ?? ?}
}
运行结果如下: ?
?
(九)定时器的实现
?? 通过多对时间函数应用实现蛇的移动速度的控制。具体代码如下
int Timer(time_t time, int id) { ?? ?static time_t start[5];//采用静态变量表示游戏进行的当前时间 ?? ?int end = clock();//游戏进行的时间为结束时间 ?? ?if (end - start[id] >= time) ?? ?{ ?? ??? ?start[id] = end; ?? ??? ?return 1; ?? ?} ?? ?return 0;
}
四、主函数的实现
主函数通过对其他函数的调用实现游戏的基本逻辑的串联。代码如下
int main()
{
? initgraph(800, 600);
BeginBatchDraw(); ?? ?setbkcolor(WHITE); ?? ?cleardevice(); ?? ?initsnake(); ?? ? ?? ?initfood(); ?? ?while (1) ?? ?{ ?? ? ?? ??? ?cleardevice(); ?? ??? ?keydown(); ?? ??? ?drawfood(); ?? ??? ?drawsnake(); ?? ??? ?if (snakedie() == 1) ?? ??? ?{ ?? ??? ??? ? ?? ??? ??? ?break; ?? ??? ?} ?? ??? ? ?? ??? ?if (Timer(80-(snake.pich/10)*5, 0)) ?? ??? ?{ ?? ??? ??? ??? ?movesnake(); ?? ??? ?} ?? ??? ?eatfood(); ?? ??? ?FlushBatchDraw();
?? ?} ?? ?EndBatchDraw(); }
return 0; }
五、总结
???? 这是一个简单的贪吃蛇程序,只有贪吃蛇游戏的基本实现。感兴趣的伙伴们可以加上菜单、文件操作等,会是一个比较完整的游戏? 。
|