본문 바로가기

[Pygame] ✈️ 전투기 슈팅 게임 만들기 13강 | 아이템 생성 & 회복 시스템 구현

@도마22026. 1. 23. 19:00
728x90


1. 이번 강의 목표

이번 강의에서는 아이템 시스템을 추가합니다.

구현할 내용은 다음과 같습니다.

  • 적 처치 시 10% 확률로 힐 아이템 생성
  • 플레이어가 힐 아이템과 닿으면 체력 1 회복
  • 체력은 최대 체력(max_hp)을 넘지 않도록 제한

이 기능이 들어가면 게임이 훨씬 “게임답게” 변합니다.


2. 준비물: 힐 아이템 스프라이트

images/
 ├─ heal.png   (10 x 10)
  • 크기: 10x10
  • 투명 배경 PNG 권장
  • 단순한 십자가/하트 형태가 가장 보기 좋음

게임에서는 살짝 키워서 사용해도 되고, 그대로 사용해도 됩니다.

이번 강의에서는 16x16으로 확대해서 사용합니다.


3. 힐 아이템 스프라이트 로드

적/총알과 동일하게 이미지 로드 후 크기 조정합니다.

heal_img = pygame.image.load("images/heal.png").convert_alpha()
heal_img = pygame.transform.scale(heal_img, (16, 16))

4. 힐 아이템 데이터 구조

힐 아이템도 적과 마찬가지로 Rect + 속성을 함께 관리합니다.

heals = []

아이템 하나는 다음 정보를 가집니다.

  • rect: 위치/충돌
  • speed: 아래로 내려오는 속도

딕셔너리 형태로 저장합니다.


5. 적 처치 시 10% 확률로 힐 아이템 생성

총알 ↔ 적 충돌 처리 부분에 아이템 생성 로직을 추가합니다.

if random.random() < 0.1:
    heal_rect = heal_img.get_rect()
    heal_rect.center = enemy["rect"].center

    heals.append({
        "rect": heal_rect,
        "speed": enemy["speed"] // 2 if enemy["speed"] > 1 else 1
    })
  • random.random() < 0.1 → 10% 확률
  • 적 위치에서 아이템 생성
  • 최소 속도는 1로 보정

6. 힐 아이템 이동 처리

매 프레임마다 힐 아이템을 아래로 이동시킵니다.

for heal in heals:
    heal["rect"].y += heal["speed"]

 


7. 화면 밖 힐 아이템 제거

화면 아래로 나간 아이템은 제거합니다.

for heal in heals[:]:
    if heal["rect"].top > SCREEN_HEIGHT:
        heals.remove(heal)

8. 플레이어 ↔ 힐 아이템 충돌 처리

플레이어와 힐 아이템이 닿으면 체력을 회복합니다.

for heal in heals[:]:
    if player_rect.colliderect(heal["rect"]):
        if hp < max_hp:
            hp += 1
        heals.remove(heal)

 

  • 체력은 max_hp를 초과하지 않음
  • 회복 후 아이템 제거

 

9. 힐 아이템 그리기

다른 오브젝트와 동일하게 blit()으로 출력합니다.

for heal in heals:
    screen.blit(heal_img, heal["rect"])

10. 마무리

이번 강의에서는 다음을 구현했습니다.

  • 적 처치 시 10% 확률 힐 아이템 드랍
  • 힐 아이템 하강 이동
  • 플레이어 충돌 시 체력 1 회복
  • 최대 체력 제한 처리

전체 코드

더보기
import pygame
import sys
import random

pygame.init()

SCREEN_WIDTH = 480
SCREEN_HEIGHT = 640
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Shooting Game")

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

# ===== 배경 =====
bg_img = pygame.image.load("images/background.png").convert()
bg_img = pygame.transform.scale(bg_img, (SCREEN_WIDTH, SCREEN_HEIGHT))
bg_speed = 2
bg_y1 = 0
bg_y2 = -SCREEN_HEIGHT

# ===== 스프라이트 =====
player_img = pygame.image.load("images/player.png").convert_alpha()
enemy_img = pygame.image.load("images/enemy.png").convert_alpha()      # NORMAL
enemy1_img = pygame.image.load("images/enemy1.png").convert_alpha()    # ZIGZAG
bullet_img = pygame.image.load("images/bullet.png").convert_alpha()
heal_img = pygame.image.load("images/heal.png").convert_alpha()

player_img = pygame.transform.scale(player_img, (40, 40))
enemy_img = pygame.transform.scale(enemy_img, (40, 40))
enemy1_img = pygame.transform.scale(enemy1_img, (40, 40))
bullet_img = pygame.transform.scale(bullet_img, (6, 16))
heal_img = pygame.transform.scale(heal_img, (16, 16))  # 원본 10x10 → 보기 좋게 약간 확대

font = pygame.font.SysFont(None, 36)
big_font = pygame.font.SysFont(None, 72)

def reset_game():
    player_rect = player_img.get_rect()
    player_rect.centerx = SCREEN_WIDTH // 2
    player_rect.bottom = SCREEN_HEIGHT - 20

    bullets = []
    enemies = []   # dict list: {"rect","img","speed","vx","type"}
    heals = []     # dict list: {"rect","speed"}

    score = 0
    max_hp = 3
    hp = max_hp

    enemy_timer = 0
    last_fire_time = 0
    last_hit_time = 0
    game_over = False

    return (
        player_rect, bullets, enemies, heals,
        score, max_hp, hp,
        enemy_timer, last_fire_time, last_hit_time,
        game_over
    )

# ===== 기본 설정 값 =====
player_speed = 5

bullet_speed = 8
fire_delay = 150

enemy_spawn_delay = 60  # 프레임 기준(60이면 약 1초)
hit_delay = 1000        # ms

