Menu
CoddyTech

反应力游戏:抢跑

本课属于 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) 时,开发板根本无法检测到这次按下。

challenge icon

挑战

中等

处理误触:

  • 在 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;
  }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

项目中的所有课

自己练习: Arduino 在线编辑器