선발대

[스파르타] 파이썬 프로젝트3: 턴제 RPG 게임 만들기 (심화) 본문

스파르타코딩클럽/활동 내용

[스파르타] 파이썬 프로젝트3: 턴제 RPG 게임 만들기 (심화)

신선한 스타트 2021. 12. 22. 09:44
게임설명

 

(1) 턴제 RPG 게임 만들기

 

[간단설명]

 

  • 공통속성: 이름 / HP / 공격력 
  • 플레이어 행동 종류: 공격 / 마법
  • 몬스터 행동 종류: 공격 /  회복 / 대기
  • 몬스터 종류: 미니고블린 / 고블린 / 슈퍼고블린

 

[상세설명]

 

1. '플레이어'와 '몬스터'라는 클래스를 만들고, 각각 할 수 있는 행동을 메소드 형태로 정의함.

2. 함수 '플레이어 턴': 플레이어가 입력하는 값에 따라 플레이어 클래스의 메소드(공격, 마법)가 실행됨,

3. 함수 '몬스터 턴': 랜덤으로 몬스터 클래스의 메소드가 실행됨. 

4. 두 함수를 While문 아래 배치해서, 모든 몬스터가 죽거나, 플레이어가 죽기 전까지 번갈아가며 진행.

 

더보기

[상세설명 / 1. 클래스 생성 파트] : '플레이어', '몬스터'는 모두 Object라는 공통된 클래스를 상속받아 만들어진 클래스임.

 

1. Object 클래스: 속성 - 이름, HP, 공격력 / 메소드 - 공격

 

  • '공격' 함수: 인자 - '공격 대상', 실행할 경우 공격 대상의 HP를 자신의 공격력만큼 감소시킴.
  • 공격할 때는 OO가 OO를 공격했고, 그에 따라 공격 대상의 남은 체력을 print문으로 출력함.

 

2. Player 클래스: Object 클래스를 상속받으며, '마법'이라는 메소드를 가지고 있음.

 

  • '마법' 함수: 인자 - '공격 대상', 실행할 경우 공격 대상의 HP를 50만큼 감소시킴.
  • 공격할 때는 OO가 OO를 공격했고, 그에 따라 공격받은 대상의 남은 체력을 print문으로 출력함.

 

3. Monster 클래스: Object 클래스를 상속받으며, '대기', '치료' 메소드를 가지고 있음.

 

  • '대기' 함수: 인자 - self, 그냥 'OO가 대기했습니다.' 만 출력됨.
  • '치료' 함수: 인자 - self, 자기 자신의 HP를 10 증가시킴. 'OO가 자신의 체력을 10만큼 회복했다.' 출력함.

 

[상세설명 / 2. 함수 생성 파트]

 

1. 전사: Player 클래스의 인스턴스. 체력: 100, 공격력: 10

2. 고블린들: Monster 클래스의 인스턴스.

 

  • 미니고블린: 체력: 10, 공격력: 10
  • 고블린: 체력: 30, 공격력: 30
  • 슈퍼고블린: 체력: 50, 공격력: 50

 

3. 정보 표시 함수: 턴 시작 전에 전사, 고블린 체력을 표시함. (이미 체력 0인 고블린은 표시 X)

4. 플레이어 턴 함수: 전사 행동은 input 함수에 따라 이루어짐. (공격/마법 + 그 대상)

 

  • 오늘 실습은 작성할 코드 자체가 많으므로, input 값에 따른 예외처리는 아예 하지 않는 것을 권장함.

 

5. 몬스터 사망 여부 체크 함수: 플레이어 턴이 끝났을 때, 체력이 0 이하인 몬스터는 앞으로 정보 표시 X.

 

  • 사망 여부를 체크하고 모든 몬스터가 다 사망했다면, 함수 밖에서 '승리' 출력 후 While문 탈출. (게임종료)

 

6. 몬스터 턴 함수: 몬스터 행동(공격, 대기, 치료)는 랜덤하게 이루어짐.

 

  • 몬스터 턴에서는 몬스터 3마리 전부 각각 하나의 행동을 실행해야 함.
  • (몬스터 1마리만 행동을 실행하고 턴이 넘어가는 것이 아님)

 

7. 플레이어 사망 여부 체크 함수:

 

  • 몬스터 턴이 끝났을 때, 플레이어 체력이 0 이하면 함수 밖에서 '패배' 출력. While문 탈출.

 

[상세설명 - 3. 게임 실행 파트] : 앞서 만든 함수들을 순서대로 While문 안에 배치함.

 

모범답안

 

(1) 턴제 RPG 게임 만들기

 

#@title
class Object:
    def __init__(self, name, hp, power):
        self.name = name
        self.hp = hp
        self.power = power
    def attack(self, target):
        print(f"{self.name}이(가) {target.name}을(를) 공격!")
        print(f"{target.name}에게 {self.power}만큼의 데미지!")
        target.hp = target.hp - self.power
        # 심화 개념: 사실은 여기서 target.hp 를 할당한다고 해서 실제로 해당 target 의 hp 값이 바뀌면 안되는 것이 원칙.
        # 그러므로 함수 마지막에서 return target.hp 를 해서 해당 target 의 값에 재할당해주는 것이 맞음.
        # 그러나 파이썬의 경우 자동으로 target 이 가진 속성의 주소를 변경해주기에, 굳이 재할당해주지 않아도, 해당 target 의 hp 값이 바뀜. 
        # (위 개념은 굳이 이해하지 않아도 괜찮습니다!) 
        if (target.hp <= 0):
            print(f"{target.name}을(를) 죽였습니다!")
        else:
            print(f"{target.name}의 HP가 {target.hp}이 되었습니다")

