c++ 小游戏(贪吃蛇)

游戏规则

        贪食蛇游戏要求玩家控制方向键(或WSAD键)来控制小蛇的前进方向,以使蛇吃掉面板上随即位置上的食物。每次成功吃掉食物后小蛇体长将增加一点,得分增加。当小蛇撞到边界或者蛇头与蛇身相撞时,蛇将挂掉,游戏随之结束。

游戏结构设计

        游戏应当包含初始欢迎界面,游戏界面,游戏结束界面。

        建立一个CGame类,来管理游戏的进度。该类放在Game.h文件中,在该类中可以分别定义NewGame(),PlayGame(),SetGame()和ExitGame()四个函数来控制游戏的各个单元,为了使整个程序看起来更像个游戏,可以采取更加漂亮的界面来展示游戏各部分。

  1. NewGame()函数设定游戏欢迎界面。可以简单地输出了一些方块字符组成的游戏名SNAKE和一句提示“Press any key to start…”。点击任意键后,游戏程序将转入SetGame()中继续执行。可以加上一些动态效果,让提示”Press any keyto start…”不断跳动。
  2. SetGame()中包括游戏的设置内容。可以选择Easy,Normal,Hard三个选项。这三个选项将对应小蛇不同的的移动速度,具体来说将体现在PlayGame()函数中每次循环执行速度。设置完成后,游戏程序将转入PlayGame()继续执行。
  3. PlayGame()函数主体将是一个死循环,因为可将游戏考虑成一个无穷的循环,循环中迭代的每一步都依次进行:判断用户是否输入、然后根据用户输入调整游戏内容(如果没有输入则按默认方式继续执行游戏)、判断是否符合规则(不符合则跳出循环,转入ExitGame()退出游戏)、判断是否需要加分扣分。执行完以上这些步骤后,将进行下一次迭代。当然进行游戏之前,还要执行必要的初始化工作,来显示大体框架和提示信息。
  4. 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);
}

猜你喜欢

转载自blog.csdn.net/acdsxzsdaffvgscds/article/details/81205309