#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) *********/ #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;如需更顺可改 40 /********* Physics (tuned) *********/ const float GRAVITY = 1.05f; // 更紧凑的抛物线 const float JUMP_IMPULSE = -9.6f; // 跳更高 const float MAX_FALL_SPEED = 11.0f; const float AIR_DOWN_BOOST = 0.60f; /********* 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 #define CH_FUNC_L 1 /********* Touch state *********/ uint16_t touch_us[N_TOUCH] = {0}; bool pressed[N_TOUCH] = {0}; // 非0即按下 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() { // 贴地对齐:y 表示顶部;地面在 groundY int h = dinoH(); int topY = dino.y; if (dino.crouch) { // crouch sprite 28x14 display.drawBitmap(dino.x, topY, DINO_CROUCH, 28, 14, SSD1306_WHITE); } else { // running 24x20,采用两帧交替(runPhase) const uint8_t* frame = (runPhase==0) ? DINO_RUN_A : DINO_RUN_B; display.drawBitmap(dino.x, topY, frame, 24, 20, 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; // 左右 if (left) dino.x -= 2; if (right) dino.x += 2; dino.x = constrain(dino.x, 2, 50); // 重力/下落 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 = dinoH(); if (dino.y > groundY - curH) { dino.y = groundY - curH; dino.vy = 0; } if (dino.y >= groundY - curH && !dino.crouch) runPhase ^= 1; // 生成 spawnTimer += FRAME_MS; if (spawnTimer >= spawnInterval) spawn_rock(); // 岩石运动/判定 for (int i=0;i best) best = score; if (score % 80 == 0 && scrollSpeed < 4) scrollSpeed++; retune_spawn_from_score(); } else { // 碰撞盒:用位图内较紧凑区域(从左偏 2px、宽略减) int hbX = dino.x + 2; int hbW = dinoW() - 4; if (hbW < 4) hbW = 4; if (collideRect(hbX, dino.y, hbW, dinoH(), 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(); Wire.begin(); Wire.setClock(400000); delay(30); display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS); display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(10, 16); display.println("Tiny Alian!"); display.setCursor(6, 28); display.println("Q0 Start/ Q1 Reset"); display.display(); randomSeed(micros()); reset_game(true); gstate = GS_MENU; } void loop(){ if (millis() - t_boot < SAFE_WINDOW_MS) { digitalWrite(LED_BUILTIN, (millis()/250)%2); delay(50); return; } static uint32_t last = 0; uint32_t now = millis(); if (now - last < FRAME_MS) return; last = now; poll_touch(); 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; } draw_scene(); }