Pyxel을 이용한 2D 게임 시작하기 (파트 12): 사운드 효과 재생

발행: (2026년 1월 16일 오전 12:00 GMT+9)
5 min read
원문: Dev.to

Source: Dev.to

번역을 진행하려면 번역하고자 하는 실제 텍스트(본문)를 제공해 주시겠어요?
본문을 알려주시면 원본 형식과 마크다운을 유지하면서 한국어로 번역해 드리겠습니다.

사운드 등록

먼저, Game 클래스의 생성자 안에서 사운드를 등록합니다.
pyxel.sound()의 인자는 사운드 ID를 지정합니다.

  • 사운드 ID – 이 번호는 나중에 사운드를 재생할 때 사용됩니다.

다음으로, 아래 데이터를 set() 메서드에 전달합니다:

매개변수설명
notes (음표)피치를 지정합니다
tones파형 종류 (예: p = 펄스 파형)
volumes볼륨
effects사운드 효과 (예: f = 페이드 아웃)
speed재생 속도 (값이 낮을수록 빠르게 재생)
# main.py
pyxel.sound(0).set(
    "c4g3",
    tones="p",
    volumes="76",
    effects="f",
    speed=20
)

사운드 재생

pyxel.play()를 사용하면 동시에 최대 네 개의 사운드를 재생할 수 있습니다.

  • 첫 번째 인자는 채널 번호(0–3)를 지정합니다.
    같은 채널을 사용할 경우, 새로운 사운드가 이전 사운드를 덮어씁니다.
  • 두 번째 인자는 사운드 ID를 지정합니다.
# main.py
pyxel.play(0, 0)

전체 코드

아래는 이 기사에서 소개한 기능들을 구현한 전체 코드입니다.
(sprite.py 파일은 이전 장과 동일합니다.)

# main.py
import pyxel
import math
import random
import sprite

W, H = 160, 120
SHIP_SPD = 1.4

ASTEROID_INTERVAL = 20
ASTEROID_LIMIT = 30

ASTEROID_SPD_MIN = 1.0
ASTEROID_SPD_MAX = 2.0
ASTEROID_DEG_MIN = 30
ASTEROID_DEG_MAX = 150

BULLET_SPD = 3

# Game
class Game:
    def __init__(self):
        """Constructor"""

        # Game over flag
        self.game_over_flg = False

        # Initialize score
        self.score = 0

        # Initialize player
        self.ship = sprite.ShipSprite(W / 2, H - 40)
        deg = 0 if random.random() < 0.5 else 180
        self.ship.move(SHIP_SPD, deg)

        # Asteroids
        self.asteroid_time = 0
        self.asteroids = []

        # Bullets
        self.bullets = []

        # Initialize Pyxel
        pyxel.init(W, H, title="Hello, Pyxel!!")
        pyxel.load("shooter.pyxres")

        # Sound (shot)
        pyxel.sound(0).set(
            "c4g3",
            tones="p",
            volumes="76",
            effects="f",
            speed=20
        )

        # Sound (hit)
        pyxel.sound(1).set(
            "e4d4c4",
            tones="p",
            volumes="76",
            effects="n",
            speed=20
        )

        pyxel.run(self.update, self.draw)

    def update(self):
        """Update process"""

        # Game over
        if self.game_over_flg:
            return

        # Update player
        self.ship.update()
        self.control_ship()
        self.overlap_spr(self.ship)

        self.check_interval()  # Add asteroids

        # Update asteroids
        for asteroid in self.asteroids:
            asteroid.update()
            self.overlap_spr(asteroid)
            # Collision check (asteroid × player)
            if asteroid.intersects(self.ship):
                self.game_over_flg = True

        # Update bullets (reverse order)
        for bullet in self.bullets[::-1]:
            bullet.update()
            # Remove if outside screen
            if bullet.y < 0:
                self.bullets.remove(bullet)
                continue
            # Collision check (bullet × asteroid)
            for asteroid in self.asteroids[::-1]:
                if asteroid.intersects(bullet):
                    self.score += 1
                    self.bullets.remove(bullet)
                    self.asteroids.remove(asteroid)

                    # Sound (hit)
                    pyxel.play(1, 1)
                    return

    def draw(self):
        """Draw process"""
        pyxel.cls(0)

        # Game over
        if self.game_over_flg:
            msg = "GAME OVER"
            pyxel.text(W / 2 - len(msg) * 2, H / 2, msg, 13)

        # Draw score
        pyxel.text(10, 10, f"SCORE:{self.score:04}", 12)

        # Draw player
        self.ship.draw()

        # Draw asteroids
        for asteroid in self.asteroids:
            asteroid.draw()

        # Draw bullets
        for bullet in self.bullets:
            bullet.draw()

    def control_ship(self):
        """Player action"""
        if pyxel.btnp(pyxel.KEY_SPACE):
            self.ship.flip_x()

            # Fire bullet
            bullet = sprite.BulletSprite(self.ship.x, self.ship.y)
            bullet.move(BULLET_SPD, 270)
            self.bullets.append(bullet)

            # Sound (shot)
            pyxel.play(0, 0)

    def overlap_spr(self, spr):
        """Wrap sprite around the screen"""
        if spr.x < -spr.w:
            spr.x = W
            return
        if W < spr.x:
            spr.x = -spr.w
            return
        if spr.y < -spr.h:
            spr.y = H
            return
        if H < spr.y:
            spr.y = -spr.h
            return

    def check_interval(self):
        # Asteroid spawn interval
        self.asteroid_time += 1
        if self.asteroid_time < ASTEROID_INTERVAL:
            retu```

```python
rn
        self.asteroid_time = 0

        # 소행성 수 제한
        if ASTEROID_LIMIT < len(self.asteroids):
            return

        # 소행성 추가
        x = random.random() * W
        y = 0
        spd = random.uniform(ASTEROID_SPD_MIN, ASTEROID_SPD_MAX)
        deg = random.uniform(ASTEROID_DEG_MIN, ASTEROID_DEG_MAX)
        asteroid = sprite.AsteroidSprite(x, y)
        asteroid.move(spd, deg)
        self.asteroids.append(asteroid)

# 게임 실행
Game()

Kajiru

전체 화면 제어

  • 전체 화면 모드 진입
  • 전체 화면 모드 종료

최종 말씀

여기까지 읽어 주셔서 감사합니다.

이 시리즈가 여러분의 게임 개발 여정에 출발점이 되길 바랍니다.

재미있으셨다면, 👍 하나가 큰 힘이 됩니다!

Back to Blog

관련 글

더 보기 »