反应力游戏:抢跑
本课属于 Coddy Arduino 学习旅程的“项目”单元。第 7 课,共 24 课。
现在,聪明的玩家可以作弊:在等待期间反复按下按钮,其中一次按下就会刚好发生在灯亮起之后。比赛对此有一条规则:在信号发出前移动属于抢跑,比赛会被取消。
状态机已经知道玩家什么时候按得太早:在 WAITING 状态下的任何一次按下。请在这里先检查,再检查时钟:
case WAITING:
if (pressed) {
Serial.println("Too soon!");
state = READY;
} else if (millis() - waitStart >= waitTime) {
// 像之前一样点亮 LED
}
break;返回 READY 就会结束这一轮:LED 不会亮起,下一次按下会用新的随机等待时间开始新游戏。使用 delay(waitTime) 时,开发板根本无法检测到这次按下。
挑战
中等处理误触:
- 在
WAITING状态下,按下按钮会显示Too soon!,然后返回READY,且不会点亮 LED - 其他情况保持不变:再次按下按钮会开始新一轮
按“运行”,快速连续按两次,然后按“检查”。
亲手试试
const int ledPin = 12;
const int buttonPin = 2;
// 一轮中的三种状态
enum State { READY, WAITING, LIT };
State state = READY;
int lastButton = HIGH; // 上一次读取到的按钮状态
unsigned long waitStart = 0; // 等待开始的时间
unsigned long waitTime = 0; // 这次等待的时长
unsigned long litAt = 0; // LED 亮起的时间
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP);
Serial.begin(9600);
Serial.println("Press the button to start");
}
void loop() {
int button = digitalRead(buttonPin);
bool pressed = (button == LOW && lastButton == HIGH); // 刚刚按下
lastButton = button;
unsigned long now = millis();
switch (state) {
case READY:
if (pressed) {
randomSeed(micros()); // 每次按下的时间都不同
waitTime = random(1000, 3001);
waitStart = now;
state = WAITING;
}
break;
case WAITING:
if (now - waitStart >= waitTime) {
digitalWrite(ledPin, HIGH);
litAt = now;
state = LIT;
}
break;
case LIT:
if (pressed) {
digitalWrite(ledPin, LOW);
unsigned long reaction = now - litAt;
Serial.print("Reaction: ");
Serial.print(reaction);
Serial.println(" ms");
state = READY;
}
break;
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
项目中的所有课
自己练习: Arduino 在线编辑器