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(); } }