基于EasyX库的C语言小游戏
文章目录
- 基于EasyX库的C语言小游戏
- 前言
- 游戏实录
- 需求与技术难点分析
- 地图与蛇的生成
- 蛇的移动
- 游戏失败
- 源码
前言
上一篇文章我们已经做过了见缝插针的小游戏,这次再利用EasyX库做一个贪吃蛇的游戏。
游戏实录
需求与技术难点分析
地图与蛇的生成
利用二维数组,这时我们就可以想到,二维数组的值的多态性,值的多态决定了地图里不止一种的表示,我们可以想到用0表示空地,>0的为蛇身。
同时,一个像素点太小了,我们可以用宏定义的方法手动设置像素块
#define BLOCK_SIZE 20 // 每个小格子的长宽大小
#define HEIGHT 30 // 高度上一共30个小格子
#define WIDTH 40 // 宽度上一共40个小格子
蛇的移动
我们从键盘获取蛇的移动方向,同时初始方向为右,为了防止出现因为前一步向上后一步向下而导致的自己咬自己的bug,我们除了定义moveDirection的同时,还定义moveDirection_old变量,用于避免这种现象的产生。
同时因为蛇本身会按照初始方向移动,键盘输入是在某一个时间点进行,因此我们将这两个事件封装为两个更新函数,一个与输入无关,另一个与输入有关。
蛇的移动本质上是数组的更新,我们在第一步设置的蛇身的值>0,由此我们可以发挥一下,将蛇头的值设置为1,将蛇身的值设置为蛇的长度,因此,数组中值最大的元素下标就对应着蛇尾,值最小的元素下标就对应着蛇头。而蛇更新的时候本质上移动的只有头和尾(如果吃到了食物的话,就只用移动头)。
游戏失败
游戏失败分为两种情况:
- 蛇头碰壁
这种情况我们只需要将蛇头坐标与边界值相比较就行 - 咬到了蛇身
我们需要将下一步对应的蛇头坐标对应数组中的值进行检查,判断是否大于零,大于0则咬到了自身
源码
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#define BLOCK_SIZE 20 // 每个小格子的长宽大小
#define HEIGHT 30 // 高度上一共30个小格子
#define WIDTH 40 // 宽度上一共40个小格子 // 全局变量定义
int Blocks[HEIGHT][WIDTH] = { 0 }; // 二维数组,用于记录所有的游戏数据
char moveDirection_old, moveDirection; // 小蛇移动方向
int food_i, food_j; // 食物的位置
int isFailure = 0; // 是否游戏失败void moveSnake() // 移动小蛇及相关处理函数
{int i, j;for (i = 0; i < HEIGHT; i++) // 对行遍历 for (j = 0; j < WIDTH; j++) // 对列遍历if (Blocks[i][j] > 0) // 大于0的为小蛇元素 Blocks[i][j]++; // 让其+1int oldTail_i, oldTail_j, oldHead_i, oldHead_j; // 定义变量,存储旧蛇尾、旧蛇头坐标 int max = 0; // 用于记录最大值 for (i = 0; i < HEIGHT; i++) // 对行列遍历{for (j = 0; j < WIDTH; j++){if (max < Blocks[i][j]) // 如果当前元素值比max大{max = Blocks[i][j]; // 更新max的值oldTail_i = i; // 记录最大值的坐标,就是旧蛇尾的位置oldTail_j = j; // }if (Blocks[i][j] == 2) // 找到数值为2 {oldHead_i = i; // 数值为2恰好是旧蛇头的位置oldHead_j = j; // }}}int newHead_i = oldHead_i; // 设定变量存储新蛇头的位置int newHead_j = oldHead_j;// 根据用户按键,设定新蛇头的位置if (moveDirection == 'w') // 向上移动newHead_i = oldHead_i - 1;else if (moveDirection == 's') // 向下移动newHead_i = oldHead_i + 1;else if (moveDirection == 'a') // 向左移动newHead_j = oldHead_j - 1;else if (moveDirection == 'd') // 向右移动newHead_j = oldHead_j + 1;// 如果蛇头超出边界,或者蛇头碰到蛇身,游戏失败if (newHead_i >= HEIGHT || newHead_i < 0 || newHead_j >= WIDTH || newHead_j < 0|| Blocks[newHead_i][newHead_j]>0){isFailure = 1; // 游戏失败return; // 函数返回}Blocks[newHead_i][newHead_j] = 1; // 新蛇头位置数值为1 if (newHead_i == food_i && newHead_j == food_j) // 如果新蛇头正好碰到食物{food_i = rand() % (HEIGHT) ; // 食物重新随机位置food_j = rand() % (WIDTH ); // // 不对旧蛇尾处理,相当于蛇的长度+1}else // 新蛇头没有碰到食物Blocks[oldTail_i][oldTail_j] = 0; // 旧蛇尾变成空白,不吃食物时保持蛇的长度不变
}void startup() // 初始化函数
{int i;Blocks[HEIGHT / 2][WIDTH / 2] = 1; // 画面中间画蛇头,数字为1for (i = 1; i <= 4; i++) // 向左依次4个蛇身,数值依次为2,3,4,5Blocks[HEIGHT / 2][WIDTH / 2 - i] = i + 1;moveDirection = moveDirection_old= 'd'; // 初始向右移动food_i = rand() % (HEIGHT) ; // 初始化随机食物位置food_j = rand() % (WIDTH ); // initgraph(WIDTH * BLOCK_SIZE, HEIGHT * BLOCK_SIZE); // 新开画面setlinecolor(RGB(200, 200, 200)); // 设置线条颜色BeginBatchDraw(); // 开始批量绘制
}void show() // 绘制函数
{cleardevice(); // 清屏int i, j;for (i = 0; i < HEIGHT; i++) // 对二维数组所有元素遍历{for (j = 0; j < WIDTH; j++){if (Blocks[i][j] > 0) // 元素大于0表示是蛇,这里让蛇的身体颜色色调渐变setfillcolor(HSVtoRGB(Blocks[i][j] * 10, 0.9, 1));elsesetfillcolor(RGB(150, 150, 150)); // 元素为0表示为空,颜色为灰色// 在对应位置处,以对应颜色绘制小方格fillrectangle(j * BLOCK_SIZE, i * BLOCK_SIZE,(j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE);}}setfillcolor(RGB(0, 255, 0)); // 食物为绿色// 绘制食物小方块fillrectangle(food_j * BLOCK_SIZE, food_i * BLOCK_SIZE,(food_j + 1) * BLOCK_SIZE, (food_i + 1) * BLOCK_SIZE);if (isFailure) // 如果游戏失败{setbkmode(TRANSPARENT); // 文字字体透明 settextcolor(RGB(255, 0, 0));// 设定文字颜色settextstyle(80, 0, _T("宋体")); // 设定文字大小、样式outtextxy(240, 220, _T("游戏失败")); // 输出文字内容}FlushBatchDraw(); // 批量绘制
}void updateWithoutInput() // 与输入无关的更新函数
{if (isFailure) // 如果游戏失败,函数返回return;static int waitIndex = 1; // 静态局部变量,初始化时为1waitIndex++; // 每一帧+1if (waitIndex == 10) // 如果等于10才执行,这样小蛇每隔10帧移动一次{moveSnake(); // 调用小蛇移动函数waitIndex = 1; // 再变成1}
}void updateWithInput() // 和输入有关的更新函数
{if (_kbhit() && isFailure == 0) // 如果有按键输入,并且不失败{char input = _getch(); // 获得按键输入switch (input) { //注意防止自咬现象(即前后两次运动方向相反)case 'a':moveDirection = input; if (moveDirection_old != 'd') {moveSnake();moveDirection_old = moveDirection;}else moveDirection = moveDirection_old;break;case 'w':moveDirection = input; if (moveDirection_old != 's') {moveSnake(); moveDirection_old = moveDirection;}else moveDirection = moveDirection_old;break;case 's':moveDirection = input; if (moveDirection_old != 'w') {moveSnake();moveDirection_old = moveDirection;}else moveDirection = moveDirection_old;break;case 'd':moveDirection = input; if (moveDirection_old != 'a') {moveSnake();moveDirection_old = moveDirection;}else moveDirection = moveDirection_old;break;}}
}int main() // 主函数
{startup(); // 初始化函数,仅执行一次 while (1) // 一直循环{show(); // 进行绘制updateWithoutInput(); // 和输入无关的更新 updateWithInput(); // 和输入有关的更新}return 0;
}