设计的小游戏,初学者可以加点自己想加的其他功能。
游戏规则
贪食蛇游戏要求玩家控制方向键(或WSAD键)来控制小蛇的前进方向,以使蛇吃掉面板上随即位置上的食物。每次成功吃掉食物后小蛇体长将增加一点,得分增加。当小蛇撞到边界或者蛇头与蛇身相撞时,蛇将挂掉,游戏随之结束。
游戏结构设计
游戏应当包含初始欢迎界面,游戏界面,游戏结束界面。
建立一个CGame类,来管理游戏的进度。该类放在Game.h文件中,在该类中可以分别定义NewGame(),PlayGame(),SetGame()和ExitGame()四个函数来控制游戏的各个单元,为了使整个程序看起来更像个游戏,可以采取更加漂亮的界面来展示游戏各部分。
NewGame()函数设定游戏欢迎界面。可以简单地输出了一些方块字符组成的游戏名SNAKE和一句提示“Press any key to start…”。点击任意键后,游戏程序将转入SetGame()中继续执行。可以加上一些动态效果,让提示”Press any keyto start…”不断跳动。
SetGame()中包括游戏的设置内容。可以选择Easy,Normal,Hard三个选项。这三个选项将对应小蛇不同的的移动速度,具体来说将体现在PlayGame()函数中每次循环执行速度。设置完成后,游戏程序将转入PlayGame()继续执行。
PlayGame()函数主体将是一个死循环,因为可将游戏考虑成一个无穷的循环,循环中迭代的每一步都依次进行:判断用户是否输入、然后根据用户输入调整游戏内容(如果没有输入则按默认方式继续执行游戏)、判断是否符合规则(不符合则跳出循环,转入ExitGame()退出游戏)、判断是否需要加分扣分。执行完以上这些步骤后,将进行下一次迭代。当然进行游戏之前,还要执行必要的初始化工作,来显示大体框架和提示信息。
EitGame()中将显示游戏得分,并询问玩家是否再玩一次。这里拼出了一个骷髅头的图案,表示Game Over。
以上为游戏的主体内容,这四个函数设定了游戏的基本结构,剩余部分将继续考虑细节问题。然后再展示Game.h的细节内容。
建立游戏对象
先建立一系列类表示游戏对象,其中应包括对游戏对象的处理方式(函数)。分析游戏,可以知道游戏主体是小蛇和食物。
所有的游戏对象,包括蛇和食物,都是由控制台上的一系列点组成的。因此需要很多处理点对象的方法。可建立Point.h来定义CPoint对象,来简化其他对象的处理。
代码如下:
- #include <stdio.h>
- #include <stdlib.h>
- #include <time.h>
- #include <conio.h>
- #include <windows.h>
- #define W 32
- #define H 22
- const int SIZE_SK = 30*20;
- HANDLE hOut;
- COORD pos= {0, 0};
- CONSOLE_CURSOR_INFO cur_info = {1, 0};
- struct snake {
- short x, y; //节点坐标
- short md; //节点运动方向
- short num; //节点内容
- int col; //节点颜色
- } snake[30*20];
- const short dx[4] = {-1, 1, 0, 0}; //方向
- const short dy[4] = {0, 0, -1, 1}; //方向
- short gamemap[W][H];
- short head, tail, score=0;
- void initmap(int tm);
- void createFood(void);
- int getkeys(void);
- int move(int idx);
- void pntSnake(int i);
- void drawEdge(void);
- void gameinfo(char *s, int n);
- int main(void)
- {
- int tm = 150; //刷新间隔150毫秒
- hOut = GetStdHandle(STD_OUTPUT_HANDLE);
- SetConsoleCursorInfo(hOut, &cur_info); //隐藏光标
- system("COLOR 2f");
- initmap(tm);
- return 0;
- }
- void initmap(int tm)
- {
- short x, y, i, j;
- int index, tmp, ret_m;
- while (1){
- head=0;
- tail=-1;
- score=0;
- //初始化蛇节点随机颜色
- srand(time(0));
- for (i=0; i<SIZE_SK; i++)
- snake[i].col = rand()%8+8 | 0x20;
- //初始化地图数组
- for (i=0; i<W; i++)
- for (j=0; j<H; j++)
- gamemap[i][j] = 0;
- system("cls");
- //绘制边界
- drawEdge();
- //打印蛇头
- srand(time(0));
- do {
- x = rand()%(W-2) + 1;
- y = rand()%(H-2) + 1;
- } while (gamemap[x][y] != 0); //只在空白处生成
- snake[head].x = x;
- snake[head].y = y;
- gamemap[x][y] = snake[head].num = 1;
- pos.X = x*2;
- pos.Y = y;
- SetConsoleCursorPosition(hOut, pos);
- printf("█");
- //打印食物
- createFood();
- while ((index=getkeys()) == 4)
- Sleep(500);
- //循环开始
- while (1){
- ret_m = move(index);
- if (ret_m == 1){
- pos.X = 0; pos.Y = H;
- SetConsoleCursorPosition(hOut, pos);
- printf("哎呀,挂了哦 ^_^");
- break;
- }
- else if (ret_m == 2){
- pos.X = 0; pos.Y = H;
- SetConsoleCursorPosition(hOut, pos);
- printf("哎呀,超神了 ^_^");
- break;
- }
- Sleep(tm);
- if ((tmp = getkeys()) != 4)
- index = tmp;
- }
- //菜单代码
- puts(" 按w\\s\\a\\d 继续玩,退出请关闭我");
- getch();
- }
- }
- void createFood(void)
- {
- short x, y;
- srand(time(0));
- do {
- x = rand()%(W-2) + 1;
- y = rand()%(H-2) + 1;
- } while (gamemap[x][y] != 0);
- gamemap[x][y] = 2;
- pos.X = x*2; pos.Y = y;
- SetConsoleCursorPosition(hOut, pos);
- SetConsoleTextAttribute(hOut, 0x2f);
- printf("⊙");
- }
- int getkeys(void) //获取方向键
- {
- char ch;
- while(kbhit()){
- ch = getch();
- if(ch == 'w')
- return 2;
- if(ch == 's')
- return 3;
- if(ch == 'a')
- return 0;
- if(ch == 'd')
- return 1;
- while (kbhit());
- }
- return 4;
- }
- int move(int idx)
- {
- int newx, newy, pt_tail, i;
- newx = snake[head].x + dx[idx];
- newy = snake[head].y + dy[idx];
- switch (gamemap[newx][newy]){
- case 0:
- if ((head = ++head%SIZE_SK) != tail){ //不相等即蛇节点队列未满
- snake[head].x = newx;
- snake[head].y = newy;
- snake[head].num = 1;
- gamemap[newx][newy] = 1;
- tail = ++tail%SIZE_SK;
- snake[tail].num = 0; //清除尾部,打印空字符
- gamemap[snake[tail].x][snake[tail].y] = 0;
- snake[head].md = idx; //存储移动方向
- pt_tail = tail - 1;
- }
- else
- return 2; //回合结束标记2代表蛇填满空间
- break;
- case 2:
- if ((head = ++head%SIZE_SK) != tail){ //不相等即蛇节点队列未满
- snake[head].x = newx; //head+1后存储新坐标为蛇头
- snake[head].y = newy;
- snake[head].num = 1;
- gamemap[newx][newy] = 1;
- snake[head].md = idx; //存储移动方向
- pt_tail = tail = tail%SIZE_SK;
- score++;
- createFood();
- }
- break;
- case 3:
- return 1; //遇到墙壁
- case 1:
- return 1; //遇到自身
- default: ;
- }
- //打印蛇
- if (pt_tail == -1)
- pt_tail++;
- if (pt_tail < head){ //尾巴在头部后面(这两者是数组索引)
- for (i=pt_tail; i<=head; i++){
- pntSnake(i); //打印
- }
- }
- else{
- for (i=pt_tail; i<SIZE_SK; i++)
- pntSnake(i);
- for (i=0; i<=head; i++)
- pntSnake(i);
- }
- gameinfo(" 分数", score);
- return 0;
- }
- void pntSnake(int i)
- {
- //转换x坐标:内部是连续整数,打印时一个字符占位2,所以要2x
- pos.X = snake[i].x * 2;
- pos.Y = snake[i].y;
- SetConsoleCursorPosition(hOut, pos);
- if (snake[i].num == 0){
- putchar('\0'); //清除蛇尾
- putchar('\0');}
- else if (snake[i].num == 1){
- //下面函数第二个参数低4位控制前景色,高4位控制背景色,共8位
- SetConsoleTextAttribute(hOut, snake[i].col);
- printf("█");
- }
- }
- void drawEdge(void)
- {
- int i;
- for (i=0; i<W; i++){
- pos.X = i*2; pos.Y = 0;
- SetConsoleCursorPosition(hOut, pos);
- printf("█");
- gamemap[i][pos.Y] = 3;
- pos.Y = H-1;
- SetConsoleCursorPosition(hOut, pos);
- printf("█");
- gamemap[i][H-1] = 3;
- }
- for (i=1; i<H-1; i++){
- pos.X = 0; pos.Y = i;
- SetConsoleCursorPosition(hOut, pos);
- printf("█");
- gamemap[pos.X][i] = 3;
- pos.X = (W-1)*2;
- SetConsoleCursorPosition(hOut, pos);
- printf("█");
- gamemap[W-1][i] = 3;
- }
- }
- void gameinfo(char *s, int n)
- {
- pos.X = 0; pos.Y = H;
- SetConsoleCursorPosition(hOut, pos);
- SetConsoleTextAttribute(hOut, 0x2f);
- printf(" 贪吃蛇");
- printf(" %s: %d", s, n);
- }
复制代码
|