// ==== HTMAA Touch + OLED Mini Game: Paddle Ball ==== #include #include #include #include "Adafruit_FreeTouch.h" // ---------- OLED ---------- #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1); // I2C 0x3C 0x3D #define OLED_ADDR 0x3C // ---------- Touch (SAMD21 FreeTouch) ---------- #define N_TOUCH 6 // 6touchpin 2~7 uint8_t touch_pins[N_TOUCH] = {2, 3, 4, 5, 6, 7}; // button enum { BTN_LEFT=1, BTN_RIGHT=0, BTN_START=2, BTN_PAUSE=3, BTN_RESET=4, BTN_SPEED=5 }; #define THRESHOLD 500 Adafruit_FreeTouch* touch_devices[N_TOUCH]; int touch_values[N_TOUCH] = {0,0,0,0,0,0}; bool pin_now[N_TOUCH] = {false,false,false,false,false,false}; bool pin_past[N_TOUCH] = {false,false,false,false,false,false}; // ---------- LED ---------- #define PIN_LED 13 // Fab/SAMD21 13=PA15 15 // ---------- Game ---------- enum GameState { MENU, PLAYING, PAUSED, GAMEOVER }; GameState state = MENU; int score = 0; int bestScore = 0; int paddleW = 20; int paddleH = 3; int paddleY = SCREEN_HEIGHT - 4; int paddleX = (SCREEN_WIDTH - paddleW)/2; int paddleSpeed = 3; float ballX = SCREEN_WIDTH/2; float ballY = SCREEN_HEIGHT/2; float ballDX = 1.5; float ballDY = -1.5; int ballR = 2; // 0=1=2= int difficulty = 1; void setDifficulty(int d){ difficulty = constrain(d, 0, 2); float v = (difficulty==0)? 1.2 : (difficulty==1? 1.6 : 2.2); ballDX = (ballDX<0?-v:+v); ballDY = (ballDY<0?-v:+v); paddleW = (difficulty==2)? 16 : (difficulty==1? 20 : 26); paddleSpeed = (difficulty==2)? 4 : (difficulty==1? 3 : 2); } void resetBallPaddle(){ paddleX = (SCREEN_WIDTH - paddleW)/2; ballX = SCREEN_WIDTH/2; ballY = SCREEN_HEIGHT/2; float v = (difficulty==0)? 1.2 : (difficulty==1? 1.6 : 2.2); ballDX = v * (random(0,2)==0 ? -1 : +1); ballDY = -v; } void updateTouch(){ Ptc *ptc = (Ptc *)PTC; for(int i=0;ibegin(); touch_values[i] = touch_devices[i]->measure(); // PTC ptc->CTRLA.bit.ENABLE = 0; ptc->CTRLA.bit.SWRESET = 1; pin_past[i] = pin_now[i]; pin_now[i] = (touch_values[i] > THRESHOLD); } } bool pressed(int b){ return (pin_now[b] && !pin_past[b]); } bool held(int b){ // touch return pin_now[b]; } void drawHUD(){ display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(0,0); display.print("Score:"); display.print(score); display.setCursor(72,0); display.print("Best:"); display.print(bestScore); } void drawMenu(){ display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(12,10); display.println("Paddle Ball (6-touch)"); display.setCursor(0,24); display.println("Q1: LEFT Q0: RIGHT"); display.setCursor(0,34); display.println("Q2: START Q3: PAUSE"); display.setCursor(0,44); display.println("Q4: RESET Q5: SPEED"); display.setCursor(0,54); display.print("Difficulty: "); display.println(difficulty==0?"SLOW":(difficulty==1?"NORM":"FAST")); display.display(); } void drawGame(){ display.clearDisplay(); drawHUD(); display.fillCircle((int)ballX, (int)ballY, ballR, SSD1306_WHITE); display.fillRect(paddleX, paddleY, paddleW, paddleH, SSD1306_WHITE); display.display(); } void drawPaused(){ drawGame(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(40, 28); display.println("PAUSED"); display.display(); } void drawGameOver(){ display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(28, 14); display.println("GAME OVER"); display.setCursor(18, 30); display.print("Score: "); display.println(score); display.setCursor(18, 40); display.print("Best: "); display.println(bestScore); display.setCursor(0, 54); display.println("Q2:Start Q4:Reset Q5:Speed"); display.display(); } void setup(){ // serialtouch Serial.begin(115200); // OLED Wire.begin(); // SAMD21 SDA=D4, SCL=D5 if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)){ while(1){ delay(10); } } display.clearDisplay(); display.display(); // Touch init for(int i=0;i= SCREEN_WIDTH){ ballX = SCREEN_WIDTH - ballR; ballDX = -ballDX; } if(ballY - ballR <= 10){ ballY = 10 + ballR; ballDY = -ballDY; } // HUD // AABB + if( (ballY + ballR >= paddleY) && (ballX >= paddleX) && (ballX <= paddleX + paddleW) && (ballDY > 0) ){ ballY = paddleY - ballR; ballDY = -ballDY; float hitPos = (ballX - paddleX) / (float)paddleW; // 0..1 float offset = (hitPos - 0.5f) * 1.2f; // -0.6..+0.6 ballDX += offset; float maxv = (difficulty==2)? 2.8 : (difficulty==1? 2.2 : 1.8); if(ballDX > maxv) ballDX = maxv; if(ballDX < -maxv) ballDX = -maxv; score++; if(score > bestScore) bestScore = score; digitalWrite(PIN_LED, HIGH); }else{ digitalWrite(PIN_LED, LOW); } // Game Over if(ballY - ballR > SCREEN_HEIGHT){ state = GAMEOVER; } break; } if(state==MENU){ }else if(state==PAUSED){ drawPaused(); }else if(state==GAMEOVER){ drawGameOver(); }else{ // PLAYING drawGame(); } // serialdebug // for(int i=0;i