class Player(Object):
    def magic(self, target):
        print(f"{self.name}이(가) {target.name}에게 마법을 사용!")
        print(f"{target.name}에게 50만큼의 데미지!")
        target.hp = target.hp - 50
        if (target.hp <= 0):
            print(f"{target.name}을(를) 죽였습니다!")
        else:
            print(f"{target.name}의 HP가 {target.hp}이 되었습니다")  

class Monster(Object):
    def cure(self):
        self.hp = self.hp + 10
        print(f"{self.name}이 체력을 10 회복했습니다!")
    def stay(self):
        print(f"{self.name}이 대기했습니다!")

 

#@title
from random import choice
from time import sleep # sleep 은 게임을 진행할 때 텍스트를 보기 편하게 하기 위함.

def createobjects():
    Warrior = Player('전사', 100, 10)
    # 이름으로 해당 몬스터 인스턴스를 찾을 수 있도록, 딕셔너리 형태로 Monsters 를 묶어놓음
    Monsters = {}
    Monsters['미니고블린'] = Monster('미니고블린', 10, 10)
    Monsters['고블린'] = Monster('고블린', 30, 30)
    Monsters['슈퍼고블린'] = Monster('슈퍼고블린', 50, 50)
    return Warrior, Monsters

def showinfo(Player, Monsters):
    print("\n--------------턴 시작---------------")
    print(f"{Player.name}의 체력 : {Player.hp}")
    for key, value in Monsters.items():
        print(f"{value.name}의 체력 : {value.hp}")

def playerturn(Player, Monsters):
    print("\n--------------플레이어 턴--------------")
    # 예외처리는 각자 해보시는 것을 추천드리겠습니다!
    # 예외처리는 try except 를 쓰는 것이 정확한 방법이긴 합니다!
    command = input('공격? 마법? : ')
    target = input('누구를 공격? : ')
    if command == '공격':
        Player.attack(Monsters[target])
    elif command == '마법':
        Player.magic(Monsters[target])
    return Monsters

def check_mdead(Monsters):
    # 이번 턴에서 죽은 몬스터가 있는지 확인
    dead_monsters = []
    for key, value in Monsters.items():
        if value.hp <= 0:
            dead_monsters.append(key)
    # 죽은 몬스터는 몬스터 명단에서 삭제
    for i in dead_monsters:
        del Monsters[i]
    # 남은 몬스터가 없다면 승리 출력, 있다면 몬스터 그대로 리턴해주기
    if len(Monsters) <= 0:
        return Monsters, True
    else:
        return Monsters, False

def monsterturn(Player, Monsters):
    print("\n------------몬스터 턴-----------")
    sleep(3)
    for key, value in Monsters.items():
        commands = ['cure', 'attack', 'stay']
        command = choice(commands)
        if command == 'cure':
            value.cure()
        elif command == 'attack':
            value.attack(Player)
        elif command == 'stay':
            value.stay()
    return Player

def check_pdead(Player):
    if Player.hp <= 0:
        return True
    else:
        return False

 

#@title
Warrior, Monsters = createobjects()

while True:
    showinfo(Warrior, Monsters)
    Monsters = playerturn(Warrior, Monsters)
    sleep(1)
    Monsters, ismdead = check_mdead(Monsters)
    if ismdead:
        print('\n승리!!!')
        break
    Warrior = monsterturn(Warrior, Monsters)
    ispdead = check_pdead(Warrior)
    if ispdead:
        print("\n패배!!!")
        break
    sleep(1)

 

후기
  • 문제에 주어진 클래스는 만들었는데 메소드 생성이 어려웠다.
  • 우선 플레이어와 몬스터는 전부 Object를 상속하므로, Object에 기본정보를 넣는다.
  • [공격함수]에는 매번 타겟의 체력이 0일 때의 메시지를 남긴다.
  • [플레이어클래스]의 [마법함수]에도 체력이 0일 때의 메시지를 남긴다.
  • choice는 주어진 범위에서 랜덤으로 선택하는 모듈. sleep은 잠시 대기하는 모듈.
  • createobjects()에서 몬스터가 여러 마리이므로 사전형으로 변수를 저장한다.
  • showinfo(P, M)는 턴 시작할 때 체력을 받아옴. 사전형 몬스터for문으로 표현.
  • playturn(P, M)에서 Player.attack(Monsters[target])은 사전형 몬스터의 value 값으로.
  • check_mdead(M)는 del Monster[i]로 아예 삭제. Monsters 길이가 0보다 작으면 True.
  • 위에서 작성한 함수들을 순서대로 while문에 넣으면 끝!
  • 아직 사전형 형태에 익숙하지 않다. 전부 하나씩 변수에 넣을 생각이었는데 더 간단한 방법이 있었다.
Comments