본문 바로가기

[Pygame] 🏰 2D 타워 디펜스 게임 만들기 3강 | 적 만들기 & 경로 따라 이동시키기

@도마22026. 2. 3. 19:00
728x90


이번 3강에서는 그 경로를 실제로 사용해 적이 스폰되고 이동하도록 구현합니다.

목표는 아래 3가지입니다.

  • 적(Enemy) 클래스 만들기
  • 웨이포인트(path_points)를 따라 이동시키기
  • 화면에 적을 그려 “움직이는 게임” 느낌 만들기

1. Enemy 클래스 설계

적은 최소한 아래 정보가 필요합니다.

  • 현재 위치(x, y)
  • 이동 속도(speed)
  • 경로 좌표 리스트(path_points)
  • 현재 목표 웨이포인트 인덱스(target_index)

아래처럼 클래스를 만듭니다.

class Enemy:
    def __init__(self, path_points, speed=2):
        self.path_points = path_points
        self.speed = speed

        self.x, self.y = path_points[0]
        self.target_index = 1  # 다음 목표 좌표

        self.radius = 12  # 적 크기(원으로 표시)

    def update(self):
        if self.target_index >= len(self.path_points):
            return  # 경로 끝까지 도달

        tx, ty = self.path_points[self.target_index]

        dx = tx - self.x
        dy = ty - self.y

        dist = (dx * dx + dy * dy) ** 0.5
        if dist == 0:
            self.target_index += 1
            return

        # 목표 방향으로 speed만큼 이동
        self.x += (dx / dist) * self.speed
        self.y += (dy / dist) * self.speed

        # 목표에 충분히 가까워지면 다음 웨이포인트로
        if dist <= self.speed:
            self.x, self.y = tx, ty
            self.target_index += 1

    def draw(self, screen):
        pygame.draw.circle(screen, (220, 80, 80), (int(self.x), int(self.y)), self.radius)

여기서는 스프라이트/이미지 대신 원(circle) 로 적을 표현합니다.
나중에 이미지로 교체하기 쉽고, 구현도 빠릅니다.


2. 적 스폰(생성)하기

이제 적을 하나 만들어서 리스트에 넣고, 매 프레임 update/draw를 호출합니다.

enemies = []
enemies.append(Enemy(path_points, speed=2))

3. 메인 루프에서 적 업데이트 & 그리기

메인 루프에서 아래 순서대로 처리합니다.

  1. 배경 지우기
  2. 경로 그리기
  3. 적 이동(update)
  4. 적 출력(draw)
  5. 화면 갱신
for enemy in enemies:
    enemy.update()
    enemy.draw(screen)

4. 실행 결과

실행하면 적이 경로를 따라 꺾이면서 이동합니다.
이제부터 진짜 타워디펜스 느낌이 나기 시작합니다.


전체 코드

더보기
import pygame
import sys

pygame.init()

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Tower Defense")

clock = pygame.time.Clock()
FPS = 60

path_points = [
    (0, 300),
    (100, 300),
    (100, 100),
    (300, 100),
    (300, 500),
    (650, 500),
    (650, 300),
    (450, 300),
    (450, 100),
    (700, 100),
    (700, 200),
    (800, 200)
]

class Enemy:
    def __init__(self, path_points, speed=2):
        self.path_points = path_points
        self.speed = speed
        self.x, self.y = path_points[0]
        self.target_index = 1
        self.radius = 12

    def update(self):
        if self.target_index >= len(self.path_points):
            return

        tx, ty = self.path_points[self.target_index]
        dx = tx - self.x
        dy = ty - self.y

        dist = (dx * dx + dy * dy) ** 0.5
        if dist == 0:
            self.target_index += 1
            return

        self.x += (dx / dist) * self.speed
        self.y += (dy / dist) * self.speed

        if dist <= self.speed:
            self.x, self.y = tx, ty
            self.target_index += 1

    def draw(self, screen):
        pygame.draw.circle(screen, (220, 80, 80), (int(self.x), int(self.y)), self.radius)

enemies = [Enemy(path_points, speed=2)]

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    screen.fill((30, 30, 30))
    pygame.draw.lines(screen, (200, 180, 100), False, path_points, 40)

    for enemy in enemies:
        enemy.update()
        enemy.draw(screen)

    pygame.display.update()
    clock.tick(FPS)

728x90
도마2
@도마2 :: 도마의 코드노트

초보자를 위한 코딩 강의를 정리합니다. 파이썬부터 C#, Unity 게임 제작까지 차근차근 기록합니다. — 도마

목차