y=0 คือขอบบนสุดbird_y ลดลง) จะทำให้นก ลอยขึ้นgame.start() — ล้างจอ + ปลุกจอยgame.Box / game.Text — นก + คะแนนgame.run(fn, fps) — ลูปเกม 50 fpsgame.keys() / game.sfx()bird_velocityของใหม่จริง ๆ ของคาบนี้มีแค่ตัวแปร
bird_velocity(ความเร็วแนวตั้ง) กับสมการ 2 บรรทัด เท่านั้นเอง
ไฟล์เด็กเล่น: practise_codes/flappy_step1.py↗ (เว้นช่อง bird_y)
import bentogame as game
game.title("FLAPPY") # หน้าเริ่ม: Start=เล่น Back=ออก (ทำ start ให้ในตัว)
# ----- เติมส่วนนี้เอง: ตำแหน่งเริ่มต้นของนก -----
bird_y = 180.0 # ความสูงของนก (พิกเซลจากขอบบน)
bird = game.Box(150, bird_y, 34, 34, game.GB_LIGHTEST) # ตัวนก = กล่องสว่างสุด
score = 0
score_label = game.Text("0", 384, 16, game.WHITE)
def on_each_frame():
# ทุกเฟรม วาดนกไว้ที่ความสูง bird_y (ยังไม่ขยับ — เป็นภาพนิ่ง)
bird.move_to(bird.x, bird_y)
# (Back=ออก / Start=เริ่มใหม่ — game.run() จัดการให้)
game.run(on_each_frame, fps=50) # ลูปเกม 50 fps
เช็ก: กล่องนกนิ่งกลางฟ้า กับเลข 0 ที่มุมบน ลองเปลี่ยน bird_y เป็น 50 หรือ 300 ดูสิ นกจะย้ายที่ตาม นี่แหละคือ 30% ที่เป็นงานของน้อง ๆ
หน้าจอ step 1 ควรหน้าตาแบบนี้ — นก (Box สว่างสุด) อยู่นิ่งที่ (150, 180) และ Score: 0 ยังไม่มีฟิสิกส์มาขยับ
ภาพจำลองหน้าจอ step 1 (course render) เทียบกับผลจริงบนบอร์ด — กรอบ BENTO Playground 800×480
ไฟล์: practise_codes/flappy_step2.py↗ (เว้นบล็อกฟิสิกส์)
GRAVITY = 0.45 # แรงโน้มถ่วง (ดึงลงต่อเฟรม)
bird_y = 180.0 # ความสูงของนก
bird_velocity = 0.0 # ความเร็วแนวตั้ง (+ = ลง, - = ขึ้น)
def on_each_frame():
global bird_y, bird_velocity
# ----- เติมส่วนนี้เอง: ฟิสิกส์ 2 บรรทัด -----
bird_velocity += GRAVITY # โน้มถ่วงดึงลงทุกเฟรม
bird_y += bird_velocity # เลื่อนนกตามความเร็ว
bird.move_to(bird.x, bird_y)
สังเกตว่านกจะร่วงเร็วขึ้นเรื่อย ๆ เพราะความเร็วมันสะสมไปทุกเฟรม:
v += GRAVITY สะสมความเร็วทุกเฟรม นี่แหละความหมายของคำว่า "เร่ง"
ลองวาดค่าจริงออกมาดู: velocity เพิ่มเป็นเส้นตรง (บวก คงที่ทุกเฟรม) แต่ y ที่เอา velocity ไปบวกสะสม กลายเป็นเส้นโค้ง ร่วงเร็วขึ้นเรื่อย ๆ พอกดกระพือ velocity ถูกตั้งใหม่เป็นค่าลบทันที (impulse) — y เลยเด้งกลับขึ้น

