-
[파이썬/Python] 텍스트 RPG(2): 튜토리얼(= 첫 전투)깜빡의 취미/파이썬을 합니다. 2021. 9. 26. 11:30728x90
# 출력 화면은 모두 Pycharm을 이용했습니다.
반응형이 글이 완료될 즈음 여러분은 가장 기초적인 형태의 턴제 RPG를 만들어볼 수 있을 것이다. 물론 가장 기초적인 형태라 실제 그래픽 구현 등은 거의 되어 있지 않다. 그래도 파이썬으로 무언가 해보고 싶은데 무엇부터 해야 할지 모르겠어 막막한 초심자라면, 한 번쯤 도전해볼 만하다.
지난 시간에는 캐릭터 기본 설정, 즉 이름과 스텟을 설정하는 코드를 소개해 보았다.
이번 시간에는 설정된 캐릭터를 이용해 튜토리얼 격의 전투를 진행하는 법을 소개하고자 한다.
우선 전투 전 개연성을 위해 대본을 집어 넣으면 좋다. 앞서 설명했지만, input 함수를 이용하면 한 줄씩 문장을 출력할 수 있다. 아래는 실제 게임에 넣은 대화 예시다.
input(f"\n{나}: 뭐야? 왜 이렇게 조용해?") input(f"\n거울 속의 {나}: 뭐긴, 시작한 거지. 이번엔 이런 몸인가? 슬슬 나가자 좀.") input(f"\n{나}: 거울 속에서 말이? 아니 그럼 훈련병 괴담이 전부 사실이었단 말이야?") input(f"\n거울 속의 {나}: 뭐... 그건 잘 모르겠고. 일단 좀 맞자.") input(f"\n{나}: ???")
자세히 설명하면, 이스케이프 코드 \n(줄바꿈 기호로 약속되어 있음)을 이용해 문장이 다닥다닥 붙어 있지 않도록 했다. f 포맷팅 기법을 이용하여 앞서 설정해 두었던 캐릭터명을 출력했다. 아래는 실제 출력 화면이다.
f 포맷팅 기법은 문장 밖의 유동적인 내용을 문장에 적용할 때 사용합니다. 문장 바로 앞에 'f'를 입력하고, 적용할 문자를 중괄호({})로 덮습니다. 이때 만일 print(f"{a}는 최고다")라는 문장을 입력하시려면 윗 줄에 a = "나"처럼 a값이 지정되어 있거나 a = input()처럼 a값을 설정할 수 있도록 미리 작업을 해주셔야 합니다.
이제 본격적으로 전투 사전 작업을 시작해 보자. 적의 스텟도 앞서 설명한 캐릭터 스텟처럼 설정해 준다. 친절하게 적 체력이 어느 정도인지도 적어 주자(이때 띄어쓰기할 경우 앞서 설정한 캐릭터 스텟과 혼동이 일어나 오류가 발생하니 참고).
적체력 = 50 적방어력 = 0 적공격력 = 7 적기 = 0 적정신력 = 0 print(f"거울 속 {나}의 체력: {적 체력}")
이해를 돕기 위해 풀네임으로 적었지만, 캐릭터 스텟 정보를 'hp, a(attack), d(defense)'처럼 소문자로 적고 적 스텟 정보를 'HP, A(attack), D(defense)'와 같이 대문자로 적는 방법도 있다. 두 방법 모두 장단이 있으니, 기호에 따라 선택하면 된다.
추가로 아래의 문장이 꼭 필요하다.
체력1 = 체력
전투가 시작되면 캐릭터의 체력이 이리저리 요동을 치게 된다. 이 때 별도의 작업을 하지 않으면 전투가 끝나고 체력을 리셋할 수 없다. 따라서 '체력1'이라는 저장소를 만들어 전투 직전의 체력을 저장한다.
{나}의 행동 패턴, 적이 패배할 경우
사전 작업도 끝났으니, 이제 주인공(캐릭터)의 행동 패턴을 먼저 정해 보자. 필자는 공격/방어/스킬/도망의 네 가지가 적절하다고 생각했다. 빠르게 전체 코드를 보여드리고, 자세히 설명을 이어가겠다.
while 1: # 주인공의 행동 패턴 print(f"\n{나}의 턴!!!!") time.sleep(1) N = input("q)공격 w)방어 e)스킬 r)도망간다\n") if N == "q": print(f"{나}은/는 잽을 두 번 날렸다.") 공격력1 = 공격력 + random.choice([1, 2, 3, 5, 8]) if 공격력1 - 적방어력 <= 0: print("공격은 완전히 복부로 흡수돼 방어됐다...") 적방어력 -= 공격력1 else: 적체력 -= (공격력1 - 적방어력) print(f"상대방의 남은 체력 = {적체력}") 적방어력 = 0 elif N == "w": 방어력 += 5 + 정신력 print(f'{나}은 덜 아프게 맞을 자세를 취했다 방어도가 {방어력}가 되었다') elif N == "e": 기 += 1 print("기~") if 기 == 2: print(f"{나}의 어퍼컷!") 적체력 -= 정신력 + random.choice([10, 15, 20, 25, 30]) print(f"상대방의 남은 체력 = {적체력}") 기 = 0 elif N == "r": escape = random.random() if escape < 운/10: print(f"{나}는 군인의 본분을 져버리고 도망갔다... \n무언가 업보가 쌓이는 듯한 느낌이 든다...") 도주 += 1 break else: print("도망에 실패했다!!!") if 적체력 <= 0: print(f"거울 속의 {나}: 쳇, 이번에도 글렀나.") break
while 1:
턴제 전투이므로 한 턴만에 끝날 리 없고, 따라서 양쪽 체력이 바닥나기 전까지는 while문을 통해 반복해야 한다.
print(f"\n{나}의 턴!!!!")
time.sleep(1)
N = input("q)공격 w)방어 e)스킬 r)도망간다\n")이스케이프 코드 \n, time.sleep(1)은 안 넣어도 좋다. time.sleep()은 import time을 통해 time 라이브러리를 불러와야 실행할 수 있으며, 괄호 안 숫자는 지연 시간을 의미한다. 쉽게 말해 위의 코드에서 print문이 출력되고 1초 있다가 input문이 출력된다.
여하튼 time 함수를 언젠가 쓸 수 있으니, 식 맨 위에 import time도 추가해 두자.
int 함수를 이용하지 않은 이유는, 엔터 키 등을 잘못 입력했을 때 오류가 발생할 수 있기 때문이다.
if N == "q":
print(f"{나}은/는 잽을 두 번 날렸다.")
공격력1 = 공격력 + random.choice([1, 2, 3, 5, 8])
캐릭터가 '공격'을 결정했을 경우 공격력에 임의의 수 5개 중 하나가 더해진다는 의미이다. 크리티컬 개념을 위해 조금 번거롭더라도 공격력1을 따로 설정했다. random.choice 함수는 괄호 안 선택지 중 하나를 무작위로 선택한다.
if 공격력1 - 적방어력 <= 0:
print("공격은 완전히 복부로 흡수돼 방어됐다...")
적방어력 -= 공격력1적의 방어력이 캐릭터의 공격력1보다 강할 수 있다. 이 경우 방어력에서 공격력1을 차감하는 것으로 체력 감소를 대신할 수 있다.
else:
적체력 -= (공격력1 - 적방어력)
print(f"상대방의 남은 체력 = {적체력}")
적방어력 = 0공격력1이 적의 방어력보다 강할 경우, 적의 체력을 깎고 적의 방어력을 0으로 고정한다. 공격력1의 피해를 줄이는데 방어력을 모두 사용했다는 설정이다.
이때 적체력 -= (공격력1 - 적방어력)은 적체력 = 적체력 - (공격력1 - 적방어력)과 같은 식이다. 비슷한 식으로 +=이 있다.
elif N == "w":
방어력 += 5 + 정신력
print(f'{나}은 덜 아프게 맞을 자세를 취했다 방어도가 {방어력}가 되었다')캐릭터가 '방어'를 결정했을 경우 방어력을 높여 준다. 앞서 설정한 정신력 스텟이 이때 영향을 미친다.
elif N == "e":
캐릭터가 '스킬'을 선택했을 경우, '기'를 올려 준다. 스킬을 난사하지 않기 위한 조치다.
기 += 1
print("기~")기가 없는 상태에서는 준비 운동을 시전한다.
if 기 == 2:
print(f"{나}의 어퍼컷!")
적체력 -= 정신력 + random.choice([10, 15, 20, 25, 30])
print(f"상대방의 남은 체력 = {적체력}")
기 = 0만일 기가 모여 2개가 되었다면, 스킬을 사용할 수 있다. 스킬은 준비 운동이 있는 만큼 방어력을 무시한다는 설정을 추가했다.
여기서 만일 기 ==2 부분을 수정하면 스킬 발동 조건을 좀 더 세부적으로 설정할 수 있다. 기를 많이 모을수록 강력한 스킬을 사용할 수 있게끔 하려면 input('스킬을 사용하시겠습니까? a=yes, b=no')같은 선택지를 추가할 수 있다. 이후 if문을 통해 스킬을 사용하거나 기를 아끼는 등의 세부 지침을 정해줄 수 있을 것이다.
elif N == "r":
escape = random.random()캐릭터가 '도망'을 선택했을 경우 random.random() 함수가 작동한다. 마찬가지로 random 라이브러리 속 함수로, 0부터 1 사이 무작위의 수를 출력한다.
if escape < 운/10:
print(f"{나}는 군인의 본분을 져버리고 도망갔다... \n무언가 업보가 쌓이는 듯한 느낌이 든다...")
도주 += 1
breakelse:
print("도망에 실패했다!!!")운 스텟이 여기서 영향을 미친다. 운 스텟이 높을수록 무작위의 수가 운 스텟보다 낮을 확률도 높아진다. 도망에 성공하면 도주 스텟을 하나 추가하고, break 함수를 이용해 while문을 종료한다. 전투가 끝날 방법인 셈이다. 실패하면... 전투는 계속된다.
if 적체력 <= 0:
print(f"거울 속의 {나}: 쳇, 이번에도 글렀나.")
break캐릭터가 '공격'이나 '스킬'을 적중했을 경우, 적의 체력은 0 이하가 될 수 있다. 이 경우 적의 패배 선언과 함께 while문을 종료한다.
적의 행동 패턴, {나}가 패배할 경우
적의 행동 패턴 또한 위에 제시한 while문 속에 있다. 스텟 설정을 무척 유사하게 해 두었기 때문에, 주인공의 행동 패턴을 표현하는 식과 그리 다르지 않다. 백문이 불여일견이라고, 바로 코드를 보자.
#상대의 행동 패턴 N = random.randrange(1, 4) print(f"\n거울 속 {나}의 턴!!!") time.sleep(1) if N == 1: 적공격력1 = 적공격력 + random.choice([1, 2, 3, 5, 8]) if 적공격력1 - 방어력 <= 0: print("공격은 완전히 복부로 흡수돼 방어됐따 ^^7") 방어력 -= 적공격력1 else: print("상대는 잽을 두 번 날렸다.") 체력 -= (적공격력1 - 방어력) print(f"{나}의 남은 체력 = {체력}") 방어력 = 0 elif N == 2: 적방어력 += 5 + 적정신력 print(f'상대는 덜 아프게 맞을 자세를 취했다 방어도가 {적방어력}가 되었다') elif N == 3: 적기 += 1 print("기~") if 적기 == 2: print("상대의 어퍼컷!") 체력 -= 적정신력 + random.choice([10, 15, 20, 25, 30]) print(f"{나}의 남은 체력 = {체력}") 적기 = 0 if 체력 <= 0: print("눈앞이 깜깜해졌다") print("\ntip: 이걸 지다니... 지나가던 개(발자)가 웃겠다.") Count = input("다시 하시겠습니까? q: 다시 한다. w: 다시 한다. e: 다시 한다.\n... ") if Count != "이걸맞추면당신은천재": t = 4 while t >= 1: t = t - 1 time.sleep(1) print(f"로딩까지 {t}초") 체력 = 체력1 적체력 = 50 적방어력 = 0 방어력 = 0 continue
워낙 비슷한 지점이 많아, 차이점만 설명하겠다.
N = random.randrange(1, 4)
적을 플레이어가 조종하는 것은 말이 안 되기 때문에(이 게임은 2P가 아니다), 앞서 스텟 설정에도 활용했던 random.randrange함수를 이용한다. 주인공과 동일한 공격/방어/스킬/도망 패턴 중 하나를 무작위로 선택하게 된다.
time.sleep(1)
if 체력 <= 0:
print("눈앞이 깜깜해졌다")
print("\ntip: 이걸 지다니... 지나가던 개(발자)가 웃겠다.")적의 '공격'과 '스킬' 적중으로 주인공의 체력이 0 이하가 될 수 있다. 이때에는 패배 선언과 함께 팁을 제공하는 것을 추천한다.
Count = input("다시 하시겠습니까? q: 다시 한다. w: 다시 한다. e: 다시 한다.\n... ")
if Count != "이걸맞추면당신은천재":튜토리얼인데 다시 할 기회를 주지 않으면 너무 아깝다. 잘못 눌러도 다시 시작할 수 있게끔, Count가 특정 암호가 아닐 경우 전부 재시작이 이루어질 수 있도록 설정해 두었다.
t = 4
while t >= 1:
t = t - 1
time.sleep(1)
print(f"로딩까지 {t}초")간단한 타이머다. 갑작스럽게 재시작되면 이상할까봐 집어넣었다. (4초 남음) -> 1초 쉬고 -> '로딩까지 3초' 와 같은 루프가 반복된다. 로딩이 다 되면 정보 초기화 및 재시작이 진행된다는 설정이다.
체력 = 체력1
적체력 = 50
적방어력 = 0
방어력 = 0
continue재시작을 위해서는 각자의 체력 및 방어력을 초기화해야 한다. 선택을 통해 변동이 일어나는 모든 요소를 초기화해주는 것이라고 이해하면 된다. continue 함수를 통해 while문의 처음으로 자연스레 돌아간다.
차근히 따라왔다면 아래의 식을 완성할 수 있다.
input(f"\n{나}: 뭐야? 왜 이렇게 조용해?") input(f"\n거울 속의 {나}: 뭐긴, 시작한 거지. 이번엔 이런 몸인가? 슬슬 나가자 좀.") input(f"\n{나}: 거울 속에서 말이? 아니 그럼 훈련병 괴담이 전부 사실이었단 말이야?") input(f"\n거울 속의 {나}: 뭐... 그건 잘 모르겠고. 일단 좀 맞자.") input(f"\n{나}: ???") 적체력 = 50 적방어력 = 0 적공격력 = 7 적기 = 0 적정신력 = 0 print(f"거울 속 {나}의 체력: {적체력}") 체력1 = 체력 while 1: #주인공의 행동 패턴 print(f"\n{나}의 턴!!!!") time.sleep(1) N = input("q)공격 w)방어 e)스킬 r)도망간다\n") if N == "q": print(f"{나}은/는 잽을 두 번 날렸다.") 공격력1 = 공격력 + random.choice([1, 2, 3, 5, 8]) if 공격력1 - 적방어력 <= 0: print("공격은 완전히 복부로 흡수돼 방어됐다...") 적방어력 -= 공격력1 else: 적체력 -= (공격력1 - 적방어력) print(f"상대방의 남은 체력 = {적체력}") 적방어력 = 0 elif N == "w": 방어력 += 5 + 정신력 print(f'{나}은 덜 아프게 맞을 자세를 취했다 방어도가 {방어력}가 되었다') elif N == "e": 기 += 1 print("기~") if 기 == 2: print(f"{나}의 어퍼컷!") 적체력 -= 정신력 + random.choice([10, 15, 20, 25, 30]) print(f"상대방의 남은 체력 = {적체력}") 기 = 0 elif N == "r": escape = random.random() if escape < 운/10: print(f"{나}는 군인의 본분을 져버리고 도망갔다... \n무언가 업보가 쌓이는 듯한 느낌이 든다...") 도주 += 1 break else: print("도망에 실패했다!!!") if 적체력 <= 0: print(f"거울 속의 {나}: 쳇, 이번에도 글렀나.") break #적의 행동 패턴 N = random.randrange(1, 4) print("\n상대의 턴!!!") time.sleep(1) if N == 1: 적공격력1 = 적공격력 + random.choice([1, 2, 3, 5, 8]) if 적공격력1 - 방어력 <= 0: print("공격은 완전히 복부로 흡수돼 방어됐따 ^^7") 방어력 -= 적공격력1 else: print("상대는 잽을 두 번 날렸다.") 체력 -= (적공격력1 - 방어력) print(f"{나}의 남은 체력 = {체력}") 방어력 = 0 elif N == 2: 적방어력 += 5 + 적정신력 print(f'상대는 덜 아프게 맞을 자세를 취했다 방어도가 {적방어력}가 되었다') elif N == 3: 적기 += 1 print("기~") if 적기 == 2: print("상대의 어퍼컷!") 체력 -= 적정신력 + random.choice([10, 15, 20, 25, 30]) print(f"{나}의 남은 체력 = {체력}") 적기 = 0 if 체력 <= 0: print("눈앞이 깜깜해졌다") print("\ntip: 이걸 지다니... 지나가던 개(발자)가 웃겠다.") Count = input("다시 하시겠습니까? q: 다시 한다. w: 다시 한다. e: 다시 한다.\n... ") if Count != "이걸맞추면당신은천재": t = 4 while t >= 1: t = t - 1 time.sleep(1) print(f"로딩까지 {t}초") 체력 = 체력1 적체력 = 50 적방어력 = 0 방어력 = 0 continue
첫 번째 시간에 소개했던 부분까지 잘 더해 주면 아래와 같이 된다.
import random import time print("당신의 이름을 선택하세요.") 나 = input() # 전투 시 실제로 적용되는 요소들 기초체력 = 75 방어력 = 0 공격력 = 0 # 전투력: 공격력, 체력 상승 / 정신력: 방어력, 스킬 강해짐 / 운: 도주 확률 증가 전투력 = 0 정신력 = 0 운 = 0 # 기: 스킬을 위해 필요 / 도주: 보스를 위한 숨겨진 요소 기 = 0 도주 = 0 while 1: 전투력 = random.randrange(5, 11) 체력 = 기초체력 + 전투력*1 공격력 = 전투력 정신력 = random.randrange(1, 6) 운 = random.randrange(1, 6) print(f"전투력 = {전투력}, 국방력= {정신력}, 열외력={운}") print("다시 하겠습니까: a = 네, b = 아니오") A = input() if A == "a": continue if A == "b": print("\n정말 결정하셨습까?: a = 네, b = 아니오") B = input() if B == "b": continue elif B == "a": print("\n결정되었습니다.") print(f""" [스테이터스] 이름 = {나} 직업 = 훈련병 체력= {체력} 전투력 = {전투력} 국방력 = {정신력} 열외력 = {운}""") break input(f"\n{나}: 뭐야? 왜 이렇게 조용해?") input(f"\n거울 속의 {나}: 뭐긴, 시작한 거지. 이번엔 이런 몸인가? 슬슬 나가자 좀.") input(f"\n{나}: 거울 속에서 말이? 아니 그럼 훈련병 괴담이 전부 사실이었단 말이야?") input(f"\n거울 속의 {나}: 뭐... 그건 잘 모르겠고. 일단 좀 맞자.") input(f"\n{나}: ???") 적체력 = 50 적방어력 = 0 적공격력 = 7 적기 = 0 적정신력 = 0 print(f"거울 속 {나}의 체력: {적체력}") 체력1 = 체력 while 1: #주인공의 행동 패턴 print(f"\n{나}의 턴!!!!") time.sleep(1) N = input("q)공격 w)방어 e)스킬 r)도망간다\n") if N == "q": print(f"{나}은/는 잽을 두 번 날렸다.") 공격력1 = 공격력 + random.choice([1, 2, 3, 5, 8]) if 공격력1 - 적방어력 <= 0: print("공격은 완전히 복부로 흡수돼 방어됐다...") 적방어력 -= 공격력1 else: 적체력 -= (공격력1 - 적방어력) print(f"상대방의 남은 체력 = {적체력}") 적방어력 = 0 elif N == "w": 방어력 += 5 + 정신력 print(f'{나}은 덜 아프게 맞을 자세를 취했다 방어도가 {방어력}가 되었다') elif N == "e": 기 += 1 print("기~") if 기 == 2: print(f"{나}의 어퍼컷!") 적체력 -= 정신력 + random.choice([10, 15, 20, 25, 30]) print(f"상대방의 남은 체력 = {적체력}") 기 = 0 elif N == "r": escape = random.random() if escape < 운/10: print(f"{나}는 군인의 본분을 져버리고 도망갔다... \n무언가 업보가 쌓이는 듯한 느낌이 든다...") 도주 += 1 break else: print("도망에 실패했다!!!") if 적체력 <= 0: print(f"거울 속의 {나}: 쳇, 이번에도 글렀나.") break #적의 행동 패턴 N = random.randrange(1, 4) print("\n상대의 턴!!!") time.sleep(1) if N == 1: 적공격력1 = 적공격력 + random.choice([1, 2, 3, 5, 8]) if 적공격력1 - 방어력 <= 0: print("공격은 완전히 복부로 흡수돼 방어됐따 ^^7") 방어력 -= 적공격력1 else: print("상대는 잽을 두 번 날렸다.") 체력 -= (적공격력1 - 방어력) print(f"{나}의 남은 체력 = {체력}") 방어력 = 0 elif N == 2: 적방어력 += 5 + 적정신력 print(f'상대는 덜 아프게 맞을 자세를 취했다 방어도가 {적방어력}가 되었다') elif N == 3: 적기 += 1 print("기~") if 적기 == 2: print("상대의 어퍼컷!") 체력 -= 적정신력 + random.choice([10, 15, 20, 25, 30]) print(f"{나}의 남은 체력 = {체력}") 적기 = 0 if 체력 <= 0: print("눈앞이 깜깜해졌다") print("\ntip: 이걸 지다니... 지나가던 개(발자)가 웃겠다.") Count = input("다시 하시겠습니까? q: 다시 한다. w: 다시 한다. e: 다시 한다.\n... ") if Count != "이걸맞추면당신은천재": t = 4 while t >= 1: t = t - 1 time.sleep(1) print(f"로딩까지 {t}초") 체력 = 체력1 적체력 = 50 적방어력 = 0 방어력 = 0 continue
마지막으로 이번에 설명드린 파트의 실제 작동 화면을 보여드리며 마무리해 본다.
결말1) 도망
결말2) 주인공의 승리
혹시 체력이 마이너스인 것이 보기 좋지 않다면... 추가로 if문을 활용하여 체력 = 0이라는 출력을 추가해줄 수 있다.
결말3) 적의 승리
저 포함 두 명이서 식을 짜고 고민하면서 알아낸 사실들, 그리고 그렇게 해서 나온 결과물을 최대한 자세하게 설명드리고 싶었습니다. 이번에도 분량 조절에 실패해 버렸네요...ㅎㅎ 이 글을 통해 조금이나마 기초 개념의 사용 선택지를 늘려 가신다면 좋겠습니다. 방문 감사합니다~ 구독 공감도 감사히 받고 있습니다!(꾸벅)
아 참, 뒤에 나올 전투들은 이 기본 포맷에 약간씩의 변화를 추가한 것이므로... 이제 여러분은 다 했다고도 볼 수 있을 것 같아요! 우선 여기까지 오신 분들에게는 축하드린다고 말씀드리고 싶네요! 그럼 정말 물러가겠습니다...!
'깜빡의 취미 > 파이썬을 합니다.' 카테고리의 다른 글
[파이썬/Python] 텍스트 RPG(4): 슈슉 슉 고라니 전투 (0) 2021.10.10 [파이썬/Python] 텍스트 RPG(3): 스텟 분배 (2) 2021.10.03 [파이썬/Python] 텍스트 RPG(1): 이름, 기본 스텟 (0) 2021.09.19 [파이썬/Python] 백준 8393번(합) 단계별로 풀어보기 + ['+=' 에 관한 조금 더 자세한 설명] (2) 2021.09.12 [파이썬/Python] 백준 10950번(A+B - 3) 단계별로 풀어보기 + [for문 대신 while문 써보기, 더하는 값 잘못 입력했을 때 원점으로 돌리기] (0) 2021.09.05