工程筆記本 / 科技觀點/快訊/報導

科技觀點/快訊/報導

Arduino X Vibe Coding 五分鐘做出小遊戲Part 1 ---- 做出 Chrome Dinosaur Game

鈺莨📅 2026-02-23👁 68

專案資訊

撰寫 / 攝影
許鈺莨
前情提要
時間
1小時內
成本
難度
★★☆☆☆
材料表

前言 Vibe Coding 是什麼?

它是一種透過 自然語言與 AI 互動 來生成程式碼的軟體開發方式,而不是手寫每一行程式碼。由 OpenAI 前共同創辦人 Andrej Karpathy 首創這個概念,意在讓人專注在思路與需求,而非語法與細節。這降低了技術門檻,讓更多人能從想法直接實現功能出來。   現今大型語言模型(如Chat GPT、Gemini….等)百花齊放,寫程式的門檻也越來越低,筆者在看Nvidia 執行長黃仁勳接受 Podcast 訪問時,提到我們要學會如何與 AI 聊天和互動,並學習擅長提問,利用 AI 獲取知識,進而達到我們的目的。 黃仁勳訪談影片: https://www.instagram.com/reels/DUkRfywgseS/   筆者將透過 Vibe Coding 搭配 《Arduino 首次接觸就上手》硬體教材,將設計多種小遊戲開發,讓各位可以透過用自然語言的方式向生成式 AI對話,進而將自己的想法能夠實現出來。以下筆者是使用 Chat GPT 來對話。
 

內文開始 先來看 Demo 成果

[video width="1920" height="1080" mp4="https://cd.ainfinity.tw/media/2026/02/Vibe-Coding-五分鐘做出小遊戲-fin.mp4"][/video]

硬體架構

首先開發小遊戲所使用的硬體是《Arduino 首次接觸就上手》,購買資訊請見上方專案資訊的材料表一欄 《Arduino 首次接觸就上手》 好處就是感測器不需要額外接線,感測器是直接焊好的,而此專案使用的感測器為 OLED 0.96"、無源蜂鳴器及按鈕,感測器和腳位介紹見下方圖示  
  • 下載/開啟 Arduino IDE

筆者使用的是 Hangeekduino 裡的 Arduino IDE 來撰寫程式 ,是用來上傳 AI 所產生的程式碼,給 Arduino 來執行的。 下載連結: https://drive.google.com/drive/folders/1iHQyfQvjbS4lhob-3mF5auntte_G86_f     下載後解壓縮,路徑位置在HanGeekduino\BlocklyDuino3\arduino-1.8.5,再開啟arduino.exe即可。  

本次專案目標:做出 Chrome Dinosaur Game

有了硬體後,筆者來分享一下為何想做出這次的小遊戲。   相信許多人都玩過 Google Chrome 瀏覽器斷網時出現的小遊戲 —— Chrome Dinosaur Game(又稱 T-Rex Game)。 某次筆者在高中用《Arduino 首次接觸就上手》上課時發現,部分學生已開始利用 ChatGPT 或 Gemini 生成程式碼並做出類似像 Chrome 恐龍跳躍小遊戲。這也顯示生成式 AI 正逐步改變學習方式,使 Vibe Coding 成為新的學習模式。 所以本專題將以 Vibe Coding 方式,實作 Arduino 版 Chrome Dinosaur Game 。

輸入提示詞 (Prompt)到 ChatGPT 產生遊戲程式

筆者是使用 ChatGPT 生成程式碼,當然讀者也可以使用其他生成式 AI 如: Gemini 、Claude...等,程式執行結果也許會與筆者不同,但這個 Vibe Coding 的過程是一樣的,請讀者開啟喜歡的生成式 AI來輸入提示詞。  
 Chrome Dinosaur Game 遊戲流程圖如下:  
這是一個典型的「狀態機遊戲流程」,遊戲會在「開始 → 遊戲中 → 結束」三種狀態之間切換,筆者將狀態、功能連同使用的硬體都轉成提示詞,傳達給 ChatGPT 。     以下是我向 ChatGPT 提出的需求(初版):
? 我現在要用 Arduino 程式做出來,我有按鈕和 0.96 吋 OLED。 ? 開機顯示 "Game Start, PLS Press Button",按下按鈕開始遊戲。 ? 用方塊代表角色與障礙物,按鈕控制跳躍,躲過得 10 分,撞到顯示 Game Over,10 秒後回到開始畫面。
  將上述需求貼到ChatGPT,寫越詳細越好,這時 ChatGPT 就會生成基本功能的程式碼,但是我們的硬體是《Arduino 首次接觸就上手》,所以第一次生成的程式碼如果燒錄有錯誤是正常的,因為每個人或每個模型所產的程式都不一樣,所以要針對錯誤,必須進一步優化我們的Prompt。

