C语言基于EsayX的贪吃蛇代码实现及其思路

[声明] 大二非专业学生拙作, 写出来只是为了整理自己的思路

此项目基于C语言EsayX函数库,运行环境VS2022

功能模块

  1. 游戏画面初始化, 刷新函数
  2. 蛇的身体坐标更改, 蛇身刷新函数
  3. 食物刷新函数
  4. 碰撞检测函数
  5. 键盘输入响应函数

思路

众所周知, 游戏有刷新率这样的说法, 也就是1s内画面更新的次数, 这也是在设计游戏时的核心思路, 即: 在一次刷新间隔内, 使不同的函数模块互相配合工作, 使得在下一次刷新后画面出现相应的更改, 往复循环, 即构成了动态的游戏画面

这里, 笔者使用了while(1)无限循环, 并搭配延迟响应函数Sleep()来控制画面刷新率

main()函数中, 不同功能模块之间的调用顺序是一大重点, 错误的调用顺序可能导致意料之外的变动(如食物不能正常刷新, 蛇身缺失等, 我都遇到过)

代码实现

/*贪吃蛇项目实战*/
#include <stdio.h>
#include <graphics.h> //用于图形界面绘制 EsayX
#include <conio.h>
#include <windows.h> //延迟函数sleep()库
#include <time.h>

#define TIME 80  //游戏刷新率(ms)

/*蛇的坐标参数, 长度, 初始方向*/
#define DIRECTION up // 方向
#define MAXSIZE 50	//最大蛇身长度

struct Snake {
	int x[MAXSIZE];
	int y[MAXSIZE];
	int length; //当前长度
	int speed;
	int direction;
};

struct Food {
	int x;
	int y;
	int r = 6;
};

enum Direction
{
	up = 1,
	down ,
	left ,
	right 
};
void CreatFood();

/*界面初始化函数*/
#define WIDTH 640
#define HEIGHT 480
void SetMap() {
	initgraph(WIDTH, HEIGHT);
	setbkcolor(RGB(65, 131, 168));
	cleardevice();
}

/*初始化蛇 并 画出蛇的基本部分*/
struct Snake snake;

void SetSnake() {
	snake.x[0] = WIDTH / 2;
	snake.y[0] = HEIGHT / 2;
	snake.length = 18;
	snake.speed = 10;
	snake.direction = right;

	setfillcolor(RGB(0, 0, 0)); //设置填充内容的颜色
	for (int i = 0; i < snake.length; i++) { //遍历填充数组坐标
		snake.x[i] = snake.x[0] - i * 10; //初始向左填充
		snake.y[i] = snake.y[0];
		printf("done\n");
		fillrectangle(snake.x[i], snake.y[i], snake.x[i] + 10, snake.y[i] + 10); //起始坐标是
	}
}

void MoveSnake() {
	for (int i = snake.length - 1; i > 0; i--) {
		snake.x[i] = snake.x[i - 1]; //从蛇尾开始,依次获取前一个节点的坐标
		snake.y[i] = snake.y[i - 1];
	}
}

/*用于每次刷新蛇的身体*/
wchar_t str3[100] = L"refresh run";
void refresh() {
	BeginBatchDraw();
	setbkcolor(RGB(65, 131, 168));
	
	cleardevice();
	
	setfillcolor(RGB(0, 0, 0));
	for (int i = 0; i < snake.length; i++) {
		fillrectangle(snake.x[i], snake.y[i], snake.x[i] + 10, snake.y[i] + 10); //左和上做定位,画一个10*10的矩形

		fillcircle(snake.x[i], snake.y[i], 2);

	}
	EndBatchDraw();
	outtextxy(10, 10, str3);
}

