本帖最后由 zoey不种土豆 于 2024-11-21 20:30 编辑
在这个项目中我们将用Arduino制作一个反应速度测试游戏。通过点亮LED灯并快速按下对应按钮来得分,游戏持续一分钟。准备挑战你的反应极限了吗?让我们开始吧!
元件清单
硬件连接
在这个项目中,我们使用到的LED灯和按键模块,都是在之前的项目中出现过的。不同的一点是之前项目【交通信号灯】中给按键外加了一个下拉电阻,在这个项目中,我们使用Arduino芯片内部的上拉电阻,详见本项目的硬件回顾。请按照下图将硬件连接正确。
示例代码
样例代码:
- //项目 - 反应速度测试
- // 定义LED灯和按钮的引脚
- const int LEDS[] = {3, 5, 7};
- const int BUTTONS[] = {2, 4, 6};
-
- int score = 0; // 玩家的得分
- bool gameStarted = false; // 游戏是否开始的标志
- unsigned long gameStartTime; // 记录游戏开始的时间戳
- const unsigned long gameDuration = 60000;
- // 游戏持续时间,单位为毫秒(1分钟)
-
- void setup() {
- Serial.begin(9600); // 初始串口通信
- // 设置LED灯和按钮的引脚模式
- for (int i = 0; i < 3; i++) {
- pinMode(LEDS[i], OUTPUT);
- pinMode(BUTTONS[i], INPUT_PULLUP);
- }
- }
-
- void loop() {
- // 检查是否同时按下了所有三个按钮来开始游戏
- if (!gameStarted && digitalRead(BUTTONS[0]) == LOW && digitalRead(BUTTONS[1]) == LOW && digitalRead(BUTTONS[2]) == LOW) {
- Serial.println("游戏开始");
- gameStarted = true;
- score = 0; // 重置得分
- gameStartTime = millis(); // 记录游戏开始的时间
- delay(1000); // 等待一秒后开始游戏
- }
-
- // 如果游戏已经开始,检查游戏是否超时
- if (gameStarted && millis() - gameStartTime < gameDuration) {
- int led = random(0, 3); // 随机选择一个LED
- digitalWrite(LEDS[led], HIGH); // 点亮这个LED
- long startTime = millis(); // 记录点亮LED的时间
- bool buttonPressed = false; // 初始化按钮状态(未被按下)
-
- // 给玩家一定时间来按按钮
- while (millis() - startTime < 1000) {
- if (digitalRead(BUTTONS[led]) == LOW) {
- // 检查是否按下了对应的按钮
- buttonPressed = true;
- break; // 如果按下了按钮,跳出循环
- }
- }
-
- digitalWrite(LEDS[led], LOW); // 熄灭LED
-
- if (buttonPressed) {
- score++; // 如果按下了按钮,增加得分
- Serial.println("得分 +1");
- }
- delay(200); // 等待一段时间再继续下一轮
- } else if (gameStarted) {
- // 如果游戏时间超过一分钟或玩家没有在时间内按下按钮,游戏结束
- gameStarted = false;
- Serial.print("游戏结束! 你的得分: ");
- Serial.println(score); // 通过串口输出得分
- delay(3000); // 等待3秒后可以重新开始游戏
- }
- }
复制代码
上传代码后,打开串口监视器,同时按下三个按键开始游戏,串口监视器上出现游戏开始字样。一旦游戏开始,系统将随机点亮一个LED灯,并给玩家一秒钟的时间来按下对应的按钮,便可获得1分。当游戏时间到达1分钟时,串口监视器出现游戏结束并告知你的游戏得分。
代码回顾
变量声明部分不做过多解释了,这个部分用于定义LED和按钮的引脚数组,以及用于跟踪得分、游戏状态和游戏时间的变量。- const int LEDS[] = {3, 5, 7};
- const int BUTTONS[] = {2, 4, 6};
-
- int score = 0;
- bool gameStarted = false;
- unsigned long gameStartTime;
- const unsigned long gameDuration = 60000;
复制代码
gameDuration是一个非负长整型的常数,用于定义游戏的持续时间。在这里,它被设置为60000毫秒,即1分钟,以确保游戏持续一分钟。 在setup()函数中,初始化串口通信,并使用一个for循环设置LED和按钮的引脚模式。 在loop()函数中,流程如下: - // 游戏开始前的检查
- if (!gameStarted && digitalRead(BUTTONS[0]) == LOW && digitalRead(BUTTONS[1]) == LOW && digitalRead(BUTTONS[2]) == LOW
- ) {
- // 游戏初始化
- gameStarted = true;
- }
-
- // 游戏被激活后
- if (gameStarted && millis() - gameStartTime < gameDuration) {
- // 游戏进行中
- } else if (gameStarted) {
- // 游戏结束
- }
- // 返回游戏开始前的检查
复制代码
如果用流程图,表示如下: 第一个部分是游戏开始前的检查。 用来判断游戏是否被激活,激活指的是从游戏的非激活状态(即未开始也未在进行中)转换到准备开始的状态。 - if (!gameStarted && digitalRead(BUTTONS[0]) == LOW && digitalRead(BUTTONS[1]) == LOW && digitalRead(BUTTONS[2]) == LOW)
复制代码
所以在第一个判断中,既要判断三个按键是否被同时按下,又要判断当时游戏是否处于未激活状态。 游戏被激活后进行游戏的初始化: - Serial.println("游戏开始");
- gameStarted = true;
- score = 0; // 重置得分
- gameStartTime = millis(); // 记录游戏开始的时间
- delay(1000); // 等待一秒后开始游戏
复制代码
通过串口输出“游戏开始”,将gameStarted设置为true以标记游戏已经开始,重置得分score为0,记录游戏开始的时间gameStartTime为当前毫秒数(使用millis()函数获取),然后暂停1秒以给用户一个视觉或心理上的准备时间。 第二部分是游戏激活后的逻辑结构。 - if(gameStarted && millis() - gameStartTime < gameDuration) {
- // 游戏进行中
- } else if (gameStarted) {
- // 游戏结束
- }
复制代码
当游戏已经开始且游戏时间未超过设定的持续时间gameDuration时,执行游戏进行中的逻辑。如果游戏已经开始但游戏时间超过了设定的持续时间gameDuration,执行游戏结束的逻辑。接下来是对于游戏进行中的逻辑和游戏结束的逻辑的详解。
游戏进行中: 一旦游戏开始,系统将随机点亮一个LED灯,并给玩家一秒钟的时间来按下对应的按钮。 - int led = random(0, 3); // 随机选择一个LED
- digitalWrite(LEDS[led], HIGH); // 点亮这个LED
- long startTime = millis();
- bool buttonPressed = false; // 初始化按钮状态(未被按下)
复制代码
同时记录下点亮LED的时间startTime,并且把buttonpressed的状态设为fasle(初始化),之后进入一个while循环,等待玩家在1秒内按下对应的按钮。 - while (millis() - startTime < 1000) {
- if (digitalRead(BUTTONS[led]) == LOW) {
- // 检查是否按下了对应的按钮
- buttonPressed = true;
- break; // 如果按下了按钮,跳出循环
- }
复制代码
如果玩家在1秒内按下了对应的按钮(通过检查BUTTONS[led]是否被按下),则标记buttonPressed为true并跳出循环。 while循环结束后,无论是否按下了按钮,都会熄灭LED。 - if (buttonPressed) {
- score++; // 如果按下了按钮,增加得分
- Serial.println("得分 +1");
- }
- delay(200);
复制代码
如果buttonPressed为true,则增加得分并输出得分增加的消息。 最后,使用delay(200)等待200毫秒后继续下一轮游戏。
游戏结束: - else if (gameStarted) {
- // 如果游戏时间超过一分钟,游戏结束
- gameStarted = false;
- Serial.print("游戏结束! 你的得分: ");
- Serial.println(score); // 通过串口输出得分
- delay(3000); // 等待3秒后可以重新开始游戏
- }
复制代码
否则如果游戏已经开始但游戏时间超过了设定的持续时间gameDuration,则执行游戏结束的逻辑。 将gameStarted设置为false以标记游戏结束,通过串口输出游戏结束的消息和玩家的最终得分,然后暂停3秒,给用户一个重新开始游戏前的缓冲时间。
硬件回顾 上拉电阻 通常,如果没有输入,将输入引脚引导至已知状态非常有用。这可以通过在输入端添加一个上拉电阻(至+5V)或一个下拉电阻(接地电阻)来实现。这个项目中使用的按键,由于没有内置上拉电阻,则需要在代码中设置引脚为INPUT_PULLUP或使用外部电阻。 Arduino的ATmega 芯片内置了 20K 上拉电阻器,可通过软件访问。这些内置上拉电阻可通过将 pinMode() 设置为 INPUT_PULLUP 来访问。这有效地反转了 INPUT 模式的行为,其中 HIGH 表示传感器关闭,LOW 表示传感器打开。
课后练习
尝试用现有的硬件,做一个颜色记忆游戏,实现一个简化版的记忆翻牌游戏,使用两个LED(代表两张牌)和一个按钮。游戏开始时,两个LED随机亮起(模拟翻牌),然后熄灭。玩家需要记住这两个LED的亮起状态,并通过按钮输入来“翻牌”(实际上是重新点亮LED),尝试匹配之前的亮起状态。根据结果配合串口输出“恭喜,你赢了!”;否则,输出“很遗憾,你输了!”。还有更多的玩法等待着你发现。
示例代码
|