程式執行遇到錯誤

筆者將第一次產生的程式碼燒錄到 Arduino ,但很顯然失敗遇到錯誤。   這時候不用緊張,將錯誤訊息複製,或按 Copy error message ,貼回 ChatGPT 即可得到解答。   ChatGPT 解釋因為 OLED 函示庫 U8X8lib 沒有繪圖功能,所以有錯,然而會再次生成修正過後的程式碼。 而筆者提供的 Arduino IDE 中含有 U8g2lib 的函示庫,所以就可以跳過安裝步驟。 再將生成過後的程式碼上傳至《Arduino 首次接觸就上手》中,但顯然還是會遇見問題。

優化 Prompt(修正 OLED 顯示方向與按鈕腳位)

這次筆者遇到的問題是 OLED 顯示的方向轉了180 度(見下圖)    
筆者遇到 2 個問題,OLED 顯示方向有錯和按鈕腳位有錯(見下圖),
因為筆者之前有遇過一樣問題, OLED 顯示方向就是把 U8G2_R0 改成 U8G2_R2 即可,讀者若不知道的話可以詢問 ChatGPT ,並重新生成正確的程式碼。
 
那筆者除了錯誤之外,還想新增功能,便進一步向 ChatGPT下優化 Prompt:
修改2個地方,和新增功能: ? 改成 U8G2_R2 ? 按鈕是 D6 腳位 另外要新增無源蜂鳴器聲音,跳躍和 Game Over 都要不同音效,無源蜂鳴器是 D5 腳位
   

上述的提示語,除了跟ChatGPT說 OLED 的顯示角度有誤,也進一步說明按鈕的腳位是D6,補足第一次沒有完整說明腳位編號,最後也提出需要有音效的產生。

這時候產出來的程式碼,已經是可以運行了,但有些細節和程式執行上有問題,筆者遇到的問題是《Arduino 首次接觸就上手》一接電就開始遊戲,和想改變按鈕的觸發動作。

 
於是向 ChatGPT 再次下 Prompt:
  1. 當OLED 顯示 "Game Start ,PLS Press Button twice",需要按按鈕兩下才能進遊戲畫面。
  2. 讓Arduino 重新燒錄/重新供電時,不會直接進到遊戲畫面。
 

產生最終可運行程式碼

經歷了千辛萬苦,終於為最終優化後,可直接運行於《Arduino 首次接觸就上手》的完整程式碼。
#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>

// ===== OLED(旋轉 R2)=====
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R2, U8X8_PIN_NONE);

// ===== 腳位 =====
const int buttonPin = 6;
const int buzzerPin = 5;

// ===== 遊戲狀態 =====
enum GameState { WAIT_START, PLAYING, GAME_OVER };
GameState gameState = WAIT_START;

// ===== 遊戲參數 =====
const int groundY = 54;
const int playerSize = 8;
const int obsSize = 8;

int playerX = 16;
int playerY = groundY - playerSize;

int vy = 0;
bool isJumping = false;

int obsX = 128;
int obsY = groundY - obsSize;
int obsSpeed = 3;

long score = 0;
bool scoredThisObstacle = false;

unsigned long gameOverAt = 0;

// ===== Start 畫面控制 =====
unsigned long bootTime = 0;
bool startScreenReady = false;
int startPressCount = 0;   // 需按兩下

// ===== 音效 =====
bool tonePlaying = false;
unsigned long toneStart = 0;
unsigned long toneDuration = 0;

void playTone(int f,int d){
  tone(buzzerPin,f);
  tonePlaying=true;
  toneStart=millis();
  toneDuration=d;
}
void updateTone(){
  if(tonePlaying && millis()-toneStart>=toneDuration){
    noTone(buzzerPin);
    tonePlaying=false;
  }
}
void soundJump(){ playTone(1200,80); }

// Game Over 音效
int goStep=0;
unsigned long goLast=0;
bool gameOverSoundActive=false;

void startGameOverSound(){
  gameOverSoundActive=true;
  goStep=0;
  goLast=0;
}
void updateGameOverSound(){
  if(!gameOverSoundActive) return;
  unsigned long now=millis();
  if(goStep==0){ playTone(600,200); goLast=now; goStep=1; }
  else if(goStep==1 && now-goLast>220){ playTone(300,300); goLast=now; goStep=2; }
  else if(goStep==2 && now-goLast>320){ gameOverSoundActive=false; }
}

// ===== 按鈕(邊緣觸發)=====
bool lastBtn=HIGH;
unsigned long lastDebounceAt=0;
const unsigned long debounceMs=25;

bool buttonPressed(){
  bool cur=digitalRead(buttonPin);

  if(cur!=lastBtn){
    lastDebounceAt=millis();
    lastBtn=cur;
  }
  if(millis()-lastDebounceAt<debounceMs) return false;

  static bool prevStable=HIGH;
  bool pressed=(prevStable==HIGH && cur==LOW);
  prevStable=cur;
  return pressed;
}