/*从键盘获取输入,转化为枚举类型并返回 将key转换为枚举*/
void keyInput() {
	char key = _getch();
	switch (key) {
	case 'w': 
		if (snake.direction == down) { //当前方向为下时,禁止向上转
			break;
		}
		snake.direction = up;
		break;
	case 'a':
		if (snake.direction == right) { //下面同理
			break;
		}
		snake.direction = left;
		break;
	case 's':
		if (snake.direction == up) {
			break;
		}
		snake.direction = down;
		break;
	case 'd':
		if (snake.direction == left) {
			break;
		}
		snake.direction = right;
		break;
	default: break;
	}
 }
/*根据键盘的输入输出对应的控制*/
void Controller() {
	switch (snake.direction) {
	case up: 
		snake.y[0] -= snake.speed;
		break;
	case down:
		snake.y[0] += snake.speed;
		break;
	case left:
		snake.x[0] -= snake.speed;
		break;
	case right:
		snake.x[0] += snake.speed;
		break;
	}
}

/*随机生成食物的坐标 并 创建食物*/
Food food = { 0,0 };
int food_flag = 0;
void CreatFood() {
	if (food_flag == 0) {
		srand((unsigned int)time(NULL)); //设置时间随机种子,以系统时间戳作为随机数
		food.x = rand() % (int)(WIDTH / 1.25) + 32;  //生成width 80%的大小,并向右平移5% 范围(32, 544) (5%, 85%)
		food.y = rand() % (int)(HEIGHT / 1.25) + 24; //同理 (24, 408) (5%, 85%)
		food_flag = 1;

		Sleep(500); //当吃到食物时延迟0.5s
	}
	BeginBatchDraw();
	setfillcolor(RGB(255,36,0));
	fillcircle(food.x, food.y, food.r);
	EndBatchDraw();
}

wchar_t str[100] = L"CrashTest run";
/*食物碰撞检测*/
struct Flag { //设置蛇头的中点为判定点
	int x;
	int y;
};
struct Flag flag;
void CrashTest() {
	flag = {
		(snake.x[0] + snake.x[0] + 10) / 2 ,
		(snake.y[0] + snake.y[0] + 10) / 2
	};
	setfillcolor(RED);
	fillcircle(flag.x, flag.y, 2); //蛇头的判定点

	if (flag.x >= food.x - food.r && flag.x <= food.x + food.r &&
		flag.y >= food.y - food.r && flag.y <= food.y + food.r && food_flag == 1) {
		snake.length++;
		food_flag = 0;
		setfillcolor(RGB(0, 0, 0));
		outtextxy(10, 10, str);
	}
	
}

/*死亡检测*/
wchar_t str2[100] = L"deathTest run";
bool deathTest() {
	if ((flag.x < 0 || flag.x > WIDTH) ||
		(flag.y < 0 || flag.y > HEIGHT)) {
		return TRUE;
		outtextxy(10, 10, str2);
	}
	for (int i = 1; i < snake.length; i++) {
		if (snake.x[0] == snake.x[i] &&
			snake.y[0] == snake.y[i]) {
			return TRUE;
		}
	}
	
	return FALSE;
}


/*
===============
注: while()循环中的函数运行顺序不可更改
===============
*/
int main() {
	
	
	SetMap();
	SetSnake();

	Direction key = right; //默认方向
	/*循环体,不断刷新界面*/
	while (1) {
		refresh(); //更新画面,根据当前数组坐标绘制蛇身(必须放最上面)
		
		
		CrashTest(); //碰撞检测应该放在controller()前面,因为用到了头坐标

		if (deathTest()) { //死亡碰撞判断
			break;
		}
		if (_kbhit()) { //键盘输入检测
			keyInput();
		}
		MoveSnake(); //移动蛇身坐标,不更新画面 注意movesnake必须放在controller前面

		Controller(); //注意放在if外面 不断更改蛇头坐标,不更新画面
		
		

		CreatFood(); //建立食物坐标,更新食物画面
		//refresh放在creatfood下面会导致食物不能被刷新,或者是覆盖(未解决) <<<=================
		
		
		Sleep(TIME);
	}

	wchar_t endMessage[30] = L"Game Over, Any keys to quit";
	outtextxy(40, 40, endMessage);
	if (_getch()) {
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值