原文链接:C语言 贪吃蛇游戏

文章目录

  • 一、说明
  • 二、效果
    • 2.1 欢迎界面
    • 2.2 游戏规则
    • 2.3 得分排行
    • 2.4 退出游戏
    • 2.5 游戏界面
    • 2.6 游戏结束
  • 三、源码
    • 3.1 cmd.h
    • 3.2 cmd.c
    • 3.3 io.h
    • 3.4 io.c
    • 3.5 model.h
    • 3.6 service.h
    • 3.7 service.c
    • 3.8 ui.h
    • 3.9 ui.c
    • 3.10 utils.h
    • 3.11 utils.c
    • 3.12 main.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(&timestamp);struct tm *ti = localtime(&timestamp);// 为当前游戏数据分配空间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) {// 蛇身长度加 1food->next = snakeHead->next;snakeHead->next = food;// 新的随机食物food = randomFood(snakeHead);// 更新分数curGameData->score += curGameData->foodFraction;scoreHint(curGameData->score);// 吃到的食物数加 1curGameData->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;}// 按 q 加速if (hitKey == 'q') {// 速度减半、速度等级增一、每个食物分数翻倍、不再自动加速curGameData->speed /= 2;curGameData->speedLevel++;curGameData->foodFraction *= 2;curGameData->eatenFoods = 0;isAutoAccelerate = 0;speedHint(curGameData->speedLevel);// 恢复上一次的操作,加速后按原方向爬行hitKey = preKey;continue;}// 按 e 减速if (hitKey == 'e') {// 速度翻倍、速度等级减一、每个食物分数减半、不再自动加速curGameData->speed *= 2;curGameData->speedLevel--;curGameData->foodFraction /= 2;curGameData->eatenFoods = 0;isAutoAccelerate = 0;speedHint(curGameData->speedLevel);// 恢复上一次的操作,减速后按原方向爬行hitKey = preKey;continue;}// 上if (hitKey == 'w') {// 吃到食物,蛇身长度加 1,创建新食物,更新分数if (q->next->x == food->x && q->next->y - 1 == food->y) {food = foodInMouth(q, food, curGameData);autoAccelerate(curGameData, isAutoAccelerate);} else {// 未吃到食物,传递上一次的舍身snakeInherit(q->next);q->next->y -= 1;}}// 下if (hitKey == 's') {if (q->next->x == food->x && q->next->y + 1 == food->y) {food = foodInMouth(q, food, curGameData);autoAccelerate(curGameData, isAutoAccelerate);} else {snakeInherit(q->next);q->next->y += 1;}}// 左if (hitKey == 'a') {if (q->next->x - 2 == food->x && q->next->y == food->y) {food = foodInMouth(q, food, curGameData);autoAccelerate(curGameData, isAutoAccelerate);} else {snakeInherit(q->next);q->next->x -= 2;}}// 右if (hitKey == 'd') {if (q->next->x + 2 == food->x && q->next->y == food->y) {food = foodInMouth(q, food, curGameData);autoAccelerate(curGameData, isAutoAccelerate);} else {snakeInherit(q->next);q->next->x += 2;}}// 记录上一次的操作preKey = hitKey;}
}

3.8 ui.h

#pragma once// 界面边框
void frame(int type);// 欢迎页
void welcomePage();// 游戏规则
void gameRules();// 游戏结束
void gameOver(int score);// 游戏地图
void gameMap();// 速度提示
void speedHint(int speedLevel);// 输出分数
void scoreHint(int score);

3.9 ui.c

