Pyxel을 이용한 2D 게임 시작하기 (파트 15): 터널 회피 게임 2 (샘플)
발행: (2026년 1월 18일 오전 12:00 GMT+9)
5 min read
원문: Dev.to
Source: Dev.to
번역할 텍스트가 제공되지 않았습니다. 번역이 필요한 본문을 알려주시면 한국어로 번역해 드리겠습니다.
에셋
Pyxel Editor를 사용하여 배경 에셋을 만들고 리소스 파일을 다운로드하세요:
Download – 페이지 오른쪽 상단에 있는 Download 버튼을 클릭하여 파일을 받으세요.
전체 샘플 코드
sprite.py
import pyxel
import math
import random
class BaseSprite:
def __init__(self, x, y, w=8, h=8):
"""Constructor"""
self.x = x
self.y = y
self.w = w
self.h = h
self.vx = 0
self.vy = 0
def update(self):
"""Update processing"""
self.x += self.vx
self.y += self.vy
def draw(self):
"""Draw processing (implemented in subclasses)"""
pass
def move(self, spd, deg):
"""Move by speed and angle"""
rad = deg * math.pi / 180
self.vx = spd * math.cos(rad) # Velocity on X axis
self.vy = spd * math.sin(rad) # Velocity on Y axis
def intersects(self, other):
"""Rectangle collision detection (AABB)"""
if other.x + other.w < self.x:
return False
if self.x + self.w < other.x:
return False
if other.y + other.h < self.y:
return False
if self.y + self.h < other.y:
return False
return True
class PlayerSprite(BaseSprite):
def __init__(self, x, y):
"""Constructor"""
super().__init__(x, y)
self.gravity = 0.4 # Gravity
self.jump_x = 1.0 # Jump X velocity
self.jump_y = -3.4 # Jump Y velocity
def update(self):
"""Update processing"""
super().update()
self.vy += self.gravity
def draw(self):
"""Draw processing"""
pyxel.blt(self.x, self.y, 0, 0, 16, self.w, self.h, 0)
# Debug
# pyxel.rectb(self.x, self.y, self.w, self.h, 3)
def jump(self):
"""Jump"""
self.vx = self.jump_x
self.vy = self.jump_y
class TunnelSprite(BaseSprite):
def __init__(self, x, y, length, top_flg=False):
"""Constructor"""
super().__init__(x, y, 16, length * 8)
self.length = length # Tunnel length
if top_flg:
self.y -= self.h # Upper tunnel
def draw(self):
"""Draw processing"""
# Top
pyxel.blt(self.x, self.y, 0, 16, 16, self.w, 8, 0)
# Middle
for i in range(self.length - 2):
y = self.y + (i + 1) * 8
pyxel.blt(self.x, y, 0, 16, 24, self.w, 8, 0)
# Bottom
y = self.y + (self.length - 1) * 8
pyxel.blt(self.x, y, 0, 16, 32, self.w, 8, 0)
# Debug
# pyxel.rectb(self.x, self.y, self.w, self.h, 3)
main.py
import pyxel
import math
import random
import sprite
# ----------------------------------------------------------------------
# Constants
# ----------------------------------------------------------------------
W, H = 160, 120
START_X = W / 2 - 48
START_Y = H / 2 - 12
MODE_TITLE = "title"
MODE_PLAY = "play"
MODE_GAME_OVER = "game_over"
TUNNEL_TOTAL = 48
class Game:
def __init__(self):
"""Constructor"""
# Initialize score
self.score = 0
# Game mode
self.game_mode = MODE_TITLE
# Initialize player
self.player = sprite.PlayerSprite(START_X, START_Y)
# Initialize stage
self.reset()
# Start Pyxel
pyxel.init(W, H, title="Hello, Pyxel!!")
pyxel.load("flappy.pyxres")
pyxel.run(self.update, self.draw)
# ------------------------------------------------------------------
# Core loop
# ------------------------------------------------------------------
def update(self):
"""Update processing"""
# Update score
self.score = int(self.player.x - START_X)
# Control input
self.control()
# Skip the rest if we are not in play mode
if self.game_mode != MODE_PLAY:
return
# Update player
self.player.update()
# Update tunnels
for tunnel in self.tunnels:
tunnel.update()
if tunnel.intersects(self.player):
self```
> **Source:** ...
```python
.game_mode = MODE_GAME_OVER
break
# Falling check
if H < self.player.y:
self.game_mode = MODE_GAME_OVER
def draw(self):
"""Draw processing"""
pyxel.cls(6)
# Tilemap background
pyxel.bltm(0, 0, 0, 0, 0, 192, 128, 0)
# Camera (set)
pyxel.camera(self.player.x - START_X, 0)
# Draw player
self.player.draw()
# Draw tunnels
for tunnel in self.tunnels:
tunnel.draw()
# Camera (reset)
pyxel.camera()
# Messages
if self.game_mode == MODE_TITLE:
msg = "SPACE TO PLAY"
pyxel.text(W / 2 - len(msg) * 2, H / 2, msg, 1)
elif self.game_mode == MODE_GAME_OVER:
msg = "GAME OVER"
pyxel.text(W / 2 - len(msg) * 2, H / 2, msg, 1)
# Draw score
pyxel.text(10, 10, f"SCORE:{self.score:04}", 1)
# ------------------------------------------------------------------
# Helper methods
# ------------------------------------------------------------------
def reset(self):
"""Initialize stage"""
# Player position
self.player.x = START_X
self.player.y = START_Y
# Tunnels
self.tunnels = []
for i in range(TUNNEL_TOTAL):
pad_x = 42
pad_y = random.randint(2, 3) * 8
x = START_X + pad_x * i + 32
y = H / 2 + random.randint(-2, 2) * 8
# Upper tunnel
t_top = sprite.TunnelSprite(x, y - pad_y, 10, True)
self.tunnels.append(t_top)
# Lower tunnel
t_bottom = sprite.TunnelSprite(x, y + pad_y, 10)
self.tunnels.append(t_bottom)
def control(self):
"""Control input"""
if not pyxel.btnp(pyxel.KEY_SPACE):
return
# Title → Play
if self.game_mode == MODE_TITLE:
self.game_mode = MODE_PLAY
# Game Over → Title
if self.game_mode == MODE_GAME_OVER:
self.game_mode = MODE_TITLE
self.reset()
# Jump (only in play mode)
if self.game_mode == MODE_PLAY:
self.player.jump()
def main():
Game()
if __name__ == "__main__":
main()
""" Main process """
Game()
if __name__ == "__main__":
main()
게임을 실행하면 다음과 같이 보일 것입니다.
이 글을 읽어 주셔서 대단히 감사합니다.
이 시리즈가 게임 개발을 시작하는 데 도움이 되길 바랍니다.
(재미있으셨다면 👍을 눌러 주시면 정말 감사하겠습니다!)