velocity เป็นเส้นตรงเพราะเราบวก เท่าเดิมทุกเฟรม ส่วน y โค้งเพราะมัน "สะสม" velocity ที่โตขึ้นเรื่อย ๆ นี่คือ Euler integration แบบง่ายสุด — บวกทีละก้าวเล็ก ๆ เร็ว ๆ จนได้การเคลื่อนที่ที่ดูเป็นธรรมชาติ
Math for Game Developers - Jumping and Gravity (Time Delta, Game Loop) — Jorge Rodriguez · อธิบาย velocity + gravity ในเฟรมลูปแบบเดียวกับที่เราเขียนเป๊ะ
GRAVITY = 0.45
FLAP = -7.4 # แรงกระพือ (ดีดขึ้น)
def on_each_frame():
global bird_y, bird_velocity
keys = game.keys()
if keys.a or keys.up:
bird_velocity = FLAP # กระพือ = ดีดขึ้น (ความเร็วติดลบ)
game.sfx("flap")
bird_velocity += GRAVITY # โน้มถ่วงดึงลงทุกเฟรม
bird_y += bird_velocity # เลื่อนนกตามความเร็ว
if bird_y < 0: # ชนเพดานบน → หยุดความเร็ว
bird_y, bird_velocity = 0, 0
if bird_y > game.HEIGHT - bird.h: # ตกพื้น → จบเกม
game.Text("GAME OVER", 320, 180, game.RED)
return False
bird.move_to(bird.x, bird_y)
ถึงตรงนี้นกจะร่วงลง พอกด A/UP มันก็เด้งขึ้นพร้อมเสียง เราคุมจังหวะให้มันลอยอยู่กลางอากาศได้แล้ว
เสียงในคาบนี้ — Flappy คาบนี้มีสองเสียง:
game.sfx("flap")ตอนกระพือเด้งขึ้น และเสียงตกพื้นตอน GAME OVER (กดเล่นฟังได้จริง):
บรรทัดกระพือ flappy_step2.py:34-35 ทำงาน ทุกเฟรม ตราบใดที่ปุ่มยังถูกกด:
if keys.a or keys.up: # flappy_step2.py:34
bird_velocity = FLAP # :35 — เซ็ตใหม่ทุกเฟรมที่ปุ่มค้าง
bird_velocity จะถูกรีเซ็ตเป็น FLAP (−7.4) ก่อน แล้วโน้มถ่วงบวกกลับนิดเดียว (+GRAVITY = +0.45 ที่ :37) → นกจึง ลอยพุ่งขึ้นค้าง ตราบใดที่ยังกด (โกงเกมได้ด้วยการกดค้าง) if (keys.a or keys.up) and not prev_a: # จับ rising edge เท่านั้น
bird_velocity = FLAP
prev_a = keys.a or keys.up # จำสถานะปุ่มของเฟรมก่อน
นี่คือแนวคิด edge vs level detection เดียวกับปุ่ม FSM คาบ 1–2 (
pressed and not prev) — level ทำเกมง่ายเกิน (กดค้างลอยฟรี) ส่วน edge บังคับให้ "แตะเป็นจังหวะ" คือความยากคลาสสิกของ Flappy Bird · theory → code: flap = level-trigger →flappy_step2.py:34-36
flappy_step2.py ใน BENTO IDEexec(open(...)) เพื่อรันโค้ดนะ วิธีของคอร์สนี้คือกด Program to Device ใน IDE ให้ไฟล์ทั้งก้อนไปรันบนบอร์ดทีเดียว
ดูภาพรวมก่อน: สำหรับคนที่เพิ่งเริ่ม คลิปสั้นนี้แสดงลำดับ "flash เฟิร์มแวร์ → ส่งโค้ด → รันบนบอร์ด" ของ MicroPython ให้เห็นเป็นจังหวะ — แนวคิดเดียวกับปุ่ม Program to Device ของ BENTO IDE (ต่างยี่ห้อบอร์ดเท่านั้น)
MicroPython for Beginners: Flash Firmware, Upload Code & Run! — SparkFun Electronics · เห็นลำดับ flash → upload → run จริง ๆ เป็นแนวทางเดียวกับ Program to Device
ฟิสิกส์ทั้งเกมเกิดจากการ "ทำซ้ำเล็ก ๆ เร็ว ๆ" ไม่มีสูตรลับอะไรเลย มีแค่ 2 บรรทัดที่วนซ้ำกันไป
flappy_step2.py:30-46)ผังก่อนหน้าเป็นลูปแบบย่อ — นี่คือ ทั้งเฟรมจริง รวมกิ่ง กระพือ และกิ่ง ชนเพดาน / ตกพื้น · สัญกรณ์: มนเทา = เริ่ม/จบ · เขียว = อ่าน/วาด · น้ำเงิน = เซ็ตค่า · ส้ม = ฟิสิกส์ · ข้าวหลามตัดม่วง = ตัดสินใจ
theory → code: impulse v ← FLAP (กิ่ง Yes) → :35 · Euler v += g → :37, y += v → :38 · ชนเพดาน clamp → :39-40 · ตกพื้น = game over → :41-43 · render move_to → :44
มีจุดตัดสินใจ 3 จุดต่อเฟรม: กด A/UP? (กระพือ) → ชนเพดาน? (หยุด v) → ตกพื้น? (จบเกม) เส้นประคือ
game.run()วนon_each_frame()50 fps มีเพียงกิ่ง "ตกพื้น" ที่return False
bird_velocity: เมื่อนกร่วง แท่งสีแดงโตลง (v บวก) ยิ่งร่วงนานยิ่งยาว พอกระพือ ค่ากลายเป็นลบทันที (แท่งเด้งไปฝั่งเขียว) นกจึงพุ่งขึ้นบางอย่างในเกมต้องเกิด "ทุก N มิลลิวินาทีเป๊ะ" ไม่ว่าเฟรมจะหนักแค่ไหน — ยกออกจาก game.run loop ไปไว้ที่ machine.Timer:
from machine import Timer
# เลื่อนฉาก/ขยับท่อทุก 500 ms เป๊ะ — เกมไม่กระตุกตามภาระเฟรม
Timer(0, mode=Timer.PERIODIC, period=500, callback=scroll_world)
# ปล่อยท่อใหม่ทุก 2 วินาที (spawn) — เก็บไว้ใช้ตอนเพิ่มท่อคาบหน้า
Timer(1, mode=Timer.PERIODIC, period=2000, callback=spawn_pipe)