// 界面边框
void frame(int type) {// 上边框setPox(17, 5);setTextColor(11);printf("⊙--------------------------");setTextColor(14);printf("oOOo");setTextColor(11);printf("----------");setTextColor(14);printf("(_)");setTextColor(11);printf("----------");setTextColor(14);printf("oOOo");setTextColor(11);printf("--------------------------⊙");// 左右竖边框for (int i = 6; i <= 19; i++) {setPox(17, i);printf("§");setPox(102, i);printf("§");}// 下边框setPox(17, 20);printf("⊙---------------------------------------");setTextColor(14);printf("☆☆☆");setTextColor(11);printf("--------------------------------------⊙");setPox(53, 23);printf("∵ˇˇˇˇˇˇˇ∵");setPox(53, 26);printf("∴^^^^^^^∴");// 启动页面字符图案if (type == 0) {setPox(57, 2);setTextColor(6);printf("\\\\\\|///");setPox(54, 3);printf("\\\\");setPox(58, 3);setTextColor(15);printf(".-.-");setPox(65, 3);setTextColor(6);printf("//");setPox(55, 4);setTextColor(14);printf("(");setPox(58, 4);setTextColor(15);printf(".@.@");setPox(65, 4);setTextColor(14);printf(")");} else {// 游戏结束字符图案setPox(57, 1);setTextColor(6);printf("∧    ∧");setPox(55, 2);printf(" /  \\  /  \\");setPox(54, 3);printf("( ︹ ˇ ︹ )");setPox(54, 4);printf("く ");setTextColor(15);printf("⊙    ⊙");setTextColor(14);printf(" / ");setPox(55, 5);printf("く   い   /");setPox(57, 6);printf("く 々 √");setPox(60, 7);printf("ˇ");}
}// 欢迎页
void welcomePage() {system("cls");setPox(53, 8);setTextColor(14);printf("贪 吃 蛇 大 作 战");setPox(26, 14);printf("1.开始游戏");setPox(46, 14);printf("2.游戏规则");setPox(66, 14);printf("3.得分排行");setPox(86, 14);printf("4.退出游戏");// 绘制界面边框frame(0);setPox(56, 24);setTextColor(14);printf("前往:");
}// 游戏规则
void gameRules() {system("cls");setPox(55, 5);printf("游戏规则");setTextColor(12);setPox(34, 8);printf("1. 'W''S''A''D' 控制上、下、左、右方向,空格键控制游戏暂停与继续");setPox(34, 10);printf("2. 按 Q 键可加速,按 E 键可减速,速度越快,单个食物分数越高");setPox(34, 12);printf("3. 在没有人为加速或减速的情况下,每吃到 3 个食物将会进行一次自动加速");setPox(34, 14);printf("4. 初始化每个食物 2 分,每加速一次单个食物分数翻倍,减速一次分数减半");setPox(34, 16);printf("5. 初始化小蛇长度为 6,每吃到一个食物长度就会加 1,分数相应地增加");setPox(34, 18);printf("6. 小蛇的初始速度为 300MS/格,速度等级为 1,等级值越高速度越快");setPox(34, 20);printf("7. 当蛇首撞墙或咬到蛇身时游戏结束");setPox(34, 22);printf("8. 以上按键皆不区分大小写");setPox(34, 24);setTextColor(1);printf("注:按任意键继续···");
}// 游戏结束
void gameOver(int score) {int choice = 1;gotoHere:system("cls");setTextColor(12);setPox(45, 8);printf("游戏结束,蛇首撞墙或咬到蛇身\n");setPox(58, 12);setTextColor(14);printf("得分:%d", score);setTextColor(14);setPox(40, 16);printf("1.重新开始");setPox(56, 16);printf("2.返回主页");setPox(70, 16);printf("3.退出游戏");// 绘制界面边框frame(1);setPox(56, 24);printf("前往:");scanf("%d", &choice);switch (choice) {case 1:system("cls");startGame();break;case 2:return;case 3:system("cls");setPox(50, 15);printf("游戏结束,欢迎再次登录");setPox(0, 0);exit(0);default:setPox(0, 0);printf("您的输入有误,请重新输入!按任意键继续···");getch();goto gotoHere;}
}// 游戏地图
void gameMap() {setTextColor(11);// 游戏界面边框:菱形边界,方块背景for (int i = 2; i < 27; i++) {// 左边框setPox(2, i);printf("◆");// 方块背景setTextColor(3);for (int j = 0; j < 29; j++)printf("■");// 右边框setTextColor(11);printf("◆");}// 上边框setPox(2, 1);for (int i = 0; i < 31; i++) {printf("◆");}// 下边框setPox(2, 27);for (int i = 0; i < 31; i++) {printf("◆");}// 游戏界面与游戏提示间隔框setTextColor(10);for (int i = 0; i < 30; i++) {setPox(70, i);printf("§");}// 得分提示边框setTextColor(6);setPox(82, 4);printf("∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞");setPox(82, 6);printf("∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞");setPox(82, 5);printf("φ");setPox(110, 5);printf("φ");// 操作提示setTextColor(12);setPox(94, 9);printf("操作提示");setPox(95, 11);printf("上:W");setPox(95, 12);printf("下:S");setPox(95, 13);printf("左:A");setPox(95, 14);printf("右:D");setPox(90, 16);printf("暂停/继续: 空格");// 速度提示框setTextColor(14);setPox(82, 19);printf("∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞");setPox(82, 21);printf("∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞");setPox(81, 20);printf("φ");setPox(111, 20);printf("φ");setPox(81, 23);printf("注:按 Q 键可加速,按 E 键可减速");
}// 速度提示
void speedHint(int speedLevel) {setTextColor(11);setPox(97, 20);printf("%d", speedLevel);
}// 输出分数
void scoreHint(int score) {setPox(97, 5);setTextColor(13);printf("%d", score);
}

3.10 utils.h

#pragma once// 比较两个元素
int compare(const void *a, const void *b);

3.11 utils.c

// 比较两个元素
int compare(const void *a, const void *b) {return (*(int *) b - *(int *) a);
}

3.12 main.c

#define _CRT_SECURE_NO_WARNINGS#include<stdio.h>#include "model.h"
#include "cmd.h"
#include "utils.h"
#include "ui.h"
#include "io.h"
#include "service.h"/** 程序入口*/
int main() {int choice;gotoHere:// 启动页面welcomePage();scanf("%d", &choice);switch (choice) {case 1:system("cls");startGame();goto gotoHere;case 2:gameRules();getch();goto gotoHere;case 3:rankingList();getch();goto gotoHere;case 4:system("cls");setPox(50, 15);printf("游戏结束,欢迎再次登录");setPox(0, 0);exit(0);default:setPox(0, 0);printf("您的输入有误,请重新输入!按任意键继续···");getch();goto gotoHere;}return 0;
}

