#include #include #include #include /********* OLED *********/ #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define SCREEN_ADDRESS 0x3C // 保持你已验证可用的构造参数 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1, 1700000UL, 1700000UL); /********* Touch pins (official pinout) *********/ // CH0..CH5 → P03,P04,P02,P27,P01,P26 (3,4,2,27,1,26) #define N_TOUCH 6 int touch_pins[N_TOUCH] = {3, 4, 2, 27, 1, 26}; /********* Touch timing *********/ const uint32_t SAFE_WINDOW_MS = 2000; volatile uint16_t T_MAX_US = 500; // 足够你的板子 const uint16_t DISCHARGE_US = 25; /********* Game timing *********/ const uint32_t FRAME_MS = 50; // 20 FPS /********* Physics tuning *********/ const float GRAVITY = 1.00f; // 更快下落 const float JUMP_IMPULSE = -7.8f; // 更有力起跳 const float MAX_FALL_SPEED = 10.0f; // 终端速度 const float AIR_DOWN_BOOST = 0.6f; // 空中按下加速落地 /********* Input mapping *********/ // CH2,3,4,5 = down,left,right,up ; CH0,1 = func right/left #define CH_DOWN 2 #define CH_LEFT 3 #define CH_RIGHT 4 #define CH_UP 5 #define CH_FUNC_R 0 // start/pause/resume #define CH_FUNC_L 1 // reset/restart /********* Touch state *********/ uint16_t touch_us[N_TOUCH] = {0}; bool pressed[N_TOUCH] = {0}; // 非0即按下 /********* Utils *********/ static inline void usb_friendly_yield() { yield(); } static inline uint16_t measure_touch_us(int pin) { pinMode(pin, OUTPUT); digitalWriteFast(pin, LOW); delayMicroseconds(DISCHARGE_US); pinMode(pin, INPUT_PULLUP); unsigned long t0 = micros(); while (!digitalReadFast(pin)) { if ((uint16_t)(micros() - t0) >= T_MAX_US) break; usb_friendly_yield(); } return (uint16_t)(micros() - t0); } void poll_touch() { for (int i=0;i= 150) scrollSpeed = 3; if (score >= 300) scrollSpeed = 4; uint32_t base = 1400; if (score >= 100) base = 1200; if (score >= 250) base = 1000; spawnInterval = base; spawnJitter = 700; minGapPx = 70; if (score >= 150) minGapPx = 60; if (score >= 300) minGapPx = 52; } void spawn_rock() { if (activeRockCount() >= activeRocksLimit) return; if (rightmostRockX() > SCREEN_WIDTH - (int)minGapPx) return; for (int i=0;i(spawnInterval + jitter, 700); } /********* collision *********/ bool collideRect(int x1,int y1,int w1,int h1, int x2,int y2,int w2,int h2) { return !(x2 > x1+w1-1 || x2+w2-1 < x1 || y2 > y1+h1-1 || y2+h2-1 < y1); } /********* drawing *********/ void draw_dino() { int x = dino.x; int h = dino.crouch ? dinoH_crouch : dinoH; int y = dino.y; // 身体 display.fillRect(x, y+2, 10, h-2, SSD1306_WHITE); // 尾巴 display.fillTriangle(x-5, y+h-3, x, y+h-3, x, y+h-7, SSD1306_WHITE); // 头 display.fillRect(x+9, y, 7, 6, SSD1306_WHITE); // 嘴开口与眼睛 display.drawPixel(x+14, y+5, SSD1306_BLACK); display.drawPixel(x+14, y+2, SSD1306_BLACK); // 背脊 display.drawPixel(x+3, y+1, SSD1306_WHITE); display.drawPixel(x+5, y, SSD1306_WHITE); // 腿 / 脚 int footY = y + h - 1; if (footY > groundY) footY = groundY; if (dino.crouch) { display.fillRect(x+2, footY-1, 3, 2, SSD1306_WHITE); display.fillRect(x+7, footY-1, 3, 2, SSD1306_WHITE); } else { bool onGround = (dino.y >= groundY - h); if (onGround) { if (runPhase == 0) { display.fillRect(x+2, footY-2, 3, 3, SSD1306_WHITE); // 左弯 display.fillRect(x+8, footY-1, 3, 2, SSD1306_WHITE); // 右伸 } else { display.fillRect(x+2, footY-1, 3, 2, SSD1306_WHITE); // 左伸 display.fillRect(x+8, footY-2, 3, 3, SSD1306_WHITE); // 右弯 } } else { display.fillRect(x+3, footY-2, 3, 3, SSD1306_WHITE); display.fillRect(x+9, footY-2, 3, 3, SSD1306_WHITE); } } } void draw_ground() { display.drawLine(0, groundY+1, SCREEN_WIDTH-1, groundY+1, SSD1306_WHITE); } void draw_rocks() { for (int i=0;i= groundY - curH); if (onGround) { dino.y = groundY - curH; dino.vy = 0; } if (jump && onGround) dino.vy = JUMP_IMPULSE; // horizontal if (left) dino.x -= 2; if (right) dino.x += 2; dino.x = constrain(dino.x, 2, 50); // gravity & vertical dino.vy += GRAVITY; if (!onGround && down) dino.vy += AIR_DOWN_BOOST; if (dino.vy > MAX_FALL_SPEED) dino.vy = MAX_FALL_SPEED; if (dino.vy < -MAX_FALL_SPEED) dino.vy = -MAX_FALL_SPEED; dino.y += (int)dino.vy; curH = dino.crouch ? dinoH_crouch : dinoH; if (dino.y > groundY - curH) { dino.y = groundY - curH; dino.vy = 0; } // 跑步动画:落地且非蹲下时,每帧切一相位 if (dino.y >= groundY - curH && !dino.crouch) runPhase ^= 1; // spawn spawnTimer += FRAME_MS; if (spawnTimer >= spawnInterval) { spawn_rock(); } // move rocks & scoring/collision for (int i=0;i best) best = score; if (score % 80 == 0 && scrollSpeed < 4) scrollSpeed++; retune_spawn_from_score(); } else { int dH = dino.crouch ? dinoH_crouch : dinoH; if (collideRect(dino.x, dino.y, dinoW, dH, rocks[i].x, rocks[i].y, rocks[i].w, rocks[i].h)) { gstate = GS_GAMEOVER; } } } } /********* setup/loop *********/ unsigned long t_boot = 0; void setup() { pinMode(LED_BUILTIN, OUTPUT); Serial.begin(115200); t_boot = millis(); // OLED Wire.begin(); Wire.setClock(400000); delay(30); display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS); display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(8, 26); display.println("Tiny Dino!"); display.setCursor(8, 38); display.println("CH0 Start / CH1 Reset"); display.display(); randomSeed(micros()); reset_game(true); gstate = GS_MENU; } void loop() { // Safe window if (millis() - t_boot < SAFE_WINDOW_MS) { digitalWrite(LED_BUILTIN, (millis()/250)%2); delay(50); return; } // frame pacing static uint32_t last = 0; uint32_t now = millis(); if (now - last < FRAME_MS) return; last = now; // input poll_touch(); // state machine switch (gstate) { case GS_MENU: do_menu(); break; case GS_PLAY: do_play(); break; case GS_PAUSE: do_pause(); break; case GS_GAMEOVER: do_gameover(); break; } // render draw_scene(); }