callback ควร สั้น (ตั้ง flag / อัปเดตตัวแปร) แล้วให้
on_each_frame()ในgame.runวาด — หลักเดียวกับ interrupt handler คาบ 1 (Eva Game มี Timer id 0–2 ให้ใช้)
ลองปรับ ทีละค่า ในไฟล์ แล้วกด Program to Device ใหม่ ดูความรู้สึกที่เปลี่ยนไป:
| ลองเปลี่ยน | ผลที่เห็น |
|---|---|
GRAVITY = 0.30 |
นกตกช้า เล่นง่าย |
GRAVITY = 0.70 |
นกตกเร็ว เล่นยาก |
FLAP = -5.0 |
กระพือเด้งขึ้นน้อย |
FLAP = -10.0 |
กระพือพุ่งขึ้นสูง |
พอเราแยก
GRAVITYกับFLAPออกมาเป็นค่าคงที่ เราก็เปลี่ยน "ความรู้สึก" ของเกมได้ โดยไม่ต้องไปยุ่งกับตรรกะเลย
FLAP = +7.4 กดแล้วพุ่งลงFLAP = -7.4 กดแล้วเด้งขึ้น (ลบ = ขึ้น)global bird_y, bird_velocity ทำให้ Python มองเป็นตัวแปรใหม่ นกเลยไม่ขยับFLAP เป็นบวก กดแล้วนกพุ่งลง (จำไว้นะว่า ลบ = ขึ้น)bird_velocity = 0 แทนที่จะเป็น = FLAP นกจะหยุดนิ่งกลางอากาศ ไม่เด้งif bird_y > HEIGHT - bird.h นกจะทะลุขอบล่างหายไปreturn False ใช้ตัวเล็ก เกมจะไม่หยุด (ต้องเป็น F ตัวใหญ่ คืนค่าจาก on_each_frame)ข้อควรระวัง:
game.tilt()(เอียงบอร์ดคุมนก) มีอยู่ใน API ก็จริง แต่ BMI270 ยังไม่ทำงานบนเฟิร์มแวร์ Game เพราะฉะนั้นให้คุมด้วยปุ่ม A/UP เท่านั้น อย่าเพิ่งไปสัญญาน้อง ๆ ว่าเอียงเล่นได้
ฟิสิกส์นกที่น้องเพิ่งเขียน ไม่ใช่ของเล่นแยกจากงานจริง มันคือรากฐานเดียวกับที่เราใช้ทำผลิตภัณฑ์:
v += g แล้ว y += v คือ numerical integration (Euler) ตัวเดียวกับที่ใช้ประมาณตำแหน่งจากความเร่ง
BMI270 (accelerometer) วัด g ได้จริง ผ่านบัส I2C (SCB0) — สมการแปลงค่าคือตัวเดียวกัน
BMI270 ที่อยู่บนบอร์ดของน้อง สังเกตขา SDA/SCL นั่นคือบัส I2C 2 เส้นที่ส่งค่า g ออกมาเป็นตัวเลข — สมการแปลงค่าเป็นมุมเอียงก็คือ integration ก้อนเดียวกับฟิสิกส์นกในคาบนี้
เป้าหมายของคอร์สนี้คือ พอน้องเข้าใจ "ทำไมนกตก" น้องก็เข้าใจ "ทำไมหุ่นยนต์รู้ว่ามันเอียง" ด้วยคณิตศาสตร์ก้อนเดียวกัน — เล่นเป็น แล้วต่อยอดไปสร้างของจริงได้
bird_velocity + 2 บรรทัดฟิสิกส์เองในไฟล์ practisefull_games/flappy_full.py
flappy_step1.py : อ่านให้เข้าใจ แล้วปิดไฟล์พิมพ์เองเฉลยนี้มีไว้ เทียบ ไม่ได้มีไว้ลอก คะแนนของคาบอยู่ที่ใบงานกับการอธิบายด้วยคำพูดของน้องเอง ไม่ใช่โค้ดตรงเฉลย วิธีใช้ให้ได้ผลจริงคือ ลองเองก่อนอย่างน้อย 15 นาที ติดตรงไหนอ่านให้เข้าใจ แล้ว ปิดไฟล์ กลับไปพิมพ์ใหม่ด้วยมือตัวเอง — ตอนพิมพ์เองนี่แหละที่สมองจำ pattern ได้ ต่อไปเราจะแกะทีละก้อน ไม่ดูรวดเดียว เพราะทุกบรรทัดมีเหตุผลของมัน
ก้อนแรก คือดึง core แล้วเปิดหน้า Start:
import bentogame as game
game.title("FLAPPY") # หน้าเริ่ม: Start=เล่น Back=ออก (ทำ start ให้ในตัว)
import bentogame as game (:13) ดึง core ที่ให้มาแล้ว 70% เข้ามาก้อนเดียว: Box, Text, run, keys อยู่ในนี้หมด ตรงกับสไลด์ "70 / 30" ที่บอกว่าน้องเขียนแค่ 30% ที่เหลือ core จัดให้game.title("FLAPPY") (:15) สร้างหน้า Start ให้ในตัว กด Start เริ่ม กด Back ออก เราไม่ต้องเขียน state machine ของเมนูเอง นี่คือสิ่งที่คอมเมนต์หมายถึง "ทำ start ให้ในตัว"เริ่มจาก "ของที่รันได้" ตัวเล็กที่สุดก่อนเสมอ อย่าเพิ่งรีบใส่ฟิสิกส์ — เห็นนกบนจอก่อน ค่อยทำให้มันมีชีวิต
# ----- เติมส่วนนี้เอง: ตำแหน่งเริ่มต้นของนก -----
bird_y = 180.0 # ความสูงของนก (พิกเซลจากขอบบน)
bird = game.Box(150, bird_y, 34, 34, game.GB_LIGHTEST) # ตัวนก = กล่องสว่างสุด
score = 0
score_label = game.Text("0", 384, 16, game.WHITE)
bird_y = 180.0 เป็น ทศนิยม ไม่ใช่ 180 เต็ม เพราะคาบหน้า bird_y += bird_velocity จะบวกค่าเศษ (0.45, 0.90 …) ถ้าเก็บเป็น int ตั้งแต่แรก การตกจะกระตุกเป็นขั้น ตรงนี้ตรงกับสไลด์ "velocity ≠ position" ที่ย้ำว่า position ต้องละเอียดพอจะสะสมความเร็วได้ (:18)game.Box(150, bird_y, 34, 34, ...): x คงที่ที่ 150, y ผูกกับตัวแปร bird_y, กล่อง 34×34 นก ไม่ขยับซ้าย-ขวา เลยทั้งเกม ที่ขยับมีแต่แกน y นี่คือเหตุผลที่ Flappy คุมแค่ "ขึ้น-ลง" มิติเดียว (:19)game.GB_LIGHTEST คือสีสว่างสุดในจาน 4 เฉดของ Game Boy จานเดียวกับที่ใช้ใน Snake เลือกเฉดสว่างสุดให้นกตัดกับพื้นหลัง โยงกับเกร็ด Game Boy (1989) ที่เราคุยเรื่องจอ 4 เฉดscore = 0 กับ score_label เป็นการวาง นั่งร้าน (scaffold) ของคะแนนไว้ก่อน ทั้งที่คาบนี้ยังไม่มีอะไรให้นับ เพราะคาบหน้าพอเพิ่มท่อ เราแค่ score += 1 กับป้ายที่มีอยู่แล้ว ไม่ต้องรื้อ — นิสัย "เตรียมที่ไว้ให้ของที่กำลังจะมา" (:20-21)เลือกชนิดข้อมูลตามอนาคตของมัน
bird_yเป็น float เพราะมันกำลังจะสะสมความเร็ว ส่วนscoreเป็น int เพราะมันจะถูกนับทีละหนึ่ง
def on_each_frame():
# ทุกเฟรม วาดนกไว้ที่ความสูง bird_y (ยังไม่ขยับ — เป็นภาพนิ่ง)
bird.move_to(bird.x, bird_y)
if game.keys().start: # กด Start เพื่อออกจากเกม (เหมือนทุกเกม)
return False
# คาบหน้า: เพิ่มแรงโน้มถ่วงให้นกตก แล้วกด A เพื่อ "กระพือ" ขึ้น
game.run(on_each_frame, fps=50) # bentogame.py:353 — ลูปเกม 50 fps
bird.move_to(bird.x, bird_y) ถูกเรียก ทุกเฟรม ทั้งที่ค่ายังไม่เปลี่ยน ดูเหมือนเปลืองเปล่า ๆ แต่นี่คือการวาง นิสัยของ render-loop ไว้ตั้งแต่ต้น พอคาบหน้า bird_y เริ่มเปลี่ยนทุกเฟรม บรรทัดเดิมบรรทัดนี้จะกลายเป็นภาพเคลื่อนไหวทันที โดยไม่ต้องเพิ่มโค้ดวาดใหม่ ตรงกับสไลด์ "render-loop: ฟิสิกส์เกิดจากการทำซ้ำเร็ว ๆ" (:25)if game.keys().start: return False อ่านปุ่มด้วย game.keys() (ของ core 70%) แล้ว คืน False เพื่อออกจากลูป เป็น pattern เดียวกับตอนจบเกมใน step หลัง ที่ return False ตอนตกพื้น จำหลักนี้ไว้: คืน False = บอก game.run ว่า "พอแล้ว" (:26-27):28 จงใจเขียนเตือนตัวเองว่าคาบหน้าจะเติมอะไรตรงนี้ — โน้มถ่วง + กระพือ นี่คือ "ช่องว่างที่ยังไม่เติม" ที่จะกลายเป็นฟิสิกส์ 2 บรรทัดในสไลด์ "ฟิสิกส์ของคาบนี้"game.run(on_each_frame, fps=50) (:30) ส่งฟังก์ชันของเราให้ core เรียกวินาทีละ 50 ครั้ง เราไม่เขียน while True เองเพราะ core ต้องแทรกงานวาดจอ/อ่านจอยระหว่างเฟรมให้ด้วย — หลักเดียวกับที่สไลด์ Timer พูดถึงการแยกจังหวะออกจากงานหนัก
on_each_frameที่ยัง "ว่าง ๆ" วันนี้ ไม่ใช่โค้ดที่ยังไม่เสร็จ แต่คือโครงที่รอเนื้อใน — คาบหน้าเราแค่เติมสองบรรทัดลงในกิ่งที่เว้นไว้
| ก้อน | บรรทัด | หน้าที่ตอนนี้ (step 1) | จะโตเป็นอะไรคาบหน้า |
|---|---|---|---|
import + title |
:13,:15 |
ดึง core + เปิดหน้า Start | เท่าเดิม (ฐานที่ไม่ต้องแตะ) |
bird_y + Box |
:18-19 |
วางนกนิ่งที่ y=180 | bird_y += bird_velocity → นกตกเอง |
| score scaffold | :20-21 |
โชว์ "0" เฉย ๆ | score += 1 เมื่อผ่านท่อ |
on_each_frame + move_to |
:23-25 |
วาดซ้ำภาพนิ่ง | บรรทัดเดิมกลายเป็นภาพเคลื่อนไหว |
keys().start → False |
:26-27 |
กด Start ออกจากเกม | + กิ่งตกพื้น = GAME OVER |
game.run(fps=50) |
:30 |
ลูป 50 fps | เท่าเดิม (หัวใจของทุก step) |
bird_y ของใหม่จริง ๆ คาบหน้าคือตัวแปร bird_velocity กับสมการ 2 บรรทัด ตรงกับที่สไลด์ "70 / 30" วาดกรอบไว้เป๊ะจับจังหวะการไต่ระดับนี้ให้ได้ แล้วน้องจะเห็นว่าเกมทั้งเกมสร้างแบบเดียวกันหมด — เริ่มจากของเล็กสุดที่รันได้ แล้วเติมทีละแนวคิด
ที่มา: ย้อนไปสไลด์ "render-loop: ฟิสิกส์เกิดจากการทำซ้ำเร็ว ๆ" และก่อนหน้านั้นคือ Snake ที่สอนเราวาดของลงจอทุกเฟรม โครง on_each_frame + game.run(fps=50) ไม่ใช่ของใหม่ของ Flappy มันคือ game loop ตัวเดียวกับ Snake เราแค่เอานก 1 กล่องมาวางแทนงูทั้งตัว ลำดับ "วาดก่อน ค่อยขยับทีหลัง" คือสิ่งที่ทำให้ debug ง่าย เพราะเห็นนกอยู่ถูกที่บนจอก่อน แล้วค่อยไปเถียงกับฟิสิกส์คาบหน้า
ที่ไป: bird_y ที่วันนี้เป็นค่าคงที่ 180.0 คือ ตัวแปรตัวเดียว ที่คาบหน้าจะมีชีวิต พอเราเติม bird_velocity += GRAVITY แล้ว bird_y += bird_velocity (สไลด์ "ฟิสิกส์ของคาบนี้") บรรทัด move_to เดิมจะทำให้นกตกเองโดยไม่ต้องแตะอะไรอีก ส่วน score_label ที่โชว์ 0 เฉย ๆ วันนี้ จะกลายเป็นคะแนนจริงตอนใส่ท่อในคาบ 9 จนครบเป็น full_games/flappy_full.py
ลองตอบสามข้อนี้ในใจ นี่แหละคือการเชื่อมจุดด้วยตัวเอง:
game.run(on_each_frame, fps=50) จากสไลด์ render-loop ได้ไหม ถ้า move_to ถูกเรียก 50 ครั้งต่อวินาทีอยู่แล้ว เราต้องเพิ่มโค้ด "วาด" อีกไหมตอนนกเริ่มขยับ? (ใบ้: ไม่ต้องเลย เปลี่ยนแค่ค่า bird_y)bird_y เปลี่ยนทุกเฟรม นกก็ตกเองได้ไหม? (ใบ้: v += g แล้ว y += v สองบรรทัด)move_to ทุกเฟรมของนก คือเรื่องเดียวกับไฟวิ่ง knight-rider ที่กวาดทีละดวงในลูป และ Snake ที่วาดใหม่ทุกเฟรม? มันคือ "ลบของเก่า วาดของใหม่ เร็ว ๆ" อันเดียวกันถ้าตอบสามข้อได้ว่า "อ๋อ มันคือเรื่องเดียวกัน" นั่นคือการหยั่งรู้ที่อยากให้เกิด — นกนิ่งวันนี้ไม่ได้นิ่งเพราะทำได้แค่นี้ มันนิ่งเพราะเรากำลังปูโครงให้ของที่จะขยับ
เทคนิควันนี้ไม่ใช่แค่แบบฝึกหัด มันคือลำดับการทำงานที่วิศวกรจริงใช้ทุกวัน:
bird + score_label ("0") ไว้ก่อนใส่ฟิสิกส์ — เห็นหน้าจอถูกก่อน ค่อยเถียงกับข้อมูลmove_to(bird.x, bird_y) ทุกเฟรมของเรา — pipeline เดียวกับที่เกร็ด Game Boy พูดถึง refresh ทั้งเฟรมscore = 0 ที่ยังไม่มีใครนับคือ placeholder แบบเดียวกันเป๊ะgame.keys().start แล้ว return False คือการออกจากลูปอย่างเป็นระเบียบ ระบบ embedded จริงต้องมีทางออกที่คืนเครื่องสู่สถานะปลอดภัยเสมอ (ปิดมอเตอร์ ดับไฟ) ไม่ปล่อยค้างกลางอากาศห้าจุดที่ดูเล็ก ๆ ในเฉลยวันนี้ ไม่มีจุดไหนเป็นของสมมติเลย ทุกจุดมีเครื่องจริงในโลกที่ทำงานด้วยหลักการเดียวกันอยู่
ลองเอาโจทย์พวกนี้ไปคิดต่อ ไม่มีคำตอบเดียวตายตัว ทุกข้อโยงกลับไปหาของที่พิมพ์เองวันนี้:
bird_y แกว่งด้วย sin ของเลขเฟรม ไม่ต้องสะสมเหมือนแรงโน้มถ่วง)bird_y เริ่มต้น จะออกแบบให้ผู้เล่นเลือกก่อนเข้าเกมยังไง โดยไม่ยุ่งกับโครง on_each_frame?Box เป็นพื้นดิน ควรวาดมันในลูปทุกเฟรม หรือวางครั้งเดียวนอกลูป เพราะอะไร? (โยงกับเรื่อง move_to ทุกเฟรมที่เราเพิ่งคุยกัน)bird_y เป็นค่าคงที่ คาบหน้าเราต้องเพิ่ม bird_velocity แล้วทำให้ทั้งคู่เปลี่ยนใน on_each_frame ลองคิดล่วงหน้า: ทำไมสองตัวนี้ต้องประกาศ global ในฟังก์ชัน แล้วถ้าลืมจะเกิดอะไรกับนก? (เปิดสไลด์ "กับดักที่เจอบ่อย" ทวนได้)เลือกมาสักข้อ แล้วเขียนลงใบงานว่า "ถ้าเป็นเรา จะออกแบบยังไง" ไม่ต้องมีคำตอบถูก ขอแค่คิดต่อจากนกนิ่งที่พิมพ์เองวันนี้ — ตรงนั้นแหละคือจุดที่น้องเริ่มเป็นคนออกแบบเกม ไม่ใช่แค่คนพิมพ์ตามเฉลย
fit-css
Marp deck — Fundamental of Embedded Systems Developer II — คาบ 8 (จาก 13 คาบ) ช่วง Flappy (ฟิสิกส์เกม) — คาบที่ 1 — MicroPython only Render: npx @marp-team/marp-cli session-08.md -o session-08.html