C 语言实现经典贪吃蛇游戏相关推荐

  1. 好玩的c语言编程游戏,C语言写个贪吃蛇游戏

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 C语言写个贪吃蛇游戏 贪吃蛇是个非常经典的游戏,用C语言来实现也是一个好玩的事情.这个游戏我写完后放在知乎,竟然点赞的人数超级多.我觉得大家喜欢,一个方面 ...

  2. 使用Python实现经典贪吃蛇游戏

    先上效果图: 目录 一.简介 二.项目目的 三.turtle的详细介绍 3.1.简单介绍turtle.Screen() 3.2.简单介绍turtle.Turtle() 四.项目源码 五.总结 一.简介 ...

  3. 【C语言项目】贪吃蛇游戏(上)

    00. 目录 文章目录 00. 目录 01. 开发背景 02. 功能介绍 03. 欢迎界面设计 3.1 常用终端控制函数 3.2 设置文本颜色函数 3.3 设置光标位置函数 3.4 绘制字符画(蛇) ...

  4. 贪吃蛇c语言课程设计报告下载,c语言课程设计-贪吃蛇游戏

    c语言课程设计-贪吃蛇游戏 C 语言课程设计 1 山东交通学院 C 语言课程设计 贪吃蛇游戏 院(系)别 交通与物流工程学院 班 级 物联 131 学 号 130516122 姓 名 王赛 指导教师 ...

  5. bat贪吃蛇游戏代码_C语言写个贪吃蛇游戏

    贪吃蛇是个非常经典的游戏,用C语言来实现也是一个好玩的事情.这个游戏我写完后放在知乎,竟然点赞的人数超级多.我觉得大家喜欢,一个方面是因为写得简单,大家都能看得懂,一个可扩展性还是非常强的. 我试了说 ...

  6. C语言写个贪吃蛇游戏

    贪吃蛇是个非常经典的游戏,用C语言来实现也是一个好玩的事情.这个游戏我写完后放在知乎,竟然点赞的人数超级多.我觉得大家喜欢,一个方面是因为写得简单,大家都能看得懂,一个可扩展性还是非常强的. 我试了说 ...

  7. x x x x c程序语言,C语言写个贪吃蛇游戏

    贪吃蛇是个非常经典的游戏,用C语言来实现也是一个好玩的事情.这个游戏我写完后放在知乎,竟然点赞的人数超级多.我觉得大家喜欢,一个方面是因为写得简单,大家都能看得懂,一个可扩展性还是非常强的. 我试了说 ...

  8. 用C语言复现用贪吃蛇游戏(二)完结

    书接上回,咱们复现了贪吃蛇的页面,接下来是对游戏的操作进行复现: //移动小蛇 //第一步扫描数组canvas的所有元素,找到正数元素都加1 //找到最大元素(蛇尾巴),把其变为0 //找到等于2的元 ...

  9. 用c语言写一个贪吃蛇游戏代码

    首先,贪吃蛇是一个经典的游戏,主要通过控制一条蛇在地图上吃食物,不断长长身体,最终实现最高分数. 以下是使用C语言编写贪吃蛇游戏的示例代码: ``` #include<stdio.h> # ...

最新文章

  1. poj1286(纯Polya定理)
  2. C语言 · 分糖果
  3. debian中添加sudo命令
  4. (八)OpenStack---M版---双节点搭建---Cinder安装和配置
  5. Oulipo(kmp算法)
  6. 熟悉Redhat 9.0
  7. 1、python基础速成
  8. php变量的数据类型
  9. STM32——EEPROM
  10. Sobel算子及cvSobe
  11. 深入解析:你听说过Oracle数据库的更新重启动吗?
  12. (39)FPGA面试题Verilog实现卖报机
  13. SpringBoot前端Ajax以JSON格式获取后台数据
  14. OneNote中到底能放多少种东西?
  15. Java 内部类、局部内部类、静态内部类使用
  16. hdu 1698 线段树成段更新
  17. 用粉红噪声煲机_解析什么是白噪和粉噪?耳机煲机用白噪好还是粉噪
  18. 分层采样(stratified sampling)
  19. wps交叉表_WPS交叉引用的使用方法
  20. python 魔法方法什么时候用_python学习之魔法方法的调用

热门文章

  1. 网络搭建与应用2022国赛环境无误版
  2. 用STM32F105双CAN激活众泰汽车中控屏投射
  3. java 双声道音频_一种多声道录音的实现方法(语音识别功能)
  4. Win10自带的邮件email上登录qq邮箱|解决无法访问此账户:xx问题
  5. 计算机无法外接投影,笔记本电脑连接投影仪无信号解决步骤
  6. 全国淘宝村数量已超2100个 阿里巴巴打造乡村振兴新样本
  7. 《你一学就会的-思维大图》读书笔记
  8. matlab怎么数值模拟。,matlab的数值模拟方法.doc
  9. APP活动运营:应关注哪些数据指标?
  10. java求最大公约数_java求最大公约数(分解质因数)