[TIL] RPG 게임 3 - 도전 기능과 마무리

티라노·2024년 11월 7일
0

Today I Learned

목록 보기
16/21

오늘의 목표 !

필수 기능에서 부족했던 점을 보완하고 도전 기능을 완성한다.

RPG 게임 제작 시리즈
1) 필수 기능 https://velog.io/@utiranoj/TIL-RPG-%EA%B2%8C%EC%9E%84-%ED%95%84%EC%88%98-%EA%B8%B0%EB%8A%A5
2 ) 필수 기능 점검 + 도전 기능 시작 https://velog.io/@utiranoj/TIL-RPG-%EA%B2%8C%EC%9E%842-%ED%95%84%EC%88%98-%EA%B8%B0%EB%8A%A5-%EB%B3%B4%EC%B6%A9

파일 입력 동기에서 비동기로 바꾸기

1) Future로 파일을 읽어오는 함수 만들기

클래스 외부에 전역으로 파일 읽는 함수를 만들어서 써봤다.
이렇게 하면 만든 몬스터 리스트가 Future<List<Monster>> 로 반환돼서 타입 변환이 곤란해졌다...

2) Game 클래스 안에 파일 읽는 함수 만들기


Game 클래스 안에
1) 파일을 읽어서 String으로 반환하는 함수와
2) 읽은 데이터를 받아서 쓰기 좋게 리스트로 가공하는 함수

를 각각 만들어보았다. 이렇게 하면 1번 함수가 데이터를 Future<String> 로 반환하기 때문에 toString 을 이용해서 형변환을 시도해봤다.
그리고 setMonsterList 에서 파일이 잘 읽혔는지 확인해보았는데...

코드
print('읽은 파일은 : ${readFile('assets/txt/monsters.txt').toString()}');

???(원래대로라면 name,hp,atk...처럼 출력되어야 한다.)

readFile 에서는 잘 출력되는 것을 보니 toString 을 쓰면서 뭔가 문제가 발생한 것 같다.
Future 로 만든 데이터는 형변환 함수를 사용할 수 없는 것 같다.

검색해보니 FutureBuilder 라는 것을 사용할 수 있다고 한다.
https://stackoverflow.com/questions/76347560/how-to-get-a-string-from-the-futurestring
await 기능 관련된 문제도 있는 것 같다.
https://stackoverflow.com/questions/54389775/instance-of-futurestring-instead-of-showing-the-value
(만드려는 기능에 비해 위젯을 적용하는 게 더 복잡해보여서 보류...)

3) Future<void>로 함수 만들기

튜터님께서 제시해주신 방향인데, 도전 기능을 만들고 나서 시도해보려고 한다.


도전 기능 2

클래스 안에 아이템 사용 가능 여부를 판별하는 bool item 변수를 두고, 아이템이 남아있는 경우에만 사용되도록 했다.

// 아이템을 사용할지 정하는 case 문
case 3:
  if(item) {character.attackWithItem(monster, item); break;}
  else {print('아이템이 없습니다.');}

attackWithItem(String, bool)Character 클래스에 새로 생성해준, 2배가 된 공격력으로 몬스터를 공격하는 함수이다.
물론 ① 기본 공격 함수를 두 번 써도 되고 ② 기본 공격 함수에 아이템 사용 여부를 체크하는 매개변수를 추가해도 된다.
하지만 나만의 기능에서 공격하는 양이 랜덤이거나 크리티컬이 뜰 수 있도록 만들 예정인데, 이런 경우 공격 함수를 두 번 호출하면 두 번의 랜덤값이 달라진다.
그래서 함수를 분리해주었다. 2번대로 안 한 이유는 함수를 쓸 때마다 매개변수를 두 개씩 집어넣는 게 싫어서...


도전 기능 3

먼저 클래스에 int monsterDefUp 을 추가한다.
이 변수가 0 또는 1 일 때는 건너뛰고 2 일 때는 몬스터의 방어력을 올리는 Monster.defUp() 을 호출한다. 방어력 추가가 끝나면 변수를 다시 0으로 만든다.