// ===== 畫面 =====
void drawStartScreen(){
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_6x12_tf);
  u8g2.drawStr(4,24,"Game Start");
  u8g2.drawStr(4,44,"PLS Press Button twice");
  u8g2.sendBuffer();
}

void drawGameOver(){
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_6x12_tf);
  u8g2.drawStr(10,24,"Game Over");
  char buf[24];
  snprintf(buf,sizeof(buf),"Score:%ld",score);
  u8g2.drawStr(10,44,buf);
  u8g2.sendBuffer();
}

void drawGame(){
  u8g2.clearBuffer();

  char buf[24];
  snprintf(buf,sizeof(buf),"Score:%ld",score);
  u8g2.drawStr(0,12,buf);

  u8g2.drawHLine(0,groundY,128);
  u8g2.drawBox(playerX,playerY,playerSize,playerSize);
  u8g2.drawBox(obsX,obsY,obsSize,obsSize);

  u8g2.sendBuffer();
}

bool checkCollision(){
  return (playerX < obsX+obsSize &&
          playerX+playerSize > obsX &&
          playerY < obsY+obsSize &&
          playerY+playerSize > obsY);
}

void resetGame(){
  playerY=groundY-playerSize;
  vy=0;
  isJumping=false;
  obsX=128;
  obsSpeed=3;
  score=0;
  scoredThisObstacle=false;
}

// ===== Setup =====
void setup(){
  pinMode(buttonPin,INPUT_PULLUP);
  pinMode(buzzerPin,OUTPUT);

  u8g2.begin();
  u8g2.setContrast(200);

  bootTime=millis();
  gameState=WAIT_START;
  startScreenReady=false;
  startPressCount=0;

  drawStartScreen();   // 上電立即顯示
}

// ===== Loop =====
void loop(){

  updateTone();
  updateGameOverSound();

  switch(gameState){

    // ===== Start 畫面(需按兩下)=====
    case WAIT_START:

      // 上電後先等待,避免誤觸
      if(!startScreenReady){
        if(millis()-bootTime>800 && digitalRead(buttonPin)==HIGH){
          startScreenReady=true;
        }
        delay(20);
        return;
      }

      if(buttonPressed()){
        startPressCount++;

        // 第二次按下才開始遊戲
        if(startPressCount>=2){
          startPressCount=0;
          resetGame();
          gameState=PLAYING;
        }
      }
      delay(20);
      break;

    // ===== 遊戲中 =====
    case PLAYING:

      if(buttonPressed() && !isJumping){
        vy=-10;
        isJumping=true;
        soundJump();
      }

      if(isJumping){
        playerY+=vy;
        vy+=1;
        if(playerY>=groundY-playerSize){
          playerY=groundY-playerSize;
          vy=0;
          isJumping=false;
        }
      }

      obsX-=obsSpeed;

      if(!scoredThisObstacle && (obsX+obsSize)<playerX){
        score+=10;
        scoredThisObstacle=true;
      }

      if(obsX<-obsSize){
        obsX=128;
        scoredThisObstacle=false;
        if(obsSpeed<8) obsSpeed++;
      }

      if(checkCollision()){
        gameState=GAME_OVER;
        gameOverAt=millis();
        startGameOverSound();
      }

      drawGame();
      delay(30);
      break;

    // ===== Game Over =====
    case GAME_OVER:
      drawGameOver();
      if(millis()-gameOverAt>=10000UL){
        gameState=WAIT_START;
        startScreenReady=false;
        startPressCount=0;
        bootTime=millis();
        drawStartScreen();
      }
      delay(20);
      break;
  }
}

恭喜你完成本專題

完成此遊戲,你已掌握:
? 使用 Prompt 與 AI 生成程式 ? 遊戲狀態流程設計(State Machine) ? OLED 畫面顯示與互動控制 ? 按鈕輸入與跳躍邏輯 ? 音效回饋設計 ? 透過優化程式行為

下一步,嘗試新增功能

你可以試著新增的內容:
?反應速度加快 ?最後顯示最高分 ?設計不同關卡
 

立即洽詢與報名

CAVEDU 教育團隊 (翰尼斯企業) 是國內執行課程經驗最豐富的 NVIDIA 教育合作夥伴。 歡迎洽詢企業包班、技術顧問與客製化課程:

郵件諮詢: service@cavedu.com 電話洽詢: (02) 2306-2900

我們的專業團隊(2025 全球第一白金級大使領軍)將為您提供專屬的 NVIDIA DLI 原廠認證課程 規劃。

相關文章 📎

留言 💬 (0)

還沒有留言,來當第一個。