c语言学了好久了,想写一个贪吃蛇游戏,但是,真的不知道如何下手,只好网上下了一个别人写的初级的贪吃蛇代码来研究!看完代码,发现,代码真的不难理解,真正难的就是思路
贪吃蛇,要能移动,转弯,吃食物,长大,死亡;移动规则,不能在原有方向上往相反方向移动,在地图内上下左右移动,死亡规则是,不能碰自己身体,不能碰墙壁
用什么窗口,如何在窗口上显示一个地图,如何在地图上显示一条蛇,如何在地图上显示食物,用什么表示蛇及食物,如何操控蛇的移动,代码中,窗口用的是控制台窗口,"绘图"其实是打印文本,这需要用到windows系统API的若干函数,有控制台光标定位函数,窗口句柄获取函数,文本颜色设定函数,获取按下的按键信息函数,这些函数可以通过微软的MSDN,查找到如何使用的帮助文件
蛇及食物是有坐标数据的链表结构体,然后按照模块化设计思想,构造游戏结构,一个循环体:创建地图,初始化蛇,调用随机数srand函数并随机生成食物,操控下上左右按键控制蛇移动方向(蛇自动移动),蛇死亡后选择是否重新开始游戏
1、snake.c,main函数
#include "snake.h"
#include <time.h>
int main()
{
int ctrl = 1;
while (ctrl)
{
createMap();
initsnack();
srand((unsigned)time(NULL));
createfood();
key();
gotoxy(60, 26);
color(3);
printf("输入0结束游戏游戏,输入1重新开始:");
scanf("%d", &ctrl);
system("cls");
}
return 0;
}
2、snake.h,包含库头文件、蛇结构体,蛇及食物结构体指针全局变量、共用函数的声明
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
/* snack链表结构体 */
typedef struct snack
{
int x; //x坐标
int y; //y坐标
struct snack *next; //指向下一个snack结构
} snack;
snack *head; //全局蛇头指针
snack *food; //全局食物指针
/*print.c*/
void gotoxy(int x, int y);
int color(WORD c);
void createMap();
void print_snack(snack *ss);
void initsnack();
void createfood();
/*move.c*/
void isfood();
void snack_move();
int kill();
void key();
3、print.c,打印地图、初始化并打印蛇及食物、光标定位、颜色设定
#include "snake.h"
/* 光标定位 */
void gotoxy(int x, int y)
{
COORD c;
c.X = x;
c.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), c);
}
/* 设定颜色 */
int color(WORD c)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c);
return 0;
}
/* 建地图 */
void createMap()
{
int i, j;
for (i = 0; i < 58; i += 2) //地图上下边界
{
color(5);
gotoxy(i, 0);
printf("□");
gotoxy(i, 26);
printf("□");
}
for (i = 1; i < 26; i++) //地图左右边界
{
gotoxy(0, i);
printf("□");
gotoxy(56, i);
printf("□");
}
for (i = 2; i < 56; i += 2) //地图内部
{
for (j = 1; j < 26; j++)
{
color(3);
gotoxy(i, j);
printf("■");
}
}
}
/* 打印蛇身体 */
void print_snack(snack *ss)//传入蛇头指针
{
while (ss != NULL) //遍历蛇体
{
gotoxy(ss->x, ss->y);
color(14);
printf("◆");
ss = ss->next;
}
}
/* 初始化蛇 */
void initsnack()
{
snack *tail;
int i;
tail = (snack *)malloc(sizeof(snack)); //分配内存
tail->x = 20; //出生点
tail->y = 15;
tail->next = NULL;
for (i = 1; i < 4; i++) //头插法加长蛇身
{
head = (snack *)malloc(sizeof(snack));
head->x = 20 + 2 * i;
head->y = 15;
head->next = tail;
tail = head;
}
print_snack(tail); //打印蛇体
}
/* 生成并打印食物 */
void createfood()
{
snack *tem_food, *ss;
tem_food = (snack *)malloc(sizeof(snack));
tem_food->x = rand() % 52 + 2; //食物坐标取随机数
while ((tem_food->x % 2) != 0) //保证x为奇数使食物对齐地图网格
{
tem_food->x = rand() % 52 + 2;
}
tem_food->y = rand() % 24 + 1;
ss = head;
while (ss != NULL) //判断食物是否与蛇体重合
{
if (ss->x == tem_food->x && ss->y == tem_food->y)
{
free(tem_food);
createfood(); //重合的话释放内存并重新生成食物
}
ss = ss->next;
}
gotoxy(tem_food->x, tem_food->y);
food = tem_food;
color(12);
printf("★"); //打印食物
}
4、move.c,蛇的移动、按键操控及获取 、吃食物、死亡
#include "snake.h"
/*限制在本文件内使用*/
static int score; /*吃到食物得分score*/
static enum {U,D,L,R} status = R; /*蛇移动的方向,初始为右*/
/*吃到食物得分,否则删除蛇尾*/
void isfood()
{
/* 判断蛇是否吃到食物 */
snack *tail = head;
if (head->x == food->x && head->y == food->y) //蛇头与食物坐标重合即吃到食物
{
print_snack(head); //打印蛇体
free(food); //释放内存
score += 1;
gotoxy(60, 1);
color(2);
printf("得分:%d", score); //打印得分+1
createfood(); //重新创建食物
gotoxy(58, 26); //光标移走
}
else
{
while (tail->next->next != NULL)
{
tail = tail->next;
}
gotoxy(58, 26); /*移走光标*/
gotoxy(tail->next->x, tail->next->y);
color(3);
printf("■"); //不是食物的话在蛇尾处补上地图
free(tail->next); //释放蛇尾
tail->next = NULL; //上一节成为蛇尾
print_snack(head); //打印蛇体
gotoxy(58, 26); /*移走光标*/
}
}
/* 蛇的移动 */
void snack_move()
{
snack *next_head; /*新蛇头*/
next_head = (snack *)malloc(sizeof(snack));
if (status == U) //向上移动
{
next_head->x = head->x;
next_head->y = head->y - 1;
}
if (status == D) //向下移动
{
next_head->x = head->x;
next_head->y = head->y + 1;
}
if (status == L) //向左移动
{
next_head->x = head->x - 2;
next_head->y = head->y;
}
if (status == R) //向右移动
{
next_head->x = head->x + 2;
next_head->y = head->y;
}
next_head->next = head; /*头插法*/
head = next_head;
isfood(); /*不是食物, 删除蛇尾*/
}
/* 判断是否死亡 */
int kill()
{
int flag = 0;
snack *s;
s = head->next;
if (head->x == 0 || head->x == 56 || head->y == 0 || head->y == 26) //撞墙判断
{
flag = 1;
}
while (s->next != NULL)
{
if (head->x == s->x && head->y == s->y) //撞到自己身体判断
{
flag = 1;
break;
}
s = s->next;
}
return flag;
}
/* 判断键盘按键 */
void key()
{
int sleeptime = 200; //间隔时间
status = R; //初始向右移动
score = 0;
gotoxy(60, 1);
color(2);
printf("得分:%d", score); //打印初始分
while (1) //获取按键指令
{
if (GetAsyncKeyState(VK_UP) && status != D)
{
status = U;
}
else if (GetAsyncKeyState(VK_DOWN) && status != U)
{
status = D;
}
else if (GetAsyncKeyState(VK_LEFT) && status != R)
{
status = L;
}
else if (GetAsyncKeyState(VK_RIGHT) && status != L)
{
status = R;
}
if (kill()) //失败跳出循环
{
gotoxy(60, 5);
color(FOREGROUND_RED);
printf("You are dead!");
break;
}
Sleep(sleeptime);
snack_move();
}
}