// 몬스터 방어력 증가 이벤트
if(monsterDefUp!=2){monsterDefUp++;}
else{monster.defUp(); monsterDefUp-=2;}

문제 : 승패 여부를 출력하지 않는다.
게임의 조건을 아래처럼 설정해두었다.
쓰러지지 않고 몬스터 4마리를 물리친다 : 승리
몬스터 4마리를 물리치기 전에 쓰러진다 : 패배
쓰러지면서 4번째 몬스터를 물리친다 : 무승부
이 정보가 화면에 출력되지 않고 있었다.

print 문을 추가해서 결과를 출력했다.


도전 기능 4 - 크리티컬

이번에 만들어볼 나만의 기능은 크리티컬이다.
게임을 할 때, 갑자기 크리티컬이 나와서 지고 있던 상황에 이기는 경험을 다들 해보았을 것 같다! 그런데 이 게임에서는 공격 값이 항상 같아서 그런 재미가 부족해보인다.

  void attackMonster(Monster monster){
    monster.hp-=atk;
    print('$name이(가) ${monster.name}에게 $atk의 데미지를 입혔습니다.');
  }

지금은 이렇게 정해진 값만큼 일정하게 상대를 공격한다.
여기에 Random() 을 이용해서 두 가지 기능을 추가해보겠다.

  1. 일반공격 양은 약간의 오차 범위를 두고 변한다.
  2. 15% 확률로 크리티컬일 시 데미지가 2배로 늘어난다.

오차 범위를 ±5 정도로 하고 싶었지만 공식 문서를 보니 Random 에서 음수 값을 뽑을 수는 없는 것 같다...
(참고 : https://api.flutter.dev/flutter/dart-math/Random-class.html)

코드를 살펴보면, atkValue 라는 실제 공격량을 나타내는 변수를 선언하고 시작했다. 이 수는 0~4 사이의 랜덤한 값에 캐릭터의 원래 공격력을 더한 수이다.
만약 일정 확률로 크리티컬할 시 atkValue 에 2를 곱해서 반영한다.

정상적으로 작동하는 것을 볼 수 있다.


도전 기능 4(2) - 도트 데미지

이 게임의 몬스터는 포켓몬 이름을 따왔는데 실제로 포켓몬스터 게임을 하다 보면 도트 데미지를 걸어두고 버티면서 클리어할 때가 있다. 이 점을 스킬로 구현해보도록 하겠다.

지금은 매 턴마다 행동을 case 로 구분하고 있어서 각 턴이 서로 독립적이다.
그런데 도트 데미지는 이후 n턴간 피해~~ 식으로 공격하기 때문에 매 턴의 모든 행동을 할 때마다 도트 적용 함수를 같이 써줘야 한다.

  // Character.dotSkill()
  void dotSkill(Monster monster){
    monster.hp-=4;
    print('${monster.name}이(가) 4 만큼 도트 데미지를 입고 있습니다.');
  }
  
  // Game.dotAttack()
  void dotAtack(Monster monster){
    if(dot>0){
      character.dotSkill(monster);
      dot--; // Game 클래스의 멤버변수
    }
  }
스킬 적용 후 실행 화면


기라티나에게 도트 데미지가 4씩 들어가는 것을 확인할 수 있다.


완료한 기능

필수 기능 1 : 파일 읽기
필수 기능 2 : 캐릭터 이름 입력받기
필수 기능 3 : 파일 쓰기

도전 기능 1 : 보너스 체력 기능
도전 기능 2 : 아이템 사용
도전 기능 3 : 3턴마다 몬스터 방어력 증가
도전 기능 4 : 공격값에 랜덤 요소 추가, 크리티컬 기능 추가, 도트 데미지 스킬 추가, 방어할 때 30%~100% 사이로 방어되게 랜덤 요소 추가

0개의 댓글