C 语言实现经典贪吃蛇游戏
Posted Spring-_-Bear
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C 语言实现经典贪吃蛇游戏相关的知识,希望对你有一定的参考价值。
原文链接:C语言 贪吃蛇游戏
文章目录
一、说明
笔者使用 C 语言实现经典贪吃蛇游戏,其中开发环境为 Windows 平台下的 VisualStudio2019
本文在 原文 的基础上将原文源码进行模块化拆分,以面向过程的自顶向下模块化思想进行编程,代码可读性高、系统健壮性强、游戏界面友好美观,功能做出适当调整并修复了一系列已知问题
二、效果
2.1 欢迎界面
2.2 游戏规则
2.3 得分排行
2.4 退出游戏
2.5 游戏界面
2.6 游戏结束
三、源码
3.1 cmd.h
#pragma once
// 设置光标
void setPox(int x, int y);
// 设置文本颜色
void setTextColor(unsigned short color);
3.2 cmd.c
#include<windows.h>
// 设置光标
void setPox(int x, int y)
COORD pox = x, y;
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOut, pox);
// 设置文本颜色
void setTextColor(unsigned short color)
HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hCon, color);
3.3 io.h
#pragma once
// 游戏排行榜,读取游戏数据
void rankingList();
// 保存成绩,游戏存档
void saveGrade(int score);
3.4 io.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<time.h>
#include "model.h"
#include "utils.h"
// 游戏排行榜,读取游戏数据
void rankingList()
system("cls");
// 打开游戏排行榜文件
FILE *fp = fopen("rank.txt", "rb");
if (fp == NULL)
setPox(56, 12);
printf("游戏排行榜文件不存在");
setPox(0, 0);
return;
rewind(fp);
// 读取文件中的游戏数据,最多读取 1000 条游戏记录
record gameRecord[1000];
int i = 0;
// feof 检查文件是否结束,遇到结束符,返回非零
while (!feof(fp))
fread(&gameRecord[i], sizeof(struct record), 1, fp);
i++;
// 按游戏得分排序
qsort(gameRecord, i - 1, sizeof(record), compare);
// 输出得分最高的 10 次游戏记录
i = i > 10 ? 10 : i;
// 输出游戏排行榜信息
setPox(55, 3);
setTextColor(12);
printf("排行榜");
setPox(42, 5);
setTextColor(14);
printf("得分\\t\\t\\t时间\\n");
setTextColor(15);
int j = 0;
for (; j < i - 1; j++)
setPox(43, 7 + j * 2);
printf("%d\\t\\t", gameRecord[j].grade);
printf("%d/%02d/%02d ", gameRecord[j].year + 1900, gameRecord[j].mon + 1, gameRecord[j].day);
printf("%02d:%02d:%02d\\n", gameRecord[j].hour, gameRecord[j].min, gameRecord[j].sec);
setPox(43, 7 + j * 2);
setTextColor(1);
printf("注:按任意键继续···");
fclose(fp);
// 保存成绩,游戏存档
void saveGrade(int score)
// 获取系统时间
time_t timestamp;
time(×tamp);
struct tm *ti = localtime(×tamp);
// 为当前游戏数据分配空间
record *gameRecord = (record *) malloc(sizeof(record));
// 保存年月日时分秒以及分数
gameRecord->year = ti->tm_year;
gameRecord->mon = ti->tm_mon;
gameRecord->day = ti->tm_mday;
gameRecord->hour = ti->tm_hour;
gameRecord->min = ti->tm_min;
gameRecord->sec = ti->tm_sec;
gameRecord->grade = score;
// 打开文件并追加写入本次游戏数据
FILE *fp = fopen("rank.txt", "ab");
if (fp == NULL)
fp = fopen("rank.txt", "wb");
fwrite(gameRecord, sizeof(record), 1, fp);
fclose(fp);
free(gameRecord);
3.5 model.h
#pragma once
// 蛇
typedef struct snake
int x;
int y;
struct snake *next;
snake;
// 游戏记录
typedef struct record
int grade;
int year;
int mon;
int day;
int hour;
int min;
int sec;
record;
// 游戏数据
typedef struct data
// 分数
int score;
// 速度,值越小蛇的速度越快
int speed;
// 速度等级,值越大小蛇速度越快
int speedLevel;
// 每个食物分数,加速一次 foodFraction 翻倍,减速一次 foodFraction 减半
int foodFraction;
// 速度变化前吃到的食物数,用于判断是否开启自动加速
int eatenFoods;
data;
3.6 service.h
#pragma once
#include "model.h"
// 初始化蛇
snake *initSnake();
// 随机食物
snake *randomFood(snake *q);
// 打印蛇身
void snakeBody(snake *p, int speed);
// 边界碰撞判定
int collision(snake *q);
// 释放蛇身
void destroy(snake *p);
// 游戏暂停
void suspendGame(snake *q);
// 蛇身传递
void snakeInherit(snake *p);
// 吃到食物
snake *foodInMouth(snake *snakeHead, snake *food, data *curGameData);
// 自动加速
void autoAccelerate(data *curGameData, int isAutoAccelerate);
// 开始游戏
void startGame();
3.7 service.c
#define _CRT_SECURE_NO_WARNINGS
#include<windows.h>
#include "model.h"
// 初始化蛇
snake *initSnake()
snake *q = (snake *) malloc(sizeof(snake));
q->next = NULL;
for (int i = 6; i < 19; i = i + 2)
snake *tmp = (snake *) malloc(sizeof(snake));
tmp->x = i;
tmp->y = 5;
tmp->next = q->next;
q->next = tmp;
return q;
// 随机食物
snake *randomFood(snake *q)
snake *p, *k;
k = (snake *) malloc(sizeof(snake));
k->next = NULL;
gotoHere:
p = q->next;
srand((unsigned) time(NULL));
// 确保食物显示在游戏地图范围内
while ((k->x = rand() % 57 + 4) % 2 != 0) ;
k->y = rand() % 24 + 3;
while (p != NULL)
// 如果新食物与蛇身重合,则重新生成
if ((k->x == p->x && k->y == p->y))
goto gotoHere;
p = p->next;
setTextColor(12);
setPox(k->x, k->y);
printf("●");
return k;
// 打印蛇身
void snakeBody(snake *p, int speed)
snake *r, *k = p->next;
setTextColor(14);
while (p->next != NULL)
r = p->next;
p = r;
setPox(r->x, r->y);
printf("★");
if (k->x != p->x || k->y != p->y)
// 覆盖尾迹
setPox(p->x, p->y);
setTextColor(3);
printf("■");
setPox(0, 0);
Sleep(speed);
// 边界碰撞判定
int collision(snake *q)
snake *p = q->next, *r = p->next;
// 撞墙
if (p->x == 2 || p->x == 62 || p->y == 1 || p->y == 27)
return 1;
while (r->next != NULL)
// 咬到自己
if (p->x == r->x && p->y == r->y)
return 1;
r = r->next;
return 0;
// 释放蛇身
void destroy(snake *p)
snake *q = p, *r;
while (q->next != NULL)
r = q;
q = q->next;
free(r);
free(q);
// 游戏暂停
void suspendGame(snake *q)
setPox(0, 0);
while (1)
// kbhit函数,非阻塞地响应键盘输入事件
if (kbhit() && getch() == ' ')
return;
// 蛇身传递
void snakeInherit(snake *p)
// p 为第一个结点,即蛇首
snake *r = p->next;
if (r->next != NULL)
snakeInherit(r);
// 把前一个结点的坐标传递给后一个结点(跟随)
r->x = p->x;
r->y = p->y;
// 吃到食物
snake *foodInMouth(snake *snakeHead, snake *food, data *curGameData)
// 蛇身长度加 1
food->next = snakeHead->next;
snakeHead->next = food;
// 新的随机食物
food = randomFood(snakeHead);
// 更新分数
curGameData->score += curGameData->foodFraction;
scoreHint(curGameData->score);
// 吃到的食物数加 1
curGameData->eatenFoods++;
return food;
// 自动加速
void autoAccelerate(data *curGameData, int isAutoAccelerate)
if (curGameData->eatenFoods % 3 == 0 && isAutoAccelerate == 1)
// 速度减半、速度等级增一、每个食物分数翻倍
curGameData->speed /= 2;
curGameData->speedLevel++;
curGameData->foodFraction *= 2;
curGameData->eatenFoods = 0;
speedHint(curGameData->speedLevel);
// 开始游戏
void startGame()
// 初始化本次游戏数据
data *curGameData = (data *) malloc(sizeof(data));
curGameData->score = 0;
curGameData->speed = 300;
curGameData->speedLevel = 1;
curGameData->foodFraction = 2;
curGameData->eatenFoods = 0;
// 是否开启自动加速:[0]不开启 [1]开启
int isAutoAccelerate = 1;
system("cls");
// 游戏地图
gameMap();
// 初始速度展示
speedHint(curGameData->speedLevel);
// 初始分数展示
scoreHint(curGameData->score);
// 初始化蛇,初始长度为 6 个节点
snake *q = initSnake();
// 初始化随机食物
snake *food = randomFood(q);
// 当前敲下的按键,默认为 d 即蛇往右移动
char hitKey = 'd';
// 上一次敲下的按键
char preKey = 'd';
while (1)
// 打印蛇身
snakeBody(q, curGameData->speed);
// 撞墙或咬到自己,游戏结束
if (collision(q))
// 游戏存档
saveGrade(curGameData->score);
// 销毁蛇身结点,释放存储空间
destroy(q);
// 结束游戏
gameOver(curGameData->score);
break;
// 键盘监听,kbhit函数,非阻塞地响应键盘输入事件
if (kbhit())
hitKey = getch();
// 敲击空格,暂停游戏
if (hitKey == ' ')
suspendGame(q);
// 恢复上一次的操作,继续游戏后按原方向爬行
hitKey = preKey;
continue;
// 兼容大写字母
hitKey = hitKey < 91 ? hitKey + 32 : hitKey;
// 蛇首撞击蛇身,游戏结束(上至下、下至上、左至右、右至左)
if ((hitKey == 'd' && preKey == 'a') || (hitKey == 's' && preKey == 'w') || (hitKey == 'a' && preKey == 'd') ||
(hitKey == 'w' && preKey == 's'))
saveGrade(curGameData->score);
destroy(q);
gameOver(curGameData->score);
break以上是关于C 语言实现经典贪吃蛇游戏的主要内容,如果未能解决你的问题,请参考以下文章