贪吃蛇游戏设计总结
程序设计
模块化
贪吃蛇游戏可大致分为三部分:
1.打印地图、蛇、果子;
2.蛇:移动、转向;
3.果子;
蛇的活动是这个程序的主要部分、其次是如何打印画面。
- 从数据结构方面考虑,蛇方面目前我分别使用了数组、链表制作了游戏。将蛇想象成由一个个节点构成的对象,那么只需要考虑蛇头也就是第一个节点的移动,其余节点各是前一个节点的复制。打印、移动、打印如此循环往复,形成动画。
- 二维数组:将二维数组视作坐标系,可选用字符或者数字实现,蛇头节点是一个数字代表蛇的长度,每次移动,将这个特殊的数字赋给下一个坐标,然后打印,再对二维数组全部进行一次-1直到0。(例如打印3,减一->2 移动->2-3、打印、减一->1-2 、 移动->1-2-3 打印 循环反复就能实现一串递增的数字在屏幕上移动 )
- 链表:链表本身就是如同蛇一样的结构,每次只需要改变第一个节点的坐标,再让后面的每一个节点的坐标等于先前前面每一个节点的左边,只需要使用gotoxy函数改变光标的位置依次打印每一个节点就可以实现移动。
- 转向:检测是否有键盘输入,如果没有则按照当前方向前进,如果有则判断是否符合运动逻辑(比如往前走,不能直接往后走),如果符合就把这个更改方向并把方向存储起来。代码里则是改变第一个节点的坐标后面节点复制前面节点实现转向。
- 果子
- 如果没有果子,则随机生成一个坐标,在这个坐标放置一个标志,在蛇运动代码中判断蛇头坐标是否等于果子坐标。
- 打印
- 打印-清除(system("cls"))-打印 重复实现动画
- 如果采取printf方式打印会出现闪屏情况,是由于屏幕刷新太快,而图案是一个个打印,所以就出现闪屏现象
- 改用双缓冲方式打印,原理是创建两个缓冲区,通过更换缓冲区实现打印。循环:将图案打印在非活动的缓冲区,再将这个缓冲区设置为活动缓冲区,这样每次打印的就是一个完整的图案,就不会有闪屏现象。(数组不需要)
代码
数组实现
点击查看代码
#include <stdio.h>
#include <windows.h>
#include <conio.h>
#include <time.h>
#include <stdlib.h>
void print1(int num[10][10])
{
system("cls");
int i, j;
for (i = 0; i < 10; i++)
{
for (j = 0; j < 10; j++)
{
printf(" %2d", num[i][j]);
}
printf("\n");
}
}
int main()
{
int x1, y1;
srand((unsigned)time(NULL));
int num[10][10] = {0};
int snakehead = 3;
num[5][5] = snakehead;
int x = 5;
int y = 5;
print1(num);
int flag = 0;
char direction = 'd';
int score = 0;
int flag1 = 0;
while (flag == 0)
{
if (flag1 == 0)
{
flag1 = 1;
do
{
x1 = rand() % 10 + 1;
y1 = rand() % 10 + 1;
} while (num[x1][y1] != 0);
num[x1][y1] = 99;
}
if (kbhit()) // kbhit 用于检测键盘输出的函数
{
char turn = getch();
if (direction == 'd' || direction == 'a')
{
if (turn == 'w' || turn == 's')
direction = turn;
}
if (direction == 'w' || direction == 's')
{
if (turn == 'a' || turn == 'd')
direction = turn;
}
}
else
{
if (direction == 'W' || direction == 'w')
{
if (x == 0)
x = 9;
else
x--;
}
if (direction == 'a' || direction == 'A')
{
if (y == 0)
y = 9;
else
y--;
}
if (direction == 's' || direction == 'S')
{
if (x == 9)
x = 0;
else
x++;
}
if (direction == 'd' || direction == 'D')
{
if (y == 9)
y = 0;
else
y++;
}
if (num[x][y] == 99)
{
score++;
flag1 = 0;
snakehead++;
}
if (num[x][y] != 0 && num[x][y] != 99)
{
flag = 1;
Sleep(1000);
}
num[x][y] = snakehead;
print1(num);
}
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
if (num[i][j] != 0 && num[i][j] != 99)
{
num[i][j]--;
}
}
}
Sleep(100);
}
system("cls");
printf("你的得分是:%d", score);
return 0;
}
链表实现(在舍友的电脑上运行看不到蛇,不知道是不是windows系统不同的问题
点击查看代码
#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <conio.h>
#include <time.h>
#include <malloc.h>
#define _Y 20 // 15行
#define _X 100 // 20列
char data[_Y][_X]; //这是全局变量定义的字符数组
HANDLE hOutput, hOutBuf; //控制台屏幕缓冲区句柄
HANDLE houtpoint;
COORD coord = {5, 0}; //初始输出位置
DWORD bytes = 0;
int hop_flag = 0; //通过指针轮流指向两个缓冲区,实现双缓冲
typedef struct snake_xy
{
int x;
int y;
} xy;
struct Node //节点
{
xy xy1;
struct Node *next;
};
void begin();
void menu(int *);
void snake(int a, int *b);
void change(int *);
void background(int x, int y);
void gotoxy(int x, int y);
struct Node *newNode(xy n);
struct Node *creatlist();
void insertNode_to_head(struct Node *snakehead, xy xy2);
void freeList(struct Node *headNode);
void printList(struct Node *headnode, int score, int level);
void fruits(int *flag, xy *fruit);
void move(struct Node *headNode, char direction, int level);
void insertnewNode_to_back(struct Node *headnode);
void printPic()
{
int i, j;
hop_flag = !hop_flag;
if (!hop_flag)
{ //这里是每次交替,直接把hOutput或hOutBuf赋给houtpoint
houtpoint = hOutput;
}
else
{
houtpoint = hOutBuf;
}
for (i = 0; i < _Y; i++)
{ //打印你需要的二维数组图案
for (j = 0; j < _X; j++)
{
if (i == 0 || i == _Y - 1 || j == 0 || j == _X - 1)
{
data[i][j] = '*';
}
else
{
data[i][j] = ' ';
}
}
}
coord.Y = 1;
for (i = 0; i < _Y; i++)
{ //循环打印每一行
coord.Y++; //每次都打印到下一行
WriteConsoleOutputCharacter(houtpoint, data[i], _X, coord, &bytes);
} // data[i]:每行的地址。 _X: 每行的长度
SetConsoleActiveScreenBuffer(houtpoint);
}
int main()
{
hOutBuf = CreateConsoleScreenBuffer(
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
CONSOLE_TEXTMODE_BUFFER,
NULL);
hOutput = CreateConsoleScreenBuffer(
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
CONSOLE_TEXTMODE_BUFFER,
NULL);
srand((unsigned)time(NULL));
begin();
system("cls");
srand(time(NULL));
int p = 1;
int level = 1;
int on = 0;
printf(" 按w/s选择,输入a确认\n\n");
Sleep(2000);
while (on == 0)
{
if (p == 1)
{
menu(&p); //菜单
}
switch (p)
{
case 0:
snake(level, &on);
break;
case 1:
change(&level);
system("cls");
break;
case 2:
return 0;
break;
default:
break;
}
}
return 0;
}
void begin()
{
int i = 0;
for (i = 0; i < 30; i++)
{
printf("*");
}
printf("\n\n");
printf("\t 贪吃蛇游戏\n\n");
for (i = 0; i < 30; i++)
{
printf("*");
}
Sleep(1500);
}
void menu(int *select)
{
int a = 0;
char b = 0;
int c = 0;
system("cls");
system("color 6");
system("cls");
printf("\n\n");
while (c == 0)
{
int i = 0;
system("cls");
system("color 6");
gotoxy(5, 0);
switch (a)
{
case 0:
printf("\t 开始游戏\n");
break;
case 1:
printf("\t 难度设置\n");
break;
case 2:
printf("\t 退出游戏\n");
break;
default:
break;
}
b = getch();
switch (b)
{
case 'w':
if (a == 0)
a = 2;
else
a--;
break;
case 's':
if (a == 2)
a = 0;
else
a++;
break;
case 'a':
c = 1;
break;
default:
break;
}
}
*select = a;
}
void change(int *level)
{
int a = 1;
printf("\t 当前难度:%d \n", *level);
printf("\t 请输入变更后的难度:>(1-5):\n");
printf("\t");
scanf("%d", &a);
getchar();
*level = a;
}
void snake(int level, int *on)
{
xy fruit; //果子坐标
char direction = 'd';
int flag = 0; //碰墙
int flag1 = 0; //果子
int score = 0;
char flag3 = 0; //是否继续
struct Node *list = creatlist();
xy xys = {50, 10};
struct Node *snakehead = newNode(xys);
list->next = snakehead;
xys.x = 49;
insertNode_to_head(snakehead, xys);
xys.x = 48;
insertNode_to_head(snakehead, xys);
Sleep(2000);
system("cls");
fflush(stdin);
printList(list, score, level);
fruits(&flag1, &fruit);
while (flag == 0)
{
fflush(stdin);
if (kbhit()) // kbhit 用于检测键盘输出的函数
{
char turn = getch();
if (direction == 'd' || direction == 'a')
{
if (turn == 'w' || turn == 's')
direction = turn;
}
if (direction == 'w' || direction == 's')
{
if (turn == 'a' || turn == 'd')
direction = turn;
}
}
else
{
move(list, direction, level);
printList(list, score, level);
fruits(&flag1, &fruit);
}
if (snakehead->xy1.x == 4 || snakehead->xy1.x == 104 || snakehead->xy1.y == 2 || snakehead->xy1.y == 21)
{
flag = 1;
gotoxy(50, 10);
printf("你输了");
printf("是否重新开始?:>(Y/N)");
scanf("%c", &flag3);
if (flag3 == 'N' || flag3 == 'n')
{
*on = 1;
freeList(list);
return;
}
system("cls");
}
if (snakehead->xy1.x == fruit.x && fruit.y == snakehead->xy1.y)
{
score++;
flag1 = 0;
insertnewNode_to_back(list);
}
}
}
void gotoxy(int x, int y) //移动光标
{
COORD p;
p.X = x;
p.Y = y;
SetConsoleCursorPosition(houtpoint, p); //移动光标
}
struct Node *creatlist() //创建链表
{
struct Node *headnode = (struct Node *)malloc(sizeof(struct Node));
headnode->next = NULL;
return headnode;
}
struct Node *newNode(xy n) //新建节点
{
struct Node *newNode1 = (struct Node *)malloc(sizeof(struct Node));
newNode1->xy1 = n;
newNode1->next = NULL;
return newNode1;
}
void printList(struct Node *headnode, int score, int level) //打印链表
{
system("cls");
printPic();
struct Node *nowNode = headnode->next; // snakehead
while (nowNode != NULL)
{
int x = nowNode->xy1.x;
int y = nowNode->xy1.y;
gotoxy(x, y);
printf("*");
nowNode = nowNode->next;
}
gotoxy(105, 10);
printf("your score:%d", score);
gotoxy(105, 11);
printf("当前的难度是:%d\n", level);
gotoxy(105, 12);
printf("用WASD进行移动");
}
void insertNode_to_head(struct Node *snakehead, xy xy2) //头部插入节点
{
struct Node *newNode = (struct Node *)malloc(sizeof(struct Node));
newNode->xy1 = xy2;
newNode->next = snakehead->next;
snakehead->next = newNode;
}
void freeList(struct Node *headNode) //释放链表内存
{
struct Node *pwdNode = headNode;
struct Node *pwdNodeback = pwdNode->next;
while (pwdNode)
{
pwdNode = pwdNodeback;
pwdNodeback = pwdNodeback->next;
free(pwdNode);
}
}
void move(struct Node *headNode, char direction, int level)
{
struct Node *nowNode = headNode->next;
struct Node *nowNode1 = (struct Node *)malloc(sizeof(struct Node *));
nowNode1->xy1 = nowNode->xy1;
struct Node *nowNode2 = (struct Node *)malloc(sizeof(struct Node *));
nowNode2->xy1 = nowNode->next->xy1;
switch (direction)
{
case 'd':
nowNode->xy1.x = nowNode->xy1.x + 1;
Sleep(110 - level * 10);
break;
case 'a':
nowNode->xy1.x = nowNode->xy1.x - 1;
Sleep(110 - level * 10);
break;
case 's':
nowNode->xy1.y = nowNode->xy1.y + 1;
Sleep(110 - level * 10);
break;
case 'w':
nowNode->xy1.y = nowNode->xy1.y - 1;
Sleep(110 - level * 10);
break;
default:
break;
}
while (nowNode->next != NULL) // B = A1 A1->C1 C1 = B1
{
nowNode2->xy1 = nowNode->next->xy1;
nowNode->next->xy1 = nowNode1->xy1;
nowNode1->xy1 = nowNode2->xy1;
nowNode = nowNode->next;
}
}
void fruits(int *flag, xy *fruit)
{
if (*flag == 0)
{
*flag = 1;
(*fruit).x = 6 + rand() % 96 + 1;
(*fruit).y = 4 + rand() % 16 + 1;
}
gotoxy((*fruit).x, (*fruit).y);
printf("*");
}
void insertnewNode_to_back(struct Node *headnode) //尾插法
{
struct Node *pwdNode = headnode->next;
struct Node *NewNode = (struct Node *)malloc(sizeof(struct Node *));
while (pwdNode->next != NULL)
{
pwdNode = pwdNode->next;
}
pwdNode->next = NewNode;
NewNode->next = NULL;
}
运行结果