김김김의 게임개발
  • C# #21 TextRPG_Team 1
    2023년 08월 28일 21시 09분 20초에 업로드 된 글입니다.
    작성자: noun06

    진행상황

    • 필수 요구 사항 3번, 전투 시스템을 담당하게 되었다. 공격력 랜덤 배율 등의 생각보다 디테일하게 신경써야할 요소들이 있었다. 두 개의 클래스를 추가하게 되었으며 하나는 몬스터의 기본 정보들을 담고있는 Monster 클래스이다. 아이템이 추가되는 방식과 비슷하게 Program 클래스에서 몬스터 정보를 세팅할 수 있다. 
    • Program 클래스 내의 DisplayBattle과 DisplayBattleStart 메서드를 통해 전투 화면을 출력하였다. 이 부분에서 신경써야 할 사항은 몬스터의 숫자와 종류의 랜덤 생성이었다. 새 리스트를 만들고 랜덤의 몬스터를 선택 후 랜덤의 인덱스의 요소를 추가하는 방식으로 구현하였다. 
    • 전투를 관리하는 BattleManager 클래스는 우선 생성자를 통해 전투에 꼭 필요한 요소들인 플레이어와 몬스터 리스트를 매개변수로 받아온다. StartBattle 메서드를 통해 전체적인 전투의 시작과 진행, 끝을 나타내었고 플레이어와 몬스터 각 턴 별 적을 공격하는 로직을 만들었다. CalculateAttackPower은 공격력 랜덤 배율을 설정하여 계산하는 메서드이다. 플레이어의 공격은 기본 공격력에서 10%의 오차를 가지고 있다. 변동 배율의 범위를 정하고 랜덤한 변동 배율을 곱해 최종 공격력을 계산하였다. 

     

    문제점과 계획

    • 가장 큰 문제는 플레이어의 공격 로직에서 잘못된 부분이 있다는 것이다. 적들이 랜덤으로 출현하기 때문에, 적들의 항목이 중복되어 나오는 경우가 발생한다. 이때, 플레이어가 공격하면 중복된 적들은 데미지를 공유해서 플레이어가 적들을 번호로 선택하는 의미가 없어지는 문제가 발생했다. 몬스터 리스트 부분에서 수정이 우선적으로 필요하다. 
    • 현재는, 턴의 진행이 반 자동으로 이루어진다. 하지만 요구사항처럼 사용자 입력에 따른 턴 전환을 구현해야한다. 

     

    코드

    public class BattleManager
    {
        private Character player;
        private List<Monster> monsters;
    
        //생성자
        public BattleManager(Character player, List<Monster> monsters)
        {
            this.player = player;
            this.monsters = monsters;
        }
    
        //전투의 시작과 진행, 각각의 턴을 번갈아가며 실행.
        public void StartBattle()
        {
            while (true)
            {
                PlayerTurn();
    
                if (IsBattleOver())
                {
                    break;
                }
    
                MonstersTurn();
    
                if (IsBattleOver())
                {
                    break;
                }
            }
    
            DisplayBattleResult();
        }
    
        //플레이어 턴 동작
        private void PlayerTurn()
        {
            int input = Program.CheckValidInput(0, monsters.Count);
            if (input == 0)
            {
                Program.DisplayBattle();
            }
    
            Monster targetMonster = monsters[input - 1];
    
            if (!targetMonster.IsDead) //살아있는 몬스터만 공격
            {
                int atkPower = CalculateAttackPower(player.Atk + Program.GetItemAtkAmount()); //아이템으로 인한 추가 수치 기준으로 계산
                targetMonster.Hp -= atkPower; //적 체력 = 기존 체력 - 공격력
    
                DisplayAttackResult(player.Name, targetMonster.Name, atkPower, targetMonster.Hp); //공격 결과
                
                if (targetMonster.IsDead)
                {
                    Console.WriteLine($"{targetMonster.Name}이(가) 죽었습니다!");
                    Console.WriteLine();
                }
            }
            else
            {
                Program.DisplayError("잘못된 입력입니다.");
                PlayerTurn();
            }
        }
    
        //몬스터 턴 동작
        private void MonstersTurn()
        {
            foreach (var monster in monsters) 
            {
                if (!monster.IsDead) //살아있는 몬스터는 순차적으로 플레이어 공격
                {
                    int atkPower = CalculateAttackPower(monster.Atk);
                    player.Hp -= atkPower;
                    DisplayAttackResult(monster.Name, player.Name, atkPower, player.Hp);
                    if (player.IsDead)
                    {
                        break;
                    }
                }
            }
        }
    
        //최종 공격력을 계산
        private int CalculateAttackPower(int baseAttack)
        {
            double errorRange = 0.1; // 10% 오차 범위
            double minMultiplier = 1 - errorRange; //최소 변동 배율
            double maxMultiplier = 1 + errorRange; //최대 변동 배율
    
            //범위 내의 랜덤한 값을 곱하여 변동 배율 결정
            double randomMultiplier = new Random().NextDouble() * (maxMultiplier - minMultiplier) + minMultiplier;
            //기본 공격력에 랜덤한 변동 배율을 곱해 최종 공격력 계산(반올림하여 정수 형태로)
            int finalAttack = (int)Math.Ceiling(baseAttack * randomMultiplier);
    
            return finalAttack;
        }
    
        //전투가 종료되었는지 판단
        private bool IsBattleOver()
        {
            if (player.IsDead)
            {
                return true;
            }
    
            return monsters.All(monster => monster.IsDead); //모든 몬스터가 죽었는지 판단 후 전투가 끝났는지의 여부 반환
        }
    
        //공격 결과를 출력(공격자, 공격 대상, 데미지, 체력)
        private void DisplayAttackResult(string attacker, string target, int damage, int health)
        {
            Thread.Sleep(2000);
            Program.DisplayTitle($"{attacker}의 공격!");
            Console.WriteLine($"{target}을(를) 맞췄습니다.[데미지: {damage}]");
            Console.WriteLine();
            Console.WriteLine($"{target}");
            Console.WriteLine($"HP -> {health}");
            Console.WriteLine();
            Console.Write(">> ");
        }
    
        //전투 결과를 출력
        private void DisplayBattleResult()
        {
            if (player.IsDead)
            {
                Console.WriteLine("패배하셨습니다. 다음에 다시 도전하세요.");
            }
            else
            {
                Console.WriteLine("축하합니다! 승리하셨습니다!");
            }
        }
    }

     

     

    댓글