จาก Sprite Sheet สู่เกมแรกของคุณ: สอน Pygame สร้างเกม Python แบบจบในตัว
เคยตัด Sprite Sheet ไว้จนครบแล้ว แต่ยังไม่รู้จะเอามาประกอบเป็นเกมยังไง? บทความนี้จะพาคุณเขียนเกม Python จบในตัว ตั้งแต่ติดตั้ง Pygame จนถึงการชนและควบคุมตัวละคร
สารบัญ

จาก Sprite ที่ตัดไว้ สู่เกมที่เล่นได้จริง
เคยสงสัยไหมว่าเกมอย่าง Hollow Knight หรือ Stardew Valley เริ่มต้นจากอะไร? คำตอบไม่ใช่กราฟิกสวยหรู แต่คือกล่องสี่เหลี่ยมเล็กๆ ที่ขยับได้ตามคำสั่งคีย์บอร์ด
ถ้าคุณเคยอ่านบทความก่อนหน้าเรื่องการตัด Sprite Sheet คุณคงมีไฟล์ภาพตัวละครที่พร้อมใช้งานแล้ว ปัญหาคือเราจะเอาภาพพวกนั้นมาทำอะไรต่อ? บทความนี้เราจะมาต่อยอดกัน ผมจะสอน Pygame สร้างเกม Python แบบจบในตัว ตั้งแต่ติดตั้งไลบรารี โหลด Sprite ควบคุมตัวละคร ไปจนถึงตรวจสอบการชน
เป้าหมายของเราคือเกมตัวอย่างชื่อ "Coin Dash" — เกมที่ผู้เล่นควบคุมตัวละครเก็บเหรียญในจอ หลบสิ่งกีดขวาง และนับคะแนนได้
ขั้นตอนที่ 1: ติดตั้ง Pygame และเตรียมโปรเจกต์
Pygame เป็นไลบรารีที่ต่อยอดจาก SDL ช่วยให้เราเขียนเกม 2D ด้วย Python ได้โดยไม่ต้องกังวลเรื่องระบบกราฟิกระดับล่าง
เปิดเทอร์มินัลแล้วรันคำสั่ง:
pip install pygame
แนะนำให้สร้างโฟลเดอร์โปรเจกต์แยก และใช้ virtual environment เพื่อไม่ให้เกิดความสับสนกับแพ็กเกจอื่น:
mkdir coin_dash
cd coin_dash
python -m venv venv
source venv/bin/activate # บน Windows ใช้ venv\Scripts\activate
pip install pygame
โครงสร้างไฟล์ที่ผมใช้ในบทความนี้:
coin_dash/
├── main.py
├── assets/
│ ├── player_sheet.png
│ └── coin.png
ไฟล์ player_sheet.png คือ Sprite Sheet ที่เราตัดไว้ ส่วน coin.png เป็นไอเทมที่ผู้เล่นจะเก็บ
ขั้นตอนที่ 2: โหลด Sprite จาก Sheet อย่างถูกวิธี
ปัญหาที่มือใหม่มักเจอคือโหลดทั้งภาพ Sheet มาแสดง ทำให้ตัวละครมีเพื่อนแสดงข้างๆ ตลอดเวลา วิธีที่ถูกคือใช้เมธอด subsurface หรือ Surface.subsurface() เพื่อตัดเฟรมที่ต้องการออกมา
สมมติ Sheet ของเรามี 4 เฟรมเรียงแนวนิ่ง แต่ละเฟรมกว้าง 32px สูง 32px:
import pygame
def load_sprite_sheet(filename, frame_width, frame_height, columns, rows):
sheet = pygame.image.load(filename).convert_alpha()
frames = []
for row in range(rows):
for col in range(columns):
x = col * frame_width
y = row * frame_height
rect = pygame.Rect(x, y, frame_width, frame_height)
frame = sheet.subsurface(rect)
frames.append(frame)
return frames
ฟังก์ชันนี้รับชื่อไฟล์ ขนาดเฟรม จำนวนคอลัมน์และแถว แล้วคืนลิสต์ของ Surface ที่ตัดแล้ว คุณสามารถวนลูปเล่นเฟรมเพื่อสร้างอนิเมชันได้
เคล็ดลับสำคัญ: เรียก .convert_alpha() หลังโหลดภาพเสมอ ถ้าไม่ทำ Pygame จะช้าลงมากเพราะต้องแปลงฟอร์แมตพิกเซลทุกครั้งที่วาด
ขั้นตอนที่ 3: สร้างคลาส Player และควบคุมด้วยคีย์บอร์ด
เกมที่ดีไม่ควรมีโค้ดกระจัดกระจาย ผมแนะนำให้แยกตัวละครเป็นคลาส สืบทอดจาก pygame.sprite.Sprite ซึ่งเป็นคลาสพื้นฐานที่ Pygame มอบให้
class Player(pygame.sprite.Sprite):
def __init__(self, x, y, frames):
super().__init__()
self.frames = frames
self.current_frame = 0
self.animation_timer = 0
self.image = self.frames[self.current_frame]
self.rect = self.image.get_rect(topleft=(x, y))
self.speed = 5
self.velocity_y = 0
self.gravity = 0.5
def update(self, keys):
# ควบคุมด้วยคีย์บอร์ด
if keys[pygame.K_LEFT]:
self.rect.x -= self.speed
if keys[pygame.K_RIGHT]:
self.rect.x += self.speed
if keys[pygame.K_UP]:
self.rect.y -= self.speed
if keys[pygame.K_DOWN]:
self.rect.y += self.speed
# อนิเมชัน
self.animation_timer += 1
if self.animation_timer >= 10:
self.animation_timer = 0
self.current_frame = (self.current_frame + 1) % len(self.frames)
self.image = self.frames[self.current_frame]
สังเกตว่าผมใช้ keys = pygame.key.get_pressed() ในลูปหลัก แล้วส่ง keys เข้าไปใน update วิธีนี้ทำให้ผู้เล่นกดปุ่มค้างได้ ต่างจาก pygame.KEYDOWN ที่ทำงานทีเดียวต่อครั้ง
ทำไมไม่ใช้ KEYDOWN?
เพราะเกมแอ็กชันต้องการการตอบสนองต่อเนื่อง ถ้าใช้ KEYDOWN ผู้เล่นจะต้องกดซ้ำเพื่อเดินทีละก้าว ซึ่งไม่ใช่ประสบการณ์ที่ดี
ขั้นตอนที่ 4: ตรวจสอบการชนด้วย Collision Detection
การชน (Collision) คือหัวใจของเกม ไม่ว่าจะเก็บไอเทม ชนศัตรู หรือชนกำแพง ล้วนใช้การตรวจสอบนี้
Pygame มีวิธีตรวจสอบการชนหลายแบบ แต่ที่ใช้บ่อยคือ spritecollide และ colliderect
4.1 การชนระหว่าง Sprite กับ Group
สมมติเรามีกลุ่มเหรียญที่ผู้เล่นต้องเก็บ:
coins = pygame.sprite.Group()
for i in range(5):
coin = Coin(100 + i * 60, 200)
coins.add(coin)
# ในลูปหลัก
hit_coins = pygame.sprite.spritecollide(player, coins, True)
for coin in hit_coins:
score += 10
พารามิเตอร์ตัวที่สาม True หมายถึงลบ Sprite ที่ชนออกจาก Group อัตโนมัติ ถ้าไม่ต้องการให้ลบ ให้ใส่ False
4.2 การชนกับกำแพงแบบกั้นการเคลื่อนที่
กรณีที่ตัวละครไม่ควรทะลุกำแพง ต้องเช็คก่อนย้ายตำแหน่ง:
def update(self, keys, walls):
old_x, old_y = self.rect.x, self.rect.y
if keys[pygame.K_LEFT]:
self.rect.x -= self.speed
if keys[pygame.K_RIGHT]:
self.rect.x += self.speed
# ตรวจสอบการชนแนวนอน
for wall in walls:
if self.rect.colliderect(wall.rect):
self.rect.x = old_x
break
if keys[pygame.K_UP]:
self.rect.y -= self.speed
if keys[pygame.K_DOWN]:
self.rect.y += self.speed
# ตรวจสอบการชนแนวตั้ง
for wall in walls:
if self.rect.colliderect(wall.rect):
self.rect.y = old_y
break
ที่ต้องแยกแนวนอนกับแนวตั้งเพราะถ้ารวมกัน ตัวละครจะติดกำแพงและเลื่อนไม่ได้เลย การแยกช่วยให้ผู้เล่นเลื่อนไปตามผนังได้
ประกอบทุกอย่างเป็นเกมจบในตัว
ตอนนี้เรามีชิ้นส่วนครบแล้ว มาประกอบเป็นเกมที่เล่นได้จริง:
import pygame
import sys
# กำหนดค่าคงที่
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 60
class Coin(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.Surface((20, 20))
self.image.fill((255, 215, 0))
self.rect = self.image.get_rect(center=(x, y))
class Wall(pygame.sprite.Sprite):
def __init__(self, x, y, w, h):
super().__init__()
self.image = pygame.Surface((w, h))
self.image.fill((100, 100, 100))
self.rect = self.image.get_rect(topleft=(x, y))
def main():
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Coin Dash")
clock = pygame.time.Clock()
font = pygame.font.Font(None, 36)
# โหลด Sprite Sheet (สมมติมี 4 เฟรม ขนาด 32x32)
player_frames = load_sprite_sheet("assets/player_sheet.png", 32, 32, 4, 1)
player = Player(50, 50, player_frames)
coins = pygame.sprite.Group()
for i in range(8):
coins.add(Coin(200 + i * 70, 300))
walls = pygame.sprite.Group()
walls.add(Wall(150, 150, 200, 20))
walls.add(Wall(400, 400, 20, 150))
all_sprites = pygame.sprite.Group(player, coins, walls)
score = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
keys = pygame.key.get_pressed()
player.update(keys, walls)
# ตรวจสอบการชนกับเหรียญ
hit_coins = pygame.sprite.spritecollide(player, coins, True)
score += len(hit_coins) * 10
# วาด
screen.fill((30, 30, 40))
all_sprites.draw(screen)
score_text = font.render(f"Score: {score}", True, (255, 255, 255))
screen.blit(score_text, (10, 10))
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
sys.exit()
if __name__ == "__main__":
main()
รันด้วย python main.py แล้วคุณจะได้เกมที่ควบคุมตัวละครเดินได้ ชนกำแพงไม่ทะลุ และเก็บเหรียญเพิ่มคะแนน
จุดที่มือใหม่มักพลาด
ลืมเรียก update ของ Group
ถ้าใช้ all_sprites.update() โดยไม่ส่งพารามิเตอร์ คลาส Player จะ error เพราะต้องการ keys และ walls วิธีแก้คือเรียก player.update() แยก หรือ override ให้คลาสอื่นรับพารามิเตอร์เดียวกันได้
ลืม convert_alpha
ภาพที่ไม่ convert จะมีปัญหา transparency และทำให้เกมกระตุก โดยเฉพาะเมื่อมี Sprite จำนวนมาก
ตรวจสอบการชนผิดจังหวะ
ถ้าตรวจสอบการชนก่อน player.update() ตำแหน่งยังไม่อัปเดต ผลลัพธ์จะผิด ต้องตรวจสอบหลัง update เสมอ
ต่อยอด: อะไรต่อจากนี้?
เกมนี้ยังขาดหลายอย่าง เช่น เสียง หน้าเมนู ระบบเกิดใหม่ หรือศัตรูที่เคลื่อนไหว ลองทำทีละอย่าง:
- เพิ่มเสียงเก็บเหรียญด้วย
pygame.mixer.Sound - สร้างหน้าเมนูเริ่มเกม
- เพิ่มศัตรูที่เดินแบบสุ่มและใช้ collision ตรวจสอบการชนกับผู้เล่น
- เพิ่มระบบเกิดใหม่เมื่อชนศัตรู
สรุป
จาก Sprite Sheet ที่ตัดไว้ ตอนนี้คุณมีเกมที่เล่นได้จริงแล้ว เราผ่านการติดตั้ง Pygame โหลด Sprite แยกเฟรม ควบคุมตัวละครด้วยคีย์บอร์ด และตรวจสอบการชนทั้งแบบเก็บไอเทมและแบบกั้นการเคลื่อนที่
สิ่งที่สำคัญที่สุดไม่ใช่โค้ด แต่คือการเข้าใจว่าแต่ละส่วนทำงานยังไง เมื่อเข้าใจแล้ว คุณจะปรับใช้กับเกมแนวอื่นได้
ลองรันโค้ดนี้แล้วดูว่าเกมของคุณเล่นได้ยังไง ถ้าเจอปัญหาหรืออยากต่อยอดเพิ่ม ฝากคอมเมนต์ไว้คุยกันได้เลย — คุณอยากเพิ่มฟีเจอร์อะไรเป็นอย่างแรกหลังจากนี้?
เนื้อหาที่จัดทำโดยมี AI ช่วยจะมีป้ายกำกับ "เรียบเรียงโดยมี AI ช่วย" เพื่อให้คุณทราบอย่างชัดเจน เราถือว่าความโปร่งใสเรื่องการใช้ AI เป็นสิ่งสำคัญต่อความไว้วางใจของผู้อ่าน
ความคิดเห็น (0)
ยังไม่มีความคิดเห็น — มาเป็นคนแรกกันเถอะ!