개요: 파이썬을 이용한 Text RPG. 직업을 가진 고양이 캐릭터들을 골라 多 vs 多 턴제 게임을 구현함
프로젝트 정보
- 프로젝트: 냥이 키우기 프로젝트 (파이썬 객체 지향 프로그래밍 세션)
- 개발기간: 2023.03.29 - 2023.03.31 ( 3일 )
- 역할
- 메인 화면 구현
- 인트로 구현
- 배틀 시스템 구현
- 마을 구현
- 여관 구현
- 사용 언어: Python
- 사용 라이브러리:
- Python(Vanilla)
프로젝트 구성
★ 파일구조
각 기능을 통합하는 main.py를 포함해 총 6개 파일로 구성되어 있다.
Cat Game ─┬─ main.py (각 기능 통합 및 게임 실행)
├─ battlephase.py (전투 시스템)
├─ items.py (아이템 및 장비)
├─ object.py (캐릭터 및 몬스터)
├─ shop.py (상점)
└─ utility.py (입력 문자열 체크, 운영체제 별 터미널 클리어)
★ 화면구성 및 코드
1. 메인화면 (시스템 변경)
각 기능이 함수 단위로 구현되었기 때문에 게임을 진행하면서 플레이어의 선택에 따라 각 함수로 이동해야 한다
그래서 main.py의 status 변수에 플레이어가 선택한 행동의 상태를 기록하고 각 함수를 실행한 뒤 return으로 status 값을 바꾸는 식으로 게임을 구현했다
아래의 코드를 보자
# main.py
while status != 'quit':
# 어떤 행동 끝나면 마을
if status == 'town':
status = town(player_character_list, player_money)
# 여관
elif status == 'inn':
status, player_money = inn(player_character_list, player_money)
# 상점
elif status == 'buy_item':
status, player_money = buy_item(player_character_list, player_money)
# 일반 전투
elif status == 'prebattle':
status, player_money = prebattle(
player_character_list, player_money, character_skills)
# 보스 전투
elif status == 'prebossbattle':
status, player_money, check_boss_clear = prebossbattle(
player_character_list, player_money, character_skills, boss_clear)
# 보스 클리어 확인
if check_boss_clear == True:
boss_clear += 1
check_boss_clear = False
elif status == 'lose':
status, player_money = lose(player_money)
# 총 스테이터스 창
elif status == 'all_status':
status = all_status_s(player_character_list)
위와 같이 main.py에서는 while문이 게임이 끝날 때까지 계속해서 돌면서 status에 어떤 값이 들어가는지 체크해 각 기능이 담긴 함수를 실행하게끔 만들었다. 또한 상점이나 전투 같은 함수가 실행되고 나면 플레이어의 소지금에 변화가 생기므로 return에 이러한 값도 담기게끔 했다
2. 인트로 (캐릭터 선택)
게임을 시작했을 때 화면이다.
네마리의 캐릭터 중 세마리를 차례로 고를 수 있도록 했다.
한마리씩 고를 때 마다 첫번째 줄에 현재 고른 캐릭터의 이름이 나오도록 했으며, 만약 이미 선택한 캐릭터를 다시 고른다면 '이미 xxx에게 간택 받았습니다.'라는 메세지가 출력되도록 했다
아래의 코드를 보자
# main.py
# 캐릭터 선택(3 마리)
while len(player_character_list) < 4:
screen_clear()
print("간택 받고 싶은 고양이 세마리를 골라주세요!(현재:", end=" ")
print(*player_character_list, end=" ")
print(")")
select = input("1)냥검사 2)냥법사 3)냥궁수 4)냥힐러 : ")
if input_check(1, len(characters), select) == False:
continue
elif characters[select] in player_character_list:
print(f"이미 {characters[select]}에게 간택 받았습니다.")
time.sleep(1)
else:
player_character_list.append(characters[select])
# 3 마리가 다 채워졌다면 break
if len(player_character_list) == 3:
break
screen_clear()함수는 게임의 가독성을 위해 화면을 지우고 새로 출력하고자 할 때, windows와 mac의 화면 지우기 명령어가 'cls'와 'clear'로 다르므로 이를 구분해 주기 위해 utility.py에 함수를 작성했다
# utility.py
import platform
import os
def screen_clear():
os_check = platform.system()
if os_check == 'Windows':
os.system("cls")
else:
os.system("clear")
platform 모듈의 system()함수를 사용했다
어쨌든 main.py에서는 if~elif~else문으로 문자열 체크, 선택한 고양이 중복 선택 체크 후에 문제가 없으면 player_character_list에 캐릭터를 append했다
그리고 3마리가 다 채워지면 while문을 빠져 나가도록 해 캐릭터를 선택하도록 했다.
3. 마을
마을로 들어왔을 때 화면이다
어떤 행동을 하고 난 뒤에는 항상 마을로 돌아오도록 시스템을 구성했다
각 캐릭터의 스테이터스와 소지금, 그리고 인벤토리를 간단하게 표시하고 총 다섯개의 선택지로 이동할 수 있게끔 했다
'1) 길거리'는 일반전투, '2) 높은 탑'은 보스 전투다
마을의 코드를 한번 살펴보자
# shop.py
# 마을
def town(character_list, money):
# 마을에서 행동 리스트
town_action = {
'1': 'prebattle',
'2': 'prebossbattle',
'3': 'inn',
'4': 'buy_item',
'5': 'all_status'
}
while True:
screen_clear()
for i in character_list:
print(i.status())
print("---------------------------------------------------")
print(f"소지금: {money}$")
print("- 인벤토리 -")
print(f"{potion1.name} : {potion1.num}개")
print(f" {potion2.name} : {potion2.num}개")
print(f"{potion3.name} : {potion3.num}개")
print(f" {potion4.name} : {potion4.num}개")
if bool(pet_list) == True:
print('현재 pet : ', end=" ")
print(*pet_list)
action = input("어디로 갈까요? [ 1)길거리 2)높은 탑 3)여관 4)상점 5)총 스테이터스] : ")
if input_check(1, 5, action) == False:
continue
else:
return town_action[action]
town_action이라는 딕셔너리 안에는 각 행동을 함수로 만들어 정리해두었다
그리고 맨 아래의 input으로 1~5까지의 값을 받아 함수를 실행하도록 했다
input_check()함수는 입력받은 문자열이 validate한지 체크하는 함수이다
# utility.py
import re
def input_check(x, y, text):
pattern = "[{}-{}]".format(x, y)
if text.isdigit() is False:
print("정수를 입력해주세요.")
return False
elif bool(re.fullmatch(pattern, text)) is False:
print("잘못 입력했습니다. 다시 시도하세요.")
return False
else:
return True
input_check()함수는 위에서 보듯이 isdigit()함수로 입력받은 문자열이 정수값인지 체크하고 re모듈의 fullmatch()함수로 특정 값이면 True를 리턴하도록 했다
4. 일반 전투( 길거리 )
마을에서 1번을 입력했을 때 실행되는 일반전투 창이다
뱀, 쥐, 바퀴벌레, 까치 4 종류의 몬스터 중에 랜덤으로 1~3마리의 몬스터가 출현한다
현재 기절하지 않은 캐릭터만 전투에 참여할 수 있도록 코드를 짰고 마지막 줄에서 보듯이 고양이들과 몬스터들의 평균 속도를 비교해 더 빠른 진영이 선공할 수 있도록 했다.
본격적인 전투에 들어가기 전에 전투에 관련한 정보를 세팅하는 prebattle()함수부터 살펴보자
# battlephase.py
# 일반배틀 준비
def prebattle(character_list, money, character_skills):
battle_character = []
for i in character_list:
if i.HP != 0:
battle_character.append(i)
if len(battle_character) == 0:
print("싸울 수 있는 고양이가 없습니다. 마을로 돌아갑니다.")
time.sleep(2)
return 'town', money
# 일반 던전 몬스터 가중치
sum_ = 0
for i in character_list:
sum_ += i.level
avg_lv = sum_ // 3 + random.randint(1, 3)
# Monster List HP / MP / ATK / DEF / SPD
monster_list = [
Monster("쥐", avg_lv, 70, 0, 13, 15, 10),
Monster("까치", avg_lv, 100, 0, 18, 18, 7),
Monster("바퀴벌레", avg_lv, 110, 0, 15, 30, 12),
Monster("뱀", avg_lv, 80, 0, 20, 20, 10)
]
# 전투에 나올 몬스터
battle_monster = []
# 몬스터 뽑기
for i in range(random.randint(1, 3)):
battle_monster.append(monster_list.pop(
random.randint(0, len(monster_list)-1)))
# 전투 시작
return battle(battle_character, battle_monster, money, character_skills)
전투 전에 for문을 돌면서 캐릭터의 HP가 0이면 전투에 참가할 수 없도록 했다.
그리고 전부 HP가 0이라면 싸울 수 있는 고양이가 없다는 뜻이므로 중단하고 다시 마을로 돌아가도록 했다
그리고 현재 캐릭터의 레벨에 비례해서 몬스터의 레벨을 변화시키는 avg_lv 변수를 만들었고, 그중 랜덤으로 몬스터가 나오게끔 했다
전투에 들어가는 battle()함수는 너무 길어 생략하고 아래에 github 주소를 남겨놓도록 하겠다.
5. 보스 전투 ( 높은 탑 )
보스 전투 역시 본 전투 전에 prebossbattle()함수를 통해 전투에 필요한 데이터를 산출한다
보스는 슈뢰딩거, 잼민이, 제리 세 보스가 구현되어 있으며, 한 보스를 처치하고 나면 달성도를 기억해 두었다가 다시 탑에 가면 다음 보스가 나오는 식이다
각 보스의 스텟은 고정이며 일반전투에서 레벨과 능력치를 쌓은 뒤 보스에 도전할 수 있도록 구성했다
prebossbattle()함수를 보자
# battlephase.py
# 보스배틀 준비
def prebossbattle(character_list, money, character_skills, boss_clear):
battle_character = []
for i in character_list:
if i.HP != 0:
battle_character.append(i)
# 고양이 셋 다 기절해 있다면
if len(battle_character) == 0:
print("싸울 수 있는 고양이가 없습니다. 마을로 돌아갑니다.")
time.sleep(2)
return 'town', money, boss_clear
# 고양이 셋 중 하나라도 기절해 있으면 의사 물어보기
if len(battle_character) != len(character_list):
question = input(
"고양이들 중에 싸울 수 없는 고양이가 있는 것 같아요. 그래도 싸우시겠습니까? [ 1) 네 2) 마을로 돌아갈래요 ]")
if question == '1':
print("좋은 자신감이에요")
time.sleep(2)
else:
return 'town', money, boss_clear
# 나올 보스
if boss_clear == 0:
battle_monster = [bosses[0]]
elif boss_clear == 1:
battle_monster = [bosses[1], bosses[2]]
elif boss_clear == 2:
battle_monster = [bosses[3]]
# 전투 시작
return battle(battle_character, battle_monster, money, character_skills)
일반전투 준비와 크게 다른 부분은 없지만 보스 전투이기 때문에 고양이 중 하나라도 기절해 있을 경우 진짜 도전할 건지 의사를 묻는 코드를 추가로 짰고, 어떤 보스를 출현시킬 건지에 관한 boss_clear변수를 매개변수로 받아 일반전투와 같은 battle()함수를 실행시키게끔 했다
6. 여관
여관은 기절했거나 HP, MP가 부족한 고양이들을 부활시켜주는 곳으로 만들었다
# shop.py
def inn(character_list, money):
while True:
screen_clear()
print("어서오세요~고양이 카페입니다. 쉬고 가실건가요?")
time.sleep(1)
print("요금은 100$입니다")
time.sleep(1)
print(f"소지금: {money}$")
action = input("1)쉬기 2)돌아가기 : ")
if input_check(1, 2, action) == False:
continue
elif money < 100:
print("돈도 없는 주제에. 저리 꺼져")
time.sleep(2)
print("마을로 돌아갑니다")
time.sleep(2)
return 'town', money
else:
if action == '1':
return recovery_in_inn(character_list, money)
elif action == '2':
return 'town', money
def recovery_in_inn(character_list, money):
money -= 100
for i in character_list:
i.HP = i.max_HP
i.MP = i.max_MP
i.faint = False
print("고양이 카페에서 푹 쉬었더니 고양이들의 체력이 회복되었습니다")
time.sleep(3)
print("마을로 돌아갑니다")
time.sleep(3)
return 'town', money
inn()함수에서 소지금이 충분한지 체크하고 recovery_in_inn()함수에서 회복 기능을 할 수 있도록 만들었다.
지금 생각해보니 굳이 나눌 필요가 없었다.
프로젝트 결과물
https://github.com/barryjung/CatGame
프로젝트 완료 후의 감상
더 효율적으로 코드를 짜지 못한 게 아쉬움으로 남는다.
처음 코드를 짤 때는 시간도 부족하고 프로젝트 방향 자체도 모호해 의식의 흐름대로 코드를 짰는데 프로젝트를 완성하고 다시 되돌아보니 비효율적인 부분들이 곳곳에서 보인다.
예를 들어 일반전투준비와 보스전투준비에서 코드가 겹치는 부분이라던가, 각 인스턴스들의 위치라던가.
항상 느끼는 거지만 프로젝트 계획 단계가 가장 중요하다.
시작부터 방향성이 확실했다면 팀원들도 더 효율적인 코드가 나왔을 것 같기도 하다
팀장님이 예비군으로 인한 부재로 이번 프로젝트도 피치 못하게 팀장대리처럼 프로젝트를 계획하고 진행했다
팀을 이끄는 건 재밌지만 생각해야 할 것도 너무 많다
'프로그래밍 > 생각나는대로 프로젝트' 카테고리의 다른 글
[프로젝트] 06. DRF 경매 서비스 - 20세기 박물관 (0) | 2023.05.16 |
---|---|
[프로젝트] 05. Django SNS CaMu (0) | 2023.04.16 |
[프로젝트] 04. Django 무신사 재고 관리 시스템 (0) | 2023.04.10 |
[프로젝트] 02. 팀 소개 프로젝트 Brain-8 (0) | 2023.03.19 |
[프로젝트] 01. 음악 추천 서비스 SMM (2) | 2023.03.11 |