花了五个小时对着B站上的视频实现了贪吃蛇,很简陋版本的,只实现了基本步骤。5个小时里有最后两个小时都在debug,也算是在加深理解了...
当然了,虽然简陋但是核心基本都在,所以也算是略懂一二了,就想着趁热打铁总结一下顺带好好捋一下思路。
贪吃蛇这里游戏里有以下一些对象已经它们需要具备的功能,完成了这些,再借助一个主函数进行游戏运行就可以了。
一个限定的地图肯定是必不可少的,这里的地图其实要包含两个功能:
分析同上:
分析同上:
移动 -》休眠 -》判定是否吃到食物 -》判定是否死亡 -》移动
即可。#include<stdio.h>
//标准输出输入头文件,写过c的都知道,没什么好说的
//本代码中提供printf(); scanf();
#include<stdlib.h>
//standard library标准库头文件
//本代码中提供system();
#include<windows.h>
//一个很重要的头文件,包含了其他Windows的头文件,在本程序中是提供
//本代码中提供GetStdHandle(); sleep();
#include<time.h>
//C/C++中的日期和时间头文件。用于需要时间方面的函数。
//本代码中提供time();
#include<conio.h>
//控制台输入输出的简写,定义了通过控制台进行数据输入输出的函数
//本代码中提供getch();
void srand(unsigned int);
//使用无符号种子产生一系列伪随机数的起点
int rand(void);
//从srand中指定的seed开始,返回一个[seed, RAND_MAX(0x7fff))的随机整数
//在使用rand()前需要调用srand(),如果没有,则自动调用srand(1);
time_t time(time_t);
//返回从1970s到现在的秒数并保存在形参中
//用NULL做形参表示不保存在某个变量中,只返回值配合形成随机数使用,但是要强制类型转换
//如何使用time函数算出当前时间?
int kbhit(void);
//检查当前是都有键盘输入,有则返回非0值,否则返回0
int fflush(FILE*);
//清除读写缓冲区,在需要立即把输出缓冲区的数据进行物理写入时
//fflush(stdin); 为了不影响后面的数据读取
int getch(void);
//当用户按下某个字符时,无需按回车,函数自动读取,但不显示在屏幕上
HANDLE GetStdHandle(STD_OUTPUT_HANDLE);
//是一个window api,获取一个标准输出(实参)的句柄,用来标识Windows对象,个人理解是用来指定设备
void SetConsoleCursorPosition(HANDLE, COORD);
//是一个window api,用于设置控制台(cmd)的光标位置
//形参中handle指具体哪个句柄,coord指要设置的光标具体位置
Sleep(int);
//用做延时,程序暂停若干时间,即形参中输入的整数,单位是毫秒
//本代码中用来调节贪吃蛇移动速度,即移动一次中间间隔的时间
int system(char* command);
//发出一个DOS指令,形参里面就是各种指令
//system("pause"); 冻结屏幕
//system("CLS"); 清屏
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<time.h>
#include<conio.h>
//辅助宏定义
#define MAPHEIGHT 25
#define MAPWIDTH 60
#define SNAKESIZE 50
#define SNAKESPPED 100
//全局变量
int key = ‘w‘; //初始化移动方向
int changeFlag = 0; //蛇是否要变化,如果吃了食物则这个循环内先不变化
int score = 0;
//食物的结构体
struct {
int x;
int y;
}food;
//蛇的结构体
struct {
//用数组是记录每一节蛇的坐标
int x[SNAKESIZE];
int y[SNAKESIZE];
//蛇的长度
int len;
//蛇的移动速度
int speed;
}snake;
//模块化设计(函数设计) --- 》》功能的划分 ---》》抽象到函数
void drawMap(); //画地图
void createFood(); //产生食物
void keyDown(); //按键操作
int snakeStatus(); //蛇的状态,判断是否结束游戏
void gotoxy(int x, int y); //辅助函数:光标移动
void drawMap() {
//☉:食物 ■:蛇身体
srand((unsigned int)time(NULL)); //随机函数种子
//1、圈地
//1、1左右边框
for (int i = 0; i <= MAPHEIGHT; i++) {
gotoxy(0, i);
printf("■");
gotoxy(MAPWIDTH, i);
printf("■");
}
//2、2上下边框
for (int i = 0; i < MAPWIDTH; i += 2) { //横向每个占用两个字符,所以是+=2
gotoxy(i, 0);
printf("■");
gotoxy(i, MAPHEIGHT);
printf("■");
}
//2、画蛇
//2、1确定蛇的属性(长度、速度、坐标)
snake.len = 3;
snake.speed = SNAKESPPED;
//开始蛇头在屏幕正中间
snake.x[0] = MAPWIDTH / 2;
snake.y[0] = MAPHEIGHT / 2;
//2、2画蛇
//画蛇头
gotoxy(snake.x[0], snake.y[0]);
printf("■");
//画蛇身
for (int i = 1; i < snake.len; i++) {
snake.x[i] = snake.x[i - 1] + 2; //从蛇头开始,下一个身位是上一个身位的横坐标+2
snake.y[i] = snake.y[i - 1]; //下一个身位与上一个身位的纵坐标是相同的,这表示蛇的初试状态是横着放的
gotoxy(snake.x[i], snake.y[i]); //开始到位置上逐个画蛇身
printf("■");
}
//3、画食物
//3、1确定坐标
while (1) {
food.x = rand() % (MAPWIDTH - 4) + 2; //去掉边界,横向每个边界占2,所以都是以2为单位的
food.y = rand() % (MAPHEIGHT - 2) + 1; //去掉边界,纵向每个边界占1,所以都是以1为单位的
if (food.x % 2 == 0)
break;
}
//3、2画出食物
gotoxy(food.x, food.y);
printf("☉");
}
void createFood() {
//蛇把食物吃了
if (snake.x[0] == food.x && snake.y[0] == food.y) {
score++;
//随机产生新的食物
srand((unsigned int)time(NULL));
while (1) {
int flag = 1;
food.x = rand() % (MAPWIDTH - 4) + 2;
food.y = rand() % (MAPHEIGHT - 2) + 1;
//且产生的食物不能在蛇的身上
for (int i = 0; i < snake.len; i++) {
if (snake.x[i] == food.x && snake.y[i] == food.y) { //利用循环判断新产生的食物是不是和蛇在一起
flag = 0; //表示食物不合适,重新产生
break;
}
}
if (flag && food.x % 2 == 0) { //因为横坐标占两个字符,所以要横坐标要是偶数,显示完整
break;
}
}
gotoxy(food.x, food.y);
printf("☉");
snake.len++;
changeFlag = 1; //蛇的标记是1,表示蛇这一个循环中先不变化
}
}
void keyDown() {
//无按键的操作
if (_kbhit()) { //检测有无按键输入
fflush(stdin); //清空输入缓冲区,通常是为了确保不影响后面的数据读取,例如在读完一个字符串后紧接着又要读取一个字符
key = _getch();
}
//擦除最后的位置
if (!changeFlag) { //如果吃了食物就不擦除,然后蛇长度+1,达到长度变长的目的
gotoxy(snake.x[snake.len - 1], snake.y[snake.len - 1]);
printf(" ");
}
//后面的蛇身
for (int i = snake.len - 1; i > 0; i--) {
snake.x[i] = snake.x[i - 1]; //后一个变成前一个的状态,所以在往前移
snake.y[i] = snake.y[i - 1]; //不能用i--,因为会对i造成变成
}
//方向的处理以及对蛇头的处理
switch (key) {
case ‘W‘:
case ‘w‘:
snake.y[0]--;
break;
case ‘S‘:
case ‘s‘:
snake.y[0]++;
break;
case ‘A‘:
case ‘a‘:
snake.x[0] -= 2;
break;
case ‘D‘:
case ‘d‘:
snake.x[0] += 2;
break;
}
gotoxy(snake.x[0], snake.y[0]);
printf("■");
changeFlag = 0;
gotoxy(MAPHEIGHT + 2, 0); //移动不能一直看着光标
}
int snakeStatus() {
//不能撞墙
if (snake.x[0] == 0 || snake.x[0] == MAPWIDTH - 2 || snake.y[0] == 0 || snake.y[0] == MAPHEIGHT - 1) //有差异,要注意
return 0;
//不能撞自己
for (int i = 1; i < snake.len; i++) {
if (snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i])
return 0;
}
return 1;
}
void gotoxy(int x, int y) {
//1、找到控制台的窗口
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
//2、光标的结构体
COORD coord;
//3、设置光标坐标
coord.X = x;
coord.Y = y;
//4、同步到控制台SetConsoleCursorPosition
SetConsoleCursorPosition(handle, coord);
}
int main() {
char t;
while (1) {
system("cls");
drawMap();
score = 0;
while (1) {
createFood();
Sleep(snake.speed); //使计算机程序(进程,任务或线程)进入休眠,使其在一段时间内处于非活动状态,形参单位是毫秒,即休眠时间
keyDown();
if (!snakeStatus()) {
break;
}
}
//gotoxy(MAPWIDTH / 2, MAPHEIGHT / 2);
printf("GameOver\t\n");
printf("Your Score is %d\n", score);
printf("Want Restart?(Y/N)\t");
scanf_s ("%c", &t);
if (t == ‘n‘)
break;
}
gotoxy(MAPWIDTH / 2, MAPHEIGHT / 2);
printf("EXIT");
system("pause");
return 0;
}
原文:https://www.cnblogs.com/Za-Ya-Hoo/p/12680401.html