ตัวอย่างทั่วไป (ไม่ใช่โค้ดแล็บ) ลองพิมพ์ทีละบรรทัดใน REPL ได้
import random
random.choice(["RED", "ORANGE", "PINK"]) # สุ่มหยิบ 1 ชิ้นจาก list เช่น "PINK"
random.uniform(1.5, 2.5) # สุ่มเลขทศนิยมในช่วง เช่น 1.98
for index, color in enumerate(["RED", "ORANGE"]):
print(index, color) # ได้ 0 RED แล้ว 1 ORANGE
for n in [1, 2, 3, 4]:
if n == 2:
continue # ข้ามที่เหลือของรอบนี้ ไปรอบถัดไป
if n == 4:
break # ออกจากลูปทันที
print(n) # พิมพ์แค่ 1 กับ 3
| ท่า | ในแล็บใช้ทำอะไร |
|---|---|
random.choice(list) · random.uniform(a, b) |
สีสุ่ม random.choice(ENEMY_COLORS) · ความเร็วสุ่ม 1.5 … 2.5 |
enumerate(list) |
ได้ทั้งลำดับ index และตัว enemy → ใช้ enemy_speed[index], respawn_enemy(index) |
continue · break |
ข้ามศัตรูที่หลุดพื้นแล้ว · เลิกเช็คกระสุนที่เหลือเมื่อโดนแล้ว |

sol4:71 · ลูปใน = กระสุน sol4:82 → ลูปวน 6 × 6 = 36 คู่ต่อเฟรม แต่เรียก game.hit จริงเฉพาะนัดที่ใช้อยู่ มากสุด 30 ครั้งbreak = นัดนี้จบศัตรูตัวนี้แล้ว (มันวนขึ้นบนไปแล้ว) · continue = ตัวนี้หลุดพื้นแล้ว ข้ามไปตัวถัดไป| เหตุการณ์ | ตัวเลข | ป้าย HUD | ศัตรู | อื่น ๆ |
|---|---|---|---|---|
| ยิงโดน | score += 1 sol4:84 |
hud.set(...) sol4:86 |
วนกลับขึ้นบน sol4:88 |
คืนกระสุน sol4:87 · เสียง "hit" sol4:85 |
| หลุดพื้น | lives -= 1 sol4:74 |
hud.set(...) sol4:75 |
วนกลับขึ้นบน sol4:76 |
ถ้า lives <= 0 → GAME OVER |
hud.set("Score: %d Lives: %d" % (score, lives)) # sol4:75 (และ sol4:86)
%d = ช่องใส่ตัวเลขจำนวนเต็ม เติมตามลำดับในวงเล็บ: score ลงช่องแรก lives ลงช่องที่สอง
| เหตุการณ์ | score | lives | ป้ายบนจอ |
|---|---|---|---|
| เริ่มเกม | 0 | 3 | Score: 0 Lives: 3 |
| ยิงโดน | 1 | 3 | Score: 1 Lives: 3 |
| หลุดพื้น | 1 | 2 | Score: 1 Lives: 2 |
on_frameแก้scoreกับlivesจึงต้องมีบรรทัดglobal— ให้มาแล้วที่:52อย่าลบ
return False บอก game.run() ให้หยุด
if lives <= 0: # ค ชีวิตหมด = จบเกม
game.sfx("gameover") # เสียงจบ (ยิง = "fire", โดน = "hit")
game.Text("GAME OVER", 320, 180, game.RED)
return False
s04_catch.py:59-63) แต่จบเพราะ ชีวิตหมด · engine เห็น False แล้วเลิกเรียก on_frame (bentogame.py:996-997) · สร้าง game.Text ได้เพราะเกิด ครั้งเดียวpractise_codes/shooter_step4.py↗| ช่อง | บรรทัด | งาน (เขียนเอง) |
|---|---|---|
| (1) ศัตรู 6 ตัว | :34-35 |
สร้าง list ศัตรูครั้งเดียว: x สุ่มในจอ · y สุ่มติดลบ · สีสุ่ม และ enemy_speed สุ่ม |
(2) respawn_enemy(index) |
:48-49 |
วนกลับขึ้นบน: move_to ไป x สุ่ม y ติดลบ + set_color สุ่ม |
| (3+4) ลูปศัตรู | :76-86 |
ขั้น ก → จ ตามผังสไลด์ถัดไป |
ชิ้นส่วนสุ่มสำหรับช่อง (1) (2)
| ชิ้นส่วน | ได้ค่า | ใช้ทำอะไร |
|---|---|---|
random.randint(0, game.WIDTH - 30) |
0 … 762 (จำนวนเต็ม) | x — ศัตรูกว้าง 30 จึงไม่ล้นขอบขวา 792 |
-random.randint(60, 700) |
−700 … −60 | y ตอนสร้าง — เหนือจอ สูงต่ำต่างกันมาก ทยอยลงมา |
-random.randint(100, 500) |
−500 … −100 | y ตอนวนกลับ — หายไปเหนือจอสักพักก่อนโผล่อีกรอบ |
random.uniform(1.5, 2.5) |
1.5 … 2.5 (ทศนิยม) | ความเร็วตก px/เฟรม เก็บใน enemy_speed |
random.choice(ENEMY_COLORS) |
RED / ORANGE / PINK | สีสุ่ม |
ค่าชุดนี้ตั้งให้ช้าพอที่มือใหม่เล่นได้รอบละราว 30–100 วินาที · อยากให้ยากขึ้นลองเพิ่มความเร็วเอง · ความยากแบบมีโหมดอยู่ใน step5


