오늘의 목표 !
필수 기능에서 부족했던 점을 보완하고 도전 기능을 완성한다.
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
클래스 외부에 전역으로 파일 읽는 함수를 만들어서 써봤다.
이렇게 하면 만든 몬스터 리스트가 Future<List<Monster>>
로 반환돼서 타입 변환이 곤란해졌다...
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
(만드려는 기능에 비해 위젯을 적용하는 게 더 복잡해보여서 보류...)
Future<void>
로 함수 만들기튜터님께서 제시해주신 방향인데, 도전 기능을 만들고 나서 시도해보려고 한다.
클래스 안에 아이템 사용 가능 여부를 판별하는 bool item
변수를 두고, 아이템이 남아있는 경우에만 사용되도록 했다.
// 아이템을 사용할지 정하는 case 문
case 3:
if(item) {character.attackWithItem(monster, item); break;}
else {print('아이템이 없습니다.');}
attackWithItem(String, bool)
은 Character
클래스에 새로 생성해준, 2배가 된 공격력으로 몬스터를 공격하는 함수이다.
물론 ① 기본 공격 함수를 두 번 써도 되고 ② 기본 공격 함수에 아이템 사용 여부를 체크하는 매개변수를 추가해도 된다.
하지만 나만의 기능에서 공격하는 양이 랜덤이거나 크리티컬이 뜰 수 있도록 만들 예정인데, 이런 경우 공격 함수를 두 번 호출하면 두 번의 랜덤값이 달라진다.
그래서 함수를 분리해주었다. 2번대로 안 한 이유는 함수를 쓸 때마다 매개변수를 두 개씩 집어넣는 게 싫어서...
먼저 클래스에 int monsterDefUp
을 추가한다.
이 변수가 0 또는 1
일 때는 건너뛰고 2
일 때는 몬스터의 방어력을 올리는 Monster.defUp()
을 호출한다. 방어력 추가가 끝나면 변수를 다시 0으로 만든다.
// 몬스터 방어력 증가 이벤트
if(monsterDefUp!=2){monsterDefUp++;}
else{monster.defUp(); monsterDefUp-=2;}
문제 : 승패 여부를 출력하지 않는다.
게임의 조건을 아래처럼 설정해두었다.
쓰러지지 않고 몬스터 4마리를 물리친다 : 승리
몬스터 4마리를 물리치기 전에 쓰러진다 : 패배
쓰러지면서 4번째 몬스터를 물리친다 : 무승부
이 정보가 화면에 출력되지 않고 있었다.
이번에 만들어볼 나만의 기능은 크리티컬이다.
게임을 할 때, 갑자기 크리티컬이 나와서 지고 있던 상황에 이기는 경험을 다들 해보았을 것 같다! 그런데 이 게임에서는 공격 값이 항상 같아서 그런 재미가 부족해보인다.
void attackMonster(Monster monster){
monster.hp-=atk;
print('$name이(가) ${monster.name}에게 $atk의 데미지를 입혔습니다.');
}
지금은 이렇게 정해진 값만큼 일정하게 상대를 공격한다.
여기에 Random()
을 이용해서 두 가지 기능을 추가해보겠다.
오차 범위를 ±5 정도로 하고 싶었지만 공식 문서를 보니 Random
에서 음수 값을 뽑을 수는 없는 것 같다...
(참고 : https://api.flutter.dev/flutter/dart-math/Random-class.html)
코드를 살펴보면, atkValue
라는 실제 공격량을 나타내는 변수를 선언하고 시작했다. 이 수는 0~4
사이의 랜덤한 값에 캐릭터의 원래 공격력을 더한 수이다.
만약 일정 확률로 크리티컬할 시 atkValue
에 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% 사이로 방어되게 랜덤 요소 추가