因为接触到了 bpibit 的 LED 矩阵,所以萌生了写出一个贪吃蛇小游戏
记录一下编写程序的过程以及思考,毕竟还是一只菜鸟,所以在程序逻辑、代码优化上还是有些不足,希望有大佬能够指出,我会认真学习大佬给的建议
这篇文章是以上一篇文章为基础写的,所以有兴趣可以看我上一篇文章ESP32: BPI-BIT 开发板外设 按键与LED矩阵 学习(Arduino)
我把贪吃蛇小游戏分为几个部分
测试使用的开发板: bpi-bit v1.2
应用到的外设: WS2812b
测试使用的软件:PlatformIO
因为在void SetPixelColor(uint16_t indexPixel, typename T_COLOR_FEATURE::ColorObject color)
中,输入的是面板的整数,不利于后面蛇方向移动程序的编写,所以写了一个将(x,y)矩阵变为整数编号形式的函数
// 将(x,y)变为整数编号
int Count(int x, int y)
{
return (x - 1) + (y - 1) * 5;
}
这样就把板子的 LED 面板变成了 XY 矩阵的形式,整个程序都是以 XY 矩阵为基础的
class Snake
{
public:
int snakeLen = 3; // 蛇的长度
// 蛇身体的位置
int *snakeLenX = new int[snakeLen];
int *snakeLenY = new int[snakeLen];
int hx = 3, hy = 3; // 头部位置
int tx = 1, ty = 3; // 尾部位置
bool IsSnakeCollision(int hx, int hy); // 判断蛇是否碰撞
void updateSnakePosition(bool appleFlag,int tx,int ty); // 更新蛇的位置
bool Key_Scan(); // 扫描按键
void DirectionAndCount(); // 蛇运动方向判断
// 开局一条蛇的位置
Snake()
{
snakeLenX[0] = 1;
snakeLenX[1] = 2;
snakeLenX[2] = 3;
snakeLenY[0] = 3;
snakeLenY[1] = 3;
snakeLenY[2] = 3;
};
String keyDirection; // 按键控制的运动方向 分别有:"upper" "lower" "right" "left"
String hdirection; // 头部朝向 分别有:"_A" "_B"
};
bool Snake::IsSnakeCollision(int hx, int hy)
{
// 墙壁碰撞
if (hx > 5 || hx < 1 || hy > 5 || hy < 1)
return true;
// 自身碰撞
for (int t = 0; t < snakeLen-1; t++)
{
if (hx == snakeLenX[t])
for (int i = 0; i < snakeLen-1; i++)
{
if (hy == snakeLenY[t])
return true;
}
}
return false;
}
检测蛇头是否超出了 XY 矩阵的范围,若超出则说明碰撞到墙壁。检测蛇头是否与身体重叠,若重叠则说明自身碰撞。
void Snake::updateSnakePosition(bool appleFlag,int tx,int ty)
{
if (!appleFlag)
{
for (int t = 0; t < snakeLen - 1; t++)
{
snakeLenX[t] = snakeLenX[t + 1];
snakeLenY[t] = snakeLenY[t + 1];
}
snakeLenX[snakeLen - 1] = hx;
snakeLenY[snakeLen - 1] = hy;
}
else
{
for (int t = (snakeLen - 1); t >0; t--)
{
snakeLenX[t] = snakeLenX[t - 1];
snakeLenY[t] = snakeLenY[t - 1];
}
snakeLenX[0]=tx;
snakeLenY[0]=ty;
}
}
当 appleFlag 为 true 时,说明蛇吃到了苹果,此时把原本消失尾部加到蛇的数组里面去。当 appleFlag 为 false 时,说明蛇在移动,此时把数组的标号减少一位,然后把蛇头的位置放在了最高位
bool Shake::Key_Scan()
{
static u_char key_up = 1;
keyDirection = "_S";
if (key_up && (digitalRead(BUTTON_A) == LOW || digitalRead(BUTTON_B) == LOW))
{
delayMicroseconds(10);
key_up = 0;
if (digitalRead(BUTTON_A) == LOW)
{
keyDirection = "_A";
}
else if (digitalRead(BUTTON_B) == LOW)
{
keyDirection = "_B";
}
return true;
}
else if (digitalRead(BUTTON_A) == HIGH && digitalRead(BUTTON_B) == HIGH)
{
key_up = 1;
return false;
}else
{
return false;
}
}
判断是否有按 A/B 键。这里不支持连按
void Shake::DirectionAndCount()
{
if (hdirection == "right")
{
if (keyDirection == "_A")
{
hy += 1;
hdirection = "lower";
}
else if (keyDirection == "_B")
{
hy -= 1;
hdirection = "upper";
}
}
else if (hdirection == "left")
{
if (keyDirection == "_A")
{
hy -= 1;
hdirection = "upper";
}
else if (keyDirection == "_B")
{
hy += 1;
hdirection = "lower";
}
}
else if (hdirection == "upper")
{
if (keyDirection == "_A")
{
hx += 1;
hdirection = "right";
}
else if (keyDirection == "_B")
{
hx -= 1;
hdirection = "left";
}
}
else if (hdirection == "lower")
{
if (keyDirection == "_A")
{
hx -= 1;
hdirection = "left";
}
else if (keyDirection == "_B")
{
hx += 1;
hdirection = "right";
}
}
}
简单的运用 if…else… 语句来进行判断。通过当前的蛇头运动方向以及按键控制的方向,来判断下一步蛇头的运动方向
利用 beforeTime = clock()
获取当前时间,再利用 clock()-before
来获取时间间隔;当满足时间间隔条件时,执行下面函数
// 自动前进
void AutoWalk()
{
if (shake.hdirection == "upper")
{
shake.hy -= 1;
}
else if (shake.hdirection == "lower")
{
shake.hy += 1;
}
else if (shake.hdirection == "left")
{
shake.hx -= 1;
}
else if (shake.hdirection == "right")
{
shake.hx += 1;
}
strip.SetPixelColor(Count(shake.shakeLenX[0], shake.shakeLenY[0]), black);
shake.updateShakePosition();
}
每隔一段时间,判读蛇头的运动方向,变化蛇头的位置。为了实现移动效果,使蛇尾的位置消失,其实就是将蛇尾的位置变黑。然后再更新蛇的位置,把新的蛇尾放在了数组的最后。
void DisplaySnake()
{
for (int i = 0; i < snake.snakeLen; i++)
{
strip.SetPixelColor(Count(snake.snakeLenX[i], snake.snakeLenY[i]), green);
}
strip.SetPixelColor(Count(snake.snakeLenX[snake.snakeLen - 1], snake.snakeLenY[snake.snakeLen - 1]), purple);
strip.Show();
}
利用循环在数组里提取蛇的位置,并显示出来
指针和动态分配内存 (不定长度数组)------新标准c++程序设计