파이썬으로 시작하는 게임 개발의 세계 🐍🎮
안녕하세요! Python Game Dev 블로그의 열한 번째 포스팅입니다. 이전에는 플랫포머 게임까지 만들어보았는데요, 오늘은 약속드린 대로 '전략 시뮬레이션 게임(Strategy Simulation Game)'을 만들어보겠습니다.
오늘의 게임: 전략 시뮬레이션 게임(Strategy Simulation Game) 🏰⚔️
전략 시뮬레이션 게임은 플레이어가 자원을 관리하고, 유닛을 생산하며, 전략적 결정을 내려 승리하는 장르입니다. 오늘은 파이썬으로 간단한 턴 기반 전략 게임을 구현해보겠습니다. '미니 왕국 전쟁(Mini Kingdom Wars)'이라는 이름의 이 게임에서 플레이어는 자신의 왕국을 관리하며 컴퓨터 AI와 경쟁하게 됩니다.
게임의 규칙 📜
- 플레이어와 AI는 각각 하나의 왕국을 관리합니다.
- 매 턴마다 금화(Gold), 식량(Food), 인구(Population) 자원이 생산됩니다.
- 플레이어는 자원을 사용하여 건물을 짓거나 군대를 훈련할 수 있습니다.
- 건물은 자원 생산량을 증가시킵니다.
- 군대는 공격과 방어에 사용됩니다.
- 플레이어는 매 턴 마다 다른 왕국을 공격할지, 방어를 강화할지, 경제에 투자할지 결정해야 합니다.
- 상대 왕국의 방어력을 무너뜨리면 승리합니다.
전체 코드 💻
import pygame
import sys
import random
import math
from pygame.locals import *
# 게임 초기화
pygame.init()
pygame.font.init()
pygame.mixer.init()
# 화면 설정
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("미니 왕국 전쟁 (Mini Kingdom Wars)")
# 색상 정의
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GOLD = (255, 215, 0)
GREEN = (0, 128, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
LIGHT_BLUE = (100, 149, 237)
BROWN = (139, 69, 19)
GRAY = (128, 128, 128)
# 폰트 설정
title_font = pygame.font.SysFont('malgungothic', 36, bold=True)
main_font = pygame.font.SysFont('malgungothic', 20)
button_font = pygame.font.SysFont('malgungothic', 24)
# 사운드 효과 로드
try:
build_sound = pygame.mixer.Sound("build.wav")
battle_sound = pygame.mixer.Sound("battle.wav")
victory_sound = pygame.mixer.Sound("victory.wav")
defeat_sound = pygame.mixer.Sound("defeat.wav")
button_sound = pygame.mixer.Sound("button.wav")
except:
print("사운드 파일을 찾을 수 없습니다. 게임은 사운드 없이 진행됩니다.")
# 게임 클래스
class Kingdom:
def __init__(self, name, is_player=False):
self.name = name
self.is_player = is_player
self.gold = 100
self.food = 100
self.population = 50
self.army = 10
self.defense = 20
# 건물 및 생산량
self.farms = 1
self.mines = 1
self.houses = 1
self.barracks = 1
self.walls = 1
# 생산량 계산
self.gold_production = 10 + (self.mines * 5)
self.food_production = 10 + (self.farms * 5)
self.population_growth = 2 + (self.houses * 1)
# 전투력 계산
self.attack_power = self.army * 2
self.defense_power = self.defense + (self.walls * 5)
def update_production(self):
# 생산량 업데이트
self.gold_production = 10 + (self.mines * 5)
self.food_production = 10 + (self.farms * 5)
self.population_growth = 2 + (self.houses * 1)
# 전투력 업데이트
self.attack_power = self.army * 2
self.defense_power = self.defense + (self.walls * 5)
def produce_resources(self):
# 자원 생산
self.gold += self.gold_production
self.food += self.food_production
# 인구 증가 (식량이 충분할 때만)
if self.food >= self.population:
max_growth = min(self.population_growth, self.houses * 10 - self.population)
self.population += max(0, max_growth)
self.food -= self.population // 2 # 식량 소비
else:
# 식량 부족 시 인구 감소
self.population = max(10, self.population - 2)
class GameState:
def __init__(self):
self.player = Kingdom("플레이어 왕국", True)
self.ai = Kingdom("AI 왕국")
self.turn = 1
self.message = "게임을 시작합니다. 왕국을 발전시켜 AI를 물리치세요!"
self.game_over = False
self.winner = None
def next_turn(self):
# 플레이어와 AI의 자원 생산
self.player.produce_resources()
self.ai.produce_resources()
# AI의 행동 결정
self.ai_make_decision()
# 턴 증가
self.turn += 1
# 게임 종료 조건 체크
self.check_game_over()
def ai_make_decision(self):
# AI의 의사결정 로직
ai = self.ai
player = self.player
# 자원 업데이트
ai.update_production()
# 기본 전략: 균형 있는 발전
if ai.gold >= 50 and ai.farms < 5:
ai.gold -= 50
ai.farms += 1
return
if ai.gold >= 50 and ai.mines < 5:
ai.gold -= 50
ai.mines += 1
return
if ai.gold >= 50 and ai.houses < 5:
ai.gold -= 50
ai.houses += 1
return
# 군사력 강화
if ai.gold >= 30 and ai.population >= 10:
ai.gold -= 30
ai.population -= 10
ai.army += 5
return
# 방어 강화
if ai.gold >= 40:
ai.gold -= 40
ai.walls += 1
return
# 공격 결정 (AI가 충분히 강하다고 판단할 때)
if ai.attack_power > player.defense_power * 0.7 and ai.army >= 20:
self.ai_attack()
def ai_attack(self):
# AI의 공격 로직
attack_strength = self.ai.attack_power
defense_strength = self.player.defense_power
# 전투 결과 계산
if attack_strength > defense_strength:
damage = attack_strength - defense_strength
self.player.defense = max(0, self.player.defense - damage // 2)
self.player.army = max(0, self.player.army - damage // 4)
# 손실된 AI 군대
self.ai.army = max(5, self.ai.army - defense_strength // 3)
self.message = f"AI가 공격했습니다! 당신의 방어력이 {damage // 2} 감소했고, 군대가 {damage // 4} 감소했습니다."
try:
battle_sound.play()
except:
pass
else:
# AI 공격 실패
self.ai.army = max(5, self.ai.army - defense_strength // 4)
self.message = "AI의 공격을 막아냈습니다! AI 군대가 감소했습니다."
def player_attack(self):
# 플레이어의 공격 로직
attack_strength = self.player.attack_power
defense_strength = self.ai.defense_power
# 전투 결과 계산
if attack_strength > defense_strength:
damage = attack_strength - defense_strength
self.ai.defense = max(0, self.ai.defense - damage // 2)
self.ai.army = max(0, self.ai.army - damage // 4)
# 손실된 플레이어 군대
self.player.army = max(0, self.player.army - defense_strength // 3)
self.message = f"공격 성공! AI의 방어력이 {damage // 2} 감소했고, 군대가 {damage // 4} 감소했습니다."
try:
battle_sound.play()
except:
pass
else:
# 플레이어 공격 실패
self.player.army = max(0, self.player.army - defense_strength // 4)
self.message = "공격에 실패했습니다! 군대가 감소했습니다."
def check_game_over(self):
# 게임 종료 조건 체크
if self.player.defense <= 0:
self.game_over = True
self.winner = "AI"
self.message = "패배했습니다! AI가 당신의 왕국을 정복했습니다."
try:
defeat_sound.play()
except:
pass
elif self.ai.defense <= 0:
self.game_over = True
self.winner = "Player"
self.message = "승리했습니다! 당신이 AI의 왕국을 정복했습니다."
try:
victory_sound.play()
except:
pass
# 버튼 클래스
class Button:
def __init__(self, x, y, width, height, text, color, hover_color, action=None):
self.rect = pygame.Rect(x, y, width, height)
self.text = text
self.color = color
self.hover_color = hover_color
self.action = action
self.is_hovered = False
def draw(self, surface):
color = self.hover_color if self.is_hovered else self.color
pygame.draw.rect(surface, color, self.rect)
pygame.draw.rect(surface, BLACK, self.rect, 2) # 테두리
text_surface = button_font.render(self.text, True, BLACK)
text_rect = text_surface.get_rect(center=self.rect.center)
surface.blit(text_surface, text_rect)
def check_hover(self, pos):
self.is_hovered = self.rect.collidepoint(pos)
return self.is_hovered
def handle_event(self, event):
if event.type == MOUSEBUTTONDOWN and event.button == 1 and self.is_hovered:
try:
button_sound.play()
except:
pass
if self.action:
return self.action()
return None
# 게임 렌더링 함수
def draw_game(screen, game_state, buttons):
screen.fill(WHITE)
# 상단 제목
title_text = title_font.render("미니 왕국 전쟁 (Mini Kingdom Wars)", True, BLACK)
screen.blit(title_text, (SCREEN_WIDTH // 2 - title_text.get_width() // 2, 20))
# 턴 정보
turn_text = main_font.render(f"턴: {game_state.turn}", True, BLACK)
screen.blit(turn_text, (SCREEN_WIDTH - 100, 30))
# 왕국 정보 패널 (플레이어)
player_panel = pygame.Rect(50, 70, 300, 200)
pygame.draw.rect(screen, LIGHT_BLUE, player_panel)
pygame.draw.rect(screen, BLUE, player_panel, 2)
player_title = main_font.render(game_state.player.name, True, BLACK)
screen.blit(player_title, (player_panel.x + 10, player_panel.y + 10))
# 플레이어 자원 정보
p = game_state.player
resource_y = player_panel.y + 40
resource_texts = [
f"금화: {p.gold} (+{p.gold_production})",
f"식량: {p.food} (+{p.food_production})",
f"인구: {p.population} (+{p.population_growth})",
f"군대: {p.army} (공격력: {p.attack_power})",
f"방어력: {p.defense_power} (성벽: {p.walls})",
f"농장: {p.farms}, 광산: {p.mines}, 주택: {p.houses}"
]
for text in resource_texts:
text_surface = main_font.render(text, True, BLACK)
screen.blit(text_surface, (player_panel.x + 10, resource_y))
resource_y += 25
# 왕국 정보 패널 (AI)
ai_panel = pygame.Rect(450, 70, 300, 200)
pygame.draw.rect(screen, LIGHT_BLUE, ai_panel)
pygame.draw.rect(screen, RED, ai_panel, 2)
ai_title = main_font.render(game_state.ai.name, True, BLACK)
screen.blit(ai_title, (ai_panel.x + 10, ai_panel.y + 10))
# AI 자원 정보 (일부만 공개)
a = game_state.ai
ai_resource_y = ai_panel.y + 40
ai_resource_texts = [
f"추정 금화: {a.gold // 10 * 10} ~ {a.gold // 10 * 10 + 10}",
f"추정 식량: {a.food // 10 * 10} ~ {a.food // 10 * 10 + 10}",
f"추정 인구: {a.population // 5 * 5} ~ {a.population // 5 * 5 + 5}",
f"관측된 군대: {a.army} (추정 공격력: {a.attack_power})",
f"관측된 방어력: {a.defense_power}",
f"관측된 건물: 농장, 광산, 성벽 등 존재"
]
for text in ai_resource_texts:
text_surface = main_font.render(text, True, BLACK)
screen.blit(text_surface, (ai_panel.x + 10, ai_resource_y))
ai_resource_y += 25
# 메시지 영역
message_rect = pygame.Rect(50, 290, 700, 50)
pygame.draw.rect(screen, WHITE, message_rect)
pygame.draw.rect(screen, BLACK, message_rect, 1)
message_text = main_font.render(game_state.message, True, BLACK)
screen.blit(message_text, (message_rect.x + 10, message_rect.y + 15))
# 버튼 그리기
for button in buttons:
button.draw(screen)
# 게임 오버 화면
if game_state.game_over:
overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 128))
screen.blit(overlay, (0, 0))
if game_state.winner == "Player":
result_text = title_font.render("승리했습니다!", True, GOLD)
else:
result_text = title_font.render("패배했습니다!", True, RED)
screen.blit(result_text, (SCREEN_WIDTH // 2 - result_text.get_width() // 2, SCREEN_HEIGHT // 2 - 50))
pygame.display.flip()
# 메인 게임 루프
def main():
clock = pygame.time.Clock()
game_state = GameState()
# 버튼 생성
buttons = []
# 건물 버튼
build_y = 350
buttons.append(Button(50, build_y, 170, 40, "농장 건설 (50G)", GREEN, (100, 200, 100),
lambda: build_farm(game_state)))
buttons.append(Button(230, build_y, 170, 40, "광산 건설 (50G)", BROWN, (150, 100, 50),
lambda: build_mine(game_state)))
buttons.append(Button(410, build_y, 170, 40, "주택 건설 (50G)", BLUE, (80, 120, 200),
lambda: build_house(game_state)))
buttons.append(Button(590, build_y, 170, 40, "성벽 강화 (40G)", GRAY, (150, 150, 150),
lambda: build_wall(game_state)))
# 군대/행동 버튼
army_y = 400
buttons.append(Button(50, army_y, 210, 40, "군대 훈련 (30G, 10인구)", RED, (200, 100, 100),
lambda: train_army(game_state)))
buttons.append(Button(270, army_y, 210, 40, "AI 왕국 공격", RED, (200, 100, 100),
lambda: attack(game_state)))
buttons.append(Button(490, army_y, 210, 40, "턴 넘기기", LIGHT_BLUE, (150, 200, 255),
lambda: next_turn(game_state)))
# 게임 재시작 버튼
restart_button = Button(SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 + 50, 200, 50,
"게임 재시작", GOLD, (255, 235, 100), lambda: restart_game())
running = True
while running:
mouse_pos = pygame.mouse.get_pos()
for button in buttons:
button.check_hover(mouse_pos)
for event in pygame.event.get():
if event.type == QUIT:
running = False
pygame.quit()
sys.exit()
# 버튼 클릭 처리
if not game_state.game_over:
for button in buttons:
button.handle_event(event)
else:
# 게임 오버 상태에서는 재시작 버튼만 처리
restart_button.check_hover(mouse_pos)
restart_button.handle_event(event)
# 화면 그리기
draw_game(screen, game_state, buttons if not game_state.game_over else [restart_button])
clock.tick(60)
# 게임 액션 함수들
def build_farm(game_state):
if game_state.player.gold >= 50:
game_state.player.gold -= 50
game_state.player.farms += 1
game_state.player.update_production()
game_state.message = "농장을 건설했습니다! 식량 생산량이 증가합니다."
try:
build_sound.play()
except:
pass
else:
game_state.message = "금화가 부족합니다!"
def build_mine(game_state):
if game_state.player.gold >= 50:
game_state.player.gold -= 50
game_state.player.mines += 1
game_state.player.update_production()
game_state.message = "광산을 건설했습니다! 금화 생산량이 증가합니다."
try:
build_sound.play()
except:
pass
else:
game_state.message = "금화가 부족합니다!"
def build_house(game_state):
if game_state.player.gold >= 50:
game_state.player.gold -= 50
game_state.player.houses += 1
game_state.player.update_production()
game_state.message = "주택을 건설했습니다! 인구 증가량이 높아집니다."
try:
build_sound.play()
except:
pass
else:
game_state.message = "금화가 부족합니다!"
def build_wall(game_state):
if game_state.player.gold >= 40:
game_state.player.gold -= 40
game_state.player.walls += 1
game_state.player.update_production()
game_state.message = "성벽을 강화했습니다! 방어력이 증가합니다."
try:
build_sound.play()
except:
pass
else:
game_state.message = "금화가 부족합니다!"
def train_army(game_state):
if game_state.player.gold >= 30 and game_state.player.population >= 10:
game_state.player.gold -= 30
game_state.player.population -= 10
game_state.player.army += 5
game_state.player.update_production()
game_state.message = "군대를 훈련했습니다! 공격력이 증가합니다."
try:
build_sound.play()
except:
pass
else:
if game_state.player.gold < 30:
game_state.message = "금화가 부족합니다!"
else:
game_state.message = "인구가 부족합니다!"
def attack(game_state):
if game_state.player.army > 0:
game_state.player_attack()
game_state.check_game_over()
else:
game_state.message = "공격할 군대가 없습니다!"
def next_turn(game_state):
game_state.next_turn()
def restart_game():
return GameState()
# 게임 실행
if __name__ == "__main__":
main()
게임 화면 📸
게임 화면은 다음과 같이 구성되어 있습니다:
- 상단: 게임 제목과 현재 턴 정보
- 중앙 상단: 플레이어와 AI 왕국의 정보 패널
- 중앙: 게임 메시지 영역
- 하단: 건물 건설, 군대 훈련, 공격, 턴 넘기기 등의 버튼
화면 구성요소 코드 설명 📝
이 게임은 크게 다음 클래스들로 구성됩니다:
- Kingdom 클래스: 왕국의 자원, 건물, 군대 등을 관리
- GameState 클래스: 게임의 전체 상태와 턴 진행을 관리
- Button 클래스: 사용자 인터페이스 버튼 생성 및 처리
Kingdom 클래스 코드 설명
이 클래스는 각 왕국의 상태를 관리합니다. 자원(금화, 식량, 인구), 건물(농장, 광산, 주택 등), 군사력(공격력, 방어력)의 정보를 저장하고 업데이트합니다.
GameState 클래스 코드 설명
이 클래스는 게임의 전체 상태를 관리합니다. 플레이어와 AI의 왕국 객체를 생성하고, 턴 진행, AI의 의사결정, 전투 처리, 게임 종료 조건 확인 등의 기능을 수행합니다.
게임 실행 방법 🚀
- 위의 코드를 strategy_game.py 파일로 저장합니다.
- (선택사항) 게임에 사용될 사운드 파일을 준비합니다:
- build.wav - 건물 건설 시 재생
- battle.wav - 전투 시 재생
- victory.wav - 승리 시 재생
- defeat.wav - 패배 시 재생
- button.wav - 버튼 클릭 시 재생
- 터미널에서 python strategy_game.py 명령으로 게임을 실행합니다.
사운드 파일이 없어도 게임은 정상적으로 작동합니다.
게임에서 사용된 프로그래밍 개념 📚
- 객체 지향 프로그래밍(OOP): 클래스와 객체를 사용하여 게임 요소를 모델링
- 이벤트 기반 프로그래밍: Pygame의 이벤트 시스템을 사용한 사용자 입력 처리
- 상태 관리: 게임 상태를 체계적으로 관리하여 복잡성 감소
- UI 컴포넌트: 버튼과 패널을 객체로 구현하여 사용자 인터페이스 구성
- 인공지능(AI): 간단한 의사결정 규칙에 기반한 AI 구현
알고리즘 설명 🧮
자원 생산 알고리즘
produce_resources 메서드에서는 다음과 같은 알고리즘으로 자원을 생산합니다:
- 금화 생산: 기본 생산량 + (광산 수 × 생산 계수)
- 식량 생산: 기본 생산량 + (농장 수 × 생산 계수)
- 인구 증가: 기본 증가량 + (주택 수 × 증가 계수)
- 단, 식량이 부족하면 인구가 감소
AI 의사결정 알고리즘
ai_make_decision 메서드에서는 다음과 같은 우선순위로 AI가 행동합니다:
- 건물 건설 (농장, 광산, 주택 순)
- 군대 훈련
- 방어 강화
- 플레이어 공격 (공격력이 플레이어의 방어력의 70% 이상일 때)
전투 알고리즘
전투는 player_attack과 ai_attack 메서드에서 처리되며, 다음과 같은 방식으로 계산됩니다:
- 공격력 > 방어력: 방어력과 군대 감소
- 방어력 감소량 = (공격력 - 방어력) ÷ 2
- 군대 감소량 = (공격력 - 방어력) ÷ 4
- 공격력 ≤ 방어력: 공격 실패, 공격자의 군대 감소
다음 포스팅 예고 🔮
다음 포스팅에서는 '파이썬으로 만드는 RPG 게임(Role-Playing Game)'에 대해 알아보겠습니다. 캐릭터 성장, 전투 시스템, 아이템 관리, 퀘스트 등 RPG의 핵심 요소들을 구현해 보겠습니다. 텍스트 기반에서 시작하여 점차 그래픽 요소를 추가하는 방식으로 진행할 예정입니다.
플레이어가 직접 캐릭터를 선택하고 능력치를 관리하며, 다양한 몬스터와 전투를 벌이고, 경험치를 쌓아 레벨업하는 과정을 코드로 구현해 보겠습니다. 또한 간단한 스토리라인과 대화 시스템도 추가하여 게임에 재미 요소를 더할 것입니다.
여러분의 의견과 질문은 언제나 환영합니다. 행복한 코딩 되세요! 🐍🎲🏰✨
'파이썬 기초문법 > 파이썬 게임 만들기' 카테고리의 다른 글
파이썬 게임 만들기 - 메모리 매칭 게임 🧠🃏 (0) | 2025.03.11 |
---|---|
파이썬 게임 만들기 - 블랙잭 🃏♠️ (0) | 2025.03.11 |
파이썬 게임 만들기 - 플랫포머 게임(Platformer Game) 🏃♂️🌟 (1) | 2025.03.09 |
파이썬 게임 만들기 - 간단한 던전 크롤러(Simple Dungeon Crawler) 🏰🗡️ (0) | 2025.03.09 |
파이썬 게임 만들기 - 스페이스 인베이더(Space Invaders) 🚀👾 (0) | 2025.03.09 |