# 게임 초기화
player_rect, bullets, enemies, heals, score, max_hp, hp, enemy_timer, last_fire_time, last_hit_time, game_over = reset_game()

running = True
while running:
    now = pygame.time.get_ticks()

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    keys = pygame.key.get_pressed()

    # ===== 게임 오버 상태 =====
    if game_over:
        if keys[pygame.K_r]:
            player_rect, bullets, enemies, heals, score, max_hp, hp, enemy_timer, last_fire_time, last_hit_time, game_over = reset_game()

        screen.fill((0, 0, 0))

        over_text = big_font.render("GAME OVER", True, (255, 255, 255))
        score_text = font.render(f"Score : {score}", True, (255, 255, 255))
        restart_text = font.render("Press R to Restart", True, (255, 255, 255))

        screen.blit(over_text, (SCREEN_WIDTH // 2 - over_text.get_width() // 2, 220))
        screen.blit(score_text, (SCREEN_WIDTH // 2 - score_text.get_width() // 2, 320))
        screen.blit(restart_text, (SCREEN_WIDTH // 2 - restart_text.get_width() // 2, 370))

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

    # ===== 배경 스크롤 =====
    bg_y1 += bg_speed
    bg_y2 += bg_speed
    if bg_y1 >= SCREEN_HEIGHT:
        bg_y1 = -SCREEN_HEIGHT
    if bg_y2 >= SCREEN_HEIGHT:
        bg_y2 = -SCREEN_HEIGHT

    # ===== 플레이어 이동 =====
    if keys[pygame.K_w]:
        player_rect.y -= player_speed
    if keys[pygame.K_s]:
        player_rect.y += player_speed
    if keys[pygame.K_a]:
        player_rect.x -= player_speed
    if keys[pygame.K_d]:
        player_rect.x += player_speed

    # 화면 밖 제한
    player_rect.clamp_ip(screen.get_rect())

    # ===== 총알 발사(연사 제한) =====
    if keys[pygame.K_SPACE] and now - last_fire_time >= fire_delay:
        last_fire_time = now
        b = bullet_img.get_rect()
        b.centerx = player_rect.centerx
        b.bottom = player_rect.top
        bullets.append(b)

    # 총알 이동/제거
    for b in bullets:
        b.y -= bullet_speed
    bullets[:] = [b for b in bullets if b.bottom > 0]

    # ===== 적 생성(7:3) =====
    enemy_timer += 1
    if enemy_timer >= enemy_spawn_delay:
        enemy_timer = 0

        if random.random() < 0.7:
            img = enemy_img
            vx = 0
            enemy_type = "NORMAL"
        else:
            img = enemy1_img
            vx = random.choice([-2, 2])
            enemy_type = "ZIGZAG"

        r = img.get_rect()
        r.x = random.randint(0, SCREEN_WIDTH - r.width)
        r.y = -r.height

        enemies.append({
            "rect": r,
            "img": img,
            "type": enemy_type,
            "speed": 3,
            "vx": vx
        })

    # ===== 적 이동/제거 =====
    for e in enemies:
        e["rect"].y += e["speed"]

        if e["type"] == "ZIGZAG":
            e["rect"].x += e["vx"]
            if e["rect"].left <= 0 or e["rect"].right >= SCREEN_WIDTH:
                e["vx"] *= -1

    enemies[:] = [e for e in enemies if e["rect"].top <= SCREEN_HEIGHT]

    # ===== 총알 vs 적 =====
    for b in bullets[:]:
        for e in enemies[:]:
            if b.colliderect(e["rect"]):
                bullets.remove(b)
                enemies.remove(e)
                score += 10

                # ===== 힐 아이템 드랍(10%) =====
                if random.random() < 0.1:
                    h = heal_img.get_rect()
                    h.center = e["rect"].center
                    # 적 속도의 절반(최소 1)
                    heal_speed = max(1, e["speed"] // 2)
                    heals.append({"rect": h, "speed": heal_speed})

                break

    # ===== 힐 아이템 이동/제거 =====
    for h in heals:
        h["rect"].y += h["speed"]
    heals[:] = [h for h in heals if h["rect"].top <= SCREEN_HEIGHT]

    # ===== 플레이어 vs 힐 =====
    for h in heals[:]:
        if player_rect.colliderect(h["rect"]):
            if hp < max_hp:
                hp += 1
            heals.remove(h)

    # ===== 플레이어 피격(무적 시간) =====
    if now - last_hit_time >= hit_delay:
        for e in enemies[:]:
            if player_rect.colliderect(e["rect"]):
                hp -= 1
                last_hit_time = now
                enemies.remove(e)
                break

    # 게임 오버 체크
    if hp <= 0:
        game_over = True

    # ===== 그리기 =====
    screen.blit(bg_img, (0, bg_y1))
    screen.blit(bg_img, (0, bg_y2))

    screen.blit(player_img, player_rect)

    for b in bullets:
        screen.blit(bullet_img, b)

    for e in enemies:
        screen.blit(e["img"], e["rect"])

    for h in heals:
        screen.blit(heal_img, h["rect"])

    # 점수 UI
    screen.blit(font.render(f"Score : {score}", True, (255, 255, 255)), (10, 10))

    # HP UI (숫자)
    screen.blit(font.render(f"HP : {hp}", True, (255, 255, 255)), (10, 45))

    # HP UI (바)
    bar_w = 100
    bar_h = 12
    bar_x = 80
    bar_y = 53

    pygame.draw.rect(screen, (80, 80, 80), (bar_x, bar_y, bar_w, bar_h))
    current_w = int(bar_w * (hp / max_hp))
    pygame.draw.rect(screen, (255, 255, 255), (bar_x, bar_y, current_w, bar_h))

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

pygame.quit()
sys.exit()

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

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

목차