คลิปจำลองตรรกะ shooter_step4.py (ไม่ใช่ภาพจากบอร์ด) · ผู้เล่นอัตโนมัติเลี้ยวยานไปใต้ศัตรูตัวที่ต่ำที่สุด และกด A ค้าง · เลือกรอบที่จบเร็ว (~21 วินาที) มาให้เห็นครบ ปกติยาวกว่านี้มาก
hit · ศัตรูวนกลับขึ้นบน| อาการ | สาเหตุ | แก้ |
|---|---|---|
UnboundLocalError ทันทีที่รัน |
ลบบรรทัด global ที่ :52 ทิ้ง |
ใส่คืน: global ship_x, ship_speed, score, lives, fire_cooldown |
| ยิงโดนแต่เลขบนจอไม่ขึ้น | ลืม hud.set(...) ตอนโดน |
ตัวเลขเปลี่ยนที่ไหน hud.set ที่นั่น (มี 2 ที่) |
NameError: name 'index' ... |
เขียน for enemy in enemies: แต่ใช้ index |
for index, enemy in enumerate(enemies): |
| ยิงโดนแล้วศัตรูไม่หาย ตกต่อจนหลุดพื้น | ลืม respawn_enemy(index) ตอนโดน |
โดนแล้วส่งกลับขึ้นบนทันที |
ลืม break |
บนจอแทบไม่มีทางเห็น แต่ผิดหลัก: ศัตรูที่วนขึ้นบนแล้วยังถูกเช็คต่อ | ใส่ไว้เสมอ = "ตัวนี้จบแล้ว" |
| กระสุนเฉียดขอบแต่ไม่นับ | ไม่ใช่บั๊ก — ขอบชนกันพอดีไม่นับ | ต้องทับกันจริง |
ได้คะแนนตอนศัตรูเพิ่งโผล่ขอบบน (ยังเห็นไม่เต็มตัว) เป็นเรื่องปกติ: นัดที่ยังใช้อยู่แถว y −20 … 0 โดนมันได้ · แถว "ลืม break": จำลองตรรกะบนคอมพิวเตอร์ 100 รอบ ไม่เห็นแต้มซ้ำเลย
game.hit(a, b) · ลูปซ้อน ศัตรู × กระสุน แล้ว breakhud.set(...) ทุกครั้ง · ชีวิตหมด → return False หยุด game.run()| ท่าวันนี้ | ในเกมของน้องเอง | นอกเกม |
|---|---|---|
game.hit(a, b) |
เก็บเหรียญ if game.hit(player, coin): score += 1 · ชนกำแพง |
แตะปุ่มบนจอสัมผัส: จุดนิ้วอยู่ในกรอบปุ่มไหม |
| วนกลับขึ้นบน + ชีวิต/HUD | อุกกาบาตที่ไหลมาไม่รู้จบ · หลอดเลือด hp -= 10 |
แถบแบตเตอรี่ในแอป · ข่าววิ่งวนบนป้าย |
ส่งงาน: โชว์อาจารย์ 1 รอบ (คะแนน ≥ 3 จนชีวิตหมด) + ส่ง shooter_step4.py + ใบงาน · ทีมที่เสร็จแล้ว → ขั้นต่อ step5
practise_codes/shooter_step5.py↗ · 5 ขั้นเดิมของกระสุน ใช้กับศัตรูได้ทุกขั้น| ขั้น | กระสุน (คาบ 11) | ศัตรู step5 (solution_codes/shooter_step5.py) |
|---|---|---|
| ① สร้างชุดครั้งเดียว | bullets = [...] 6 นัด |
enemies = [...] 8 ตัว + enemy_active = [False] * MAX_ENEMIES sol5:65-68 |
| ② หาตัวว่าง | if bullet.y < -20 |
if not enemy_active[index] sol5:80 |
| ③ ยืม | show() + move_to(หัวยาน) |
enemy_active[index] = True + move_to(x สุ่ม, -20) + show() sol5:81-84 |
| ④ อัปเดตทุกเฟรม | bullet.y - 9 |
ข้ามตัวว่าง sol5:129-130 แล้ว enemy.y + enemy_speed[index] sol5:132 |
| ⑤ คืน | พ้นจอบน → hide() |
park_enemy(index): ธง False + hide() + move_to(0, -50) sol5:88-91 |
ต่างกันที่ "ป้ายบอกว่าว่าง": กระสุนดูจาก ตำแหน่ง (
y < -20) · ศัตรูใช้ list ธงenemy_active(True = ใช้อยู่) — อ่านง่ายกว่า ไม่ต้องจำเลข −20 (ตอบคำถามภาคผนวกคาบ 11: ศัตรูใช้ธง)
spawn_timer — ปล่อยศัตรูทีละตัวตามจังหวะ
if spawn_timer > 0:
spawn_timer -= 1
else:
spawn_enemy()
spawn_timer = SPAWN_DELAY
หลักเดียวกับคูลดาวน์กระสุนคาบ 11 (sol5:102-106 · ในไฟล์ practise ให้มาแล้วที่ :112-116): นับถอยหลังทีละเฟรม ถึง 0 แล้วทำ 1 ครั้ง แล้วตั้งใหม่ · ทาย: ถ้าไม่ยิงเลย (และไม่ชนยาน) ชุด 8 ตัวจะหมดไหม?
MODES — ความยากทั้งหมดอยู่ในตารางเดียว| โหมด | ปล่อยทุก (เฟรม) | ตัวคูณความเร็ว | ชีวิต | หลุดพื้นเสีย? | ชนยานเสีย? |
|---|---|---|---|---|---|
| FREE SHOOTER | 16 + 1 = 17 | 0.85 | 99 | ไม่ | ไม่ |
| EASY | 26 + 1 = 27 | 0.70 | 5 | ไม่ | เสีย |
| DIFFICULT | 12 + 1 = 13 | 1.00 | 3 | เสีย | เสีย |
MODE = 2 (sol5:49 · ในไฟล์ practise :57 เริ่มที่ 0) แล้วบรรทัดเดียวแกะทั้งแถวเป็นตัวแปร 6 ตัว (sol5:50)random.uniform(1.8, 3.4) * SPEED_MUL (sol5:85) — สุ่มได้ 3.0 → DIFFICULT 3.0 × 1.00 = 3.0 · EASY 3.0 × 0.70 = 2.1 px/เฟรมon_frame (ข้อมูลขับพฤติกรรม เหมือนตาราง LED_PATTERN คาบ 2)เฉลยที่ทาย: DIFFICULT ศัตรู 1 ตัวอยู่บนจอ 123–233 เฟรม (418 ÷ 3.4 ถึง 418 ÷ 1.8) แต่ปล่อยใหม่ทุก 13 เฟรม → ต้องใช้ราว 10–18 ตัว มีแค่ 8 → ชุดเต็ม spawn_enemy() หาตัวว่างไม่เจอก็ไม่ปล่อย เหมือนกระสุนหมดชุดตอนคูลดาวน์ 4
practise_codes/shooter_step5.py↗| ช่อง | บรรทัด | งาน |
|---|---|---|
| (1) ตารางโหมด | :54-56 |
เพิ่มแถว EASY และ DIFFICULT (ตัวเลขเลือกเองให้ "ยากขึ้นเป็นชั้น") |
(2) spawn_enemy() |
:85-96 |
② หาตัวว่าง (enemy_active[index] เป็น False) → ③ ยืม: ตั้ง True · สีสุ่ม · move_to เหนือจอ · show() · ความเร็ว × SPEED_MUL · return |
| (3) ยานชนศัตรู | :169-178 |
boxes_overlap(...) ยาน × ศัตรู → ⑤ คืน park_enemy(index) → ถ้า LOSE_WHEN_HIT เสีย 1 ชีวิต + hud.set → หมด = GAME OVER |
MODE เป็น 0 / 1 / 2 แล้วเล่นโหมดละ 1 รอบ — จดความต่างลงใบงานboxes_overlap() กล่องชน "หด" (กระสุน 4×10 · ศัตรู 26×16) ต้องยิงเข้าเนื้อจริงถึงนับ — เฉลยอยู่ภาคผนวกเฉลย step4 / step5 · ลึกอีกนิด (สามวิธีเขียนการชน · ทำไมกระสุนไม่ทะลุ · งบ widget) · เปิดฝาเกมเต็ม · เกร็ด · เสียง · ใช้จริงที่ไหน · คิดต่อเอง
enemies = [game.Box(random.randint(0, game.WIDTH - 30), -random.randint(60, 700),
30, 24, random.choice(ENEMY_COLORS)) for _ in range(MAX_ENEMIES)]
enemy_speed = [random.uniform(1.5, 2.5) for _ in range(MAX_ENEMIES)] # ช้าพอให้ยิงทัน
def respawn_enemy(index):
enemies[index].move_to(random.randint(0, game.WIDTH - 30), -random.randint(100, 500))
enemies[index].set_color(random.choice(ENEMY_COLORS))
solution_codes/shooter_step4.py:30-32 และ :41-43
on_frameenemies[i] กับ enemy_speed[i] คือ list คู่ขนาน: ตำแหน่ง i ของทั้งสอง list คือศัตรูตัวเดียวกันrespawn_enemy ไม่แตะ enemy_speed) for index, enemy in enumerate(enemies):
enemy.move_to(enemy.x, enemy.y + enemy_speed[index]) # ก ตกลงหนึ่งก้าว
if enemy.y > game.HEIGHT: # ข หลุดพื้น = เสีย 1 ชีวิต
lives -= 1
hud.set("Score: %d Lives: %d" % (score, lives))
respawn_enemy(index)
if lives <= 0: # ค ชีวิตหมด = จบเกม
game.sfx("gameover")
game.Text("GAME OVER", 320, 180, game.RED)
return False
continue
solution_codes/shooter_step4.py:71-81
Lives: 0 ก่อนจบcontinue = ศัตรูตัวนี้จบเฟรมนี้แล้ว ข้ามไปตัวถัดไป ไม่ต้องเอาไปเช็คกับกระสุน for bullet in bullets: # ง วนกระสุนทุกนัด
if bullet.y >= -20 and game.hit(bullet, enemy): # จ ใช้อยู่ และโดน?
score += 1
game.sfx("hit")
hud.set("Score: %d Lives: %d" % (score, lives))
bullet.move_to(0, -50); bullet.hide() # คืนกระสุน (จอดเหนือจอ = ว่าง)
respawn_enemy(index) # ศัตรูวนกลับขึ้นบน
break # นัดนี้จบศัตรูตัวนี้แล้ว
solution_codes/shooter_step4.py:82-89
bullet.y >= -20 and ... เช็คก่อนว่านัดนี้ใช้อยู่ — ถ้าเป็นนัดว่าง Python ไม่เรียก game.hit เลย (and หยุดที่ข้อแรกที่เป็นเท็จ)move_to(0, -50) + hide(): ย้ายไป y = −50 ทำให้มัน ว่าง ทันที (-50 < -20) พร้อมให้ยืมใหม่hud.set(...) บรรทัด sol4:86 — ไม่งั้นคะแนนขึ้นในตัวแปรแต่ป้ายค้างเลขเดิมspawn_enemy() = หาตัวว่าง + ยืมdef spawn_enemy():
for index in range(MAX_ENEMIES):
if not enemy_active[index]: # (2) ตัวนี้ว่างไหม
enemy_active[index] = True # (3) ยืม: ใช้อยู่แล้ว
enemies[index].set_color(random.choice(ENEMY_COLORS))
enemies[index].move_to(random.randint(0, game.WIDTH - 30), -20)
enemies[index].show()
enemy_speed[index] = random.uniform(1.8, 3.4) * SPEED_MUL # ความเร็ว x โหมด
return
solution_codes/shooter_step5.py:78-86 · ช่อง (1) คือแถว EASY / DIFFICULT ในตารางสไลด์ MODES (sol5:44-48)
find_free_bullet() ทุกประการ: เดินหาตัวแรกที่ว่าง เจอแล้วใช้เลยreturn สำคัญ: ปล่อย ตัวเดียว ต่อการเรียก 1 ครั้ง · ไม่มีตัวว่างเลย → วนจบลูปเฉย ๆ ไม่ปล่อย if boxes_overlap(ship_x, 352, ship.w, ship.h, *enemy_box):
park_enemy(index)
if LOSE_WHEN_HIT:
lives -= 1
hud.set("%s Score: %d Lives: %d" % (MODE_NAME, score, lives))
if lives <= 0:
game.sfx("gameover")
game.Text("GAME OVER", 320, 180, game.RED)
return False
game.sfx("explode") # ยังเหลือชีวิต: เสียงระเบิด (เกมเต็มใช้ "lose_life")
solution_codes/shooter_step5.py:160-169
*enemy_box แกะ tuple 4 ค่า (x, y, 26, 16) ออกมาเป็นอาร์กิวเมนต์ 4 ตัวของกล่องที่สองpark_enemy(index) ก่อนเสมอ (⑤ คืน) ไม่ว่าจะเสียชีวิตหรือไม่ — โหมด FREE (LOSE_WHEN_HIT = False) ศัตรูหายแต่ไม่ลงโทษ| ฟังก์ชัน | อยู่ที่ | แตะขอบพอดี |
|---|---|---|
game.hit(a, b) |
bentogame.py:466-469 |
ไม่นับ — ต้องทับกันจริง (< และ >) |
boxes_overlap(...) |
solution_codes/shooter_step5.py:36-40 |
ไม่นับ · ใช้กล่องชนหด 4×10 และ 26×16 |
overlap(...) |
full_games/shooter_full.py:48-50 |
นับว่าโดน — ต่างจาก game.hit ตรงนี้ |
def overlap(ax, ay, aw, ah, ox, oy, ow, oh):
return not (ax + aw < ox or ax > ox + ow or ay + ah < oy or ay > oy + oh)
or) แล้ว not กลับ — ช่องว่าง 0 px จึงนับว่าโดนgame.hit: 330 < 330 เท็จ → ไม่โดน · overlap: 330 > 330 ก็เท็จ ไม่มีด้านไหนเป็นช่องว่าง → not False → โดนgame.hit เห็นแน่ (ถ้าแกน x ตรงกัน)game.run() 1 (bentogame.py:957) = 15 ชิ้น + ป้าย GAME OVER ตอนจบ = 16bentogame.py:827) → เหลือที่ว่าง 32 − 16 = 16 ชิ้นgame.Box(...) ใหม่ทุกครั้งที่ศัตรูเกิดใหม่: 1 รอบเกิดใหม่ = แต้มที่ได้ + ชีวิตที่เสีย (3) · ผู้เล่นที่ได้ 14 แต้ม ต้องสร้างกล่องใหม่ 14 + 3 = 17 ใบ → เกินงบแล้ว · ผู้เล่นอัตโนมัติแบบคนทั่วไปในการจำลองได้ราว 70–80 แต้มต่อรอบ → เกินงบตั้งแต่ต้นรอบ (ผู้เล่นที่ไม่ยิงเลยจบเกมหลังเกิดใหม่แค่ 3 ครั้ง จึงไม่เห็นปัญหา)full_games/shooter_full.py — เกมเต็ม 278 บรรทัด| ส่วน | บรรทัด | ทำอะไร |
|---|---|---|
ตารางโหมด MODES |
:36-40 |
ค่าเดียวกับ step5 |
overlap() ชนแบบกล่อง |
:48-50 |
เรียกที่ :237 (กระสุน × ศัตรู) และ :250 (ยาน × ศัตรู) |
| เมนูเลือกโหมดบนจอ | :57-64 |
game.menu(...) แทนตัวแปร MODE |
| ชุดหมุนเวียน | :71-76 |
กระสุน 4 นัด (Box) · ศัตรู 5 ตัว และระเบิด 3 ลูก ด้วย game.pool(...) |
| ธงใช้อยู่/ว่าง | :83-88 |
bact, eact — list ธงแบบเดียวกับ enemy_active |
state รวมใน dict st |
:79-82 |
แทนการประกาศ global หลายตัว |
| ปล่อยศัตรู · ยิง | :123-148 |
หาตัวว่าง → ยืม (5 ขั้นเดิม) · กระสุนพกความเร็วยาน :145 |
update() ต่อเฟรม |
:172-262 |
ยาน 2 แกน · pressed_once ยิงทีละนัดต่อการกด :195 · นาฬิกาปล่อยศัตรู :199-203 |
| หลังจบรอบ | :269-278 |
game.run(update, fps=50) → game.game_over(...) เล่นใหม่ได้ |
ที่มา: "Space Invaders" — Tomomarusan, CC BY 2.5, Wikimedia Commons
"fire" ยิง (คาบ 11)
"hit" ยิงโดน
"gameover" จบเกม
fire, hit) บอกผลทันทีทุกครั้งที่ยิง · gameover ยาวกว่าเพื่อปิดรอบ| ท่าในเกม | ระบบจริง |
|---|---|
| ชุดหมุนเวียน (จองครั้งเดียว ยืม-คืน) | บัฟเฟอร์รับแพ็กเก็ตเครือข่าย · วงแหวน DMA บนไมโครคอนโทรลเลอร์ — จองไว้ตั้งแต่เริ่ม ไม่จองใหม่ระหว่างทำงาน |
| ชนแบบกล่อง | ทัชสกรีนหาว่าแตะโดนปุ่มไหน · กรอบวัตถุในระบบตรวจจับภาพ · เอนจินฟิสิกส์ใช้กล่องคัดคู่ที่อาจชนก่อนคำนวณละเอียด |
| ลูปทุกคู่ | สัมผัสหลายนิ้ว × หลายปุ่ม · เตือนชนรถ/โดรน × สิ่งกีดขวางหลายชิ้น |
| สถานะ + หยุดลูป | เครื่องซักผ้า · ตู้ ATM — FSM แบบไฟจราจรคาบ 2 |
นับถอยหลังแล้วทำ (spawn_timer) |
งานตามคาบในระบบปฏิบัติการเวลาจริง: อ่านเซนเซอร์ทุก N tick · ส่งสัญญาณ "ยังอยู่" (heartbeat) |
ต่อจาก shooter_step5.py↗ ทำได้ถึงไหนก็ได้แค่นั้น ทุกข้อหยิบไปใช้ในโปรเจกต์ปลายภาคได้
| ระดับ | โจทย์ | ใบ้ (เทคนิคที่เคยเรียน) |
|---|---|---|
| 3 เติม | ยิ่งเก่งยิ่งยาก — ศัตรูที่เกิดใหม่เร็วขึ้นตาม score แต่ไม่เกินเพดาน |
สูตรเร่งแบบมีเพดานจากคาบ 3: min(ฐาน + score // 5, เพดาน) |
| 3 เติม | ยานบอกว่าเจ็บ — ยานเป็นสีแดง 6 เฟรมเมื่อเสียชีวิต แล้วกลับสีเดิม | ship.set_color(game.RED) + ตัวนับถอยหลังแบบ fire_cooldown (คาบ 11) |
| 4 สร้าง | ระเบิดเป็นภาพ — ยิงโดนแล้วโชว์ภาพระเบิดตรงนั้น 10 เฟรมแล้วซ่อน | boom = game.Sprite("boom1", 0, -50) สร้างครั้งเดียว แล้วย้ายไปจุดที่โดน (ดู full_games/shooter_full.py↗) |
| 4 สร้าง | บอสกับแถบพลัง — บอส 1 ตัว ต้องยิงโดน 5 ครั้ง แถบพลังเป็นกล่องเล็ก 5 ก้อน ซ่อนทีละก้อน | ใช้ .hide() ห้ามย่อกล่องทุกเฟรม · นับวิดเจ็ตรวมไม่เกิน 26 ชิ้น |
ก่อนแก้โค้ด เขียนลงใบงานหนึ่งบรรทัดว่า "ถ้าเป็นเรา จะออกแบบยังไง" — ไม่มีคำตอบถูกข้อเดียว
SPAWN_DELAY / SPEED_MUL กับปุ่มให้เปลี่ยนได้ทันทีระหว่างเล่น (ใน step5 เป็นค่าคงที่ ต้องเปลี่ยนเป็นตัวแปรที่ประกาศ global)fit-css
core
core
core
core
core
core
core
core
core
core
core
core
core
core
core
core
core
core