김김김의 게임개발
  • C# #22 TextRPG_Team 2
    2023년 08월 29일 21시 33분 10초에 업로드 된 글입니다.
    작성자: noun06

    진행상황

    • 전반적인 BattleManager 클래스의 수정을 통해 기본적인 전투 시스템 구현을 완료하였다. 일단 전투 로직부터 전투 창 출력까지 모든 전투 관련한 것들은 이 클래스에서 관리하기로 하였다. 
    • StartBattle 메서드는 전투의 시작과 진행을 나타낸다. 기존 전투 창이 두개로 나누어져 있는 것을 매개변수를 통해 하나의 메서드로 관리가 가능하도록 수정하였다. 
    • ShuffleMonsters 메서드는 몬스터 랜덤 출현을 나타낸다. 랜덤한 수와 랜덤한 종류로 출력되며 기존 데미지 공유의 문제를 다음과 같은 방식으로 해결하였다. 우선 Monster 클래스에 기존 몬스터와 같은 속성을 반환하는 Clone이라는 메서드를 만들었다. 다시 ShuffleMonsters 메서드로 돌아와서 Clone 메서드를 통해 복제된 몬스터들을 배열에 추가하고, 그 복제된 몬스터들을 최종 배열로 변환하여 할당하였다. 이렇게 함으로써, 겉으로 보기에 같은 이름과 속성을 가진 중복된 몬스터들이 출현해도, 실제로는 데미지를 공유하지 않는다.
    • DisplayBattleScreen 메서드는 전투 창을 나타내는 역할을 한다. 사망한 몬스터의 색상을 변경하며 전투에 들어가면 몬스터 앞의 숫자를 활성화시킨다. 
    • PlayerTurn과 MonsterTurn 메서드는 각 캐릭터의 턴 동작 로직을 구성한다. 죽어있는 몬스터의 경우 공격과 공격 받음이 불가능하다. 

     

    문제점과 계획

    • 현재 기본적인 던전, 스테이지 시스템을 구현하기까지 하였다. 하지만 팀원이 만든 결과 보상 시스템과 충돌이 나는 듯 보여 전반적인 수정작업이 필요한 상황이다. 또한 저장 시스템에 부터 던전 클리어 진행상황 관련된 부분을 추가해볼 계획이다.

     

    코드

    namespace TextRPG_Team
    {
        public class BattleManager
        {
            private Random Random = new Random();
            private Character player;
            private Monster[] monsters;
    
            private Dungeon dungeon;
    
            //생성자
            public BattleManager(Character player, Monster[] monsters, Dungeon dungeon)
            {
                this.player = player;
                this.monsters = monsters;
                this.dungeon = dungeon;
    
                ShuffleMonsters(); // 몬스터 배열 섞기
            }
    
            //전투 시작과 진행
            public void StartBattle(Character player, Dungeon dungeon)
            {
    
                while (true)
                {
                    DisplayBattleScreen(false); //전투 화면 표시
                    int input = Program.CheckValidInput(0, 1);
                    switch (input)
                    {
                        case 0:
                            Program.DisplayGameIntro();
                            break;
    
                        case 1:
                            DisplayBattleScreen(true);
                            break;
                    }
    
                    PlayerTurn();
                    MonsterTurn();
    
                    if (CheckBattleEnd())
                    {
                        if (CheckAllMonstersDead())
                            dungeon.ClearDungeon(); // 던전 클리어 처리
                        else
                            Program.DisplayGameIntro();
                    }
                }
    
            }
    
    
            //몬스터 랜덤 출현
            public void ShuffleMonsters()
            {
                int numberOfMonsters = Random.Next(1, 5); // 1부터 4까지 랜덤한 숫자
                List<Monster> newMonstersList = new List<Monster>();
    
                for (int i = 0; i < numberOfMonsters; i++)
                {
                    int randomIndex = Random.Next(0, monsters.Length);
                    Monster randomMonster = monsters[randomIndex].Clone(); // 몬스터를 복제하여 추가
                    newMonstersList.Add(randomMonster);
                }
    
                monsters = newMonstersList.ToArray(); // 복제된 몬스터들을 배열로 변환하여 할당
                
                //몬스터 수 업데이트
            }
    
            //전투 시작화면
            public void DisplayBattleScreen(bool showMonsterNumbers)
            {
                Console.Clear();
                WriteColored("Battle!!", ConsoleColor.Yellow);
                Console.WriteLine();
    
                for (int i = 0; i < monsters.Length; i++)
                {
                    Monster curMonster = monsters[i];
    
                    if (showMonsterNumbers) //몬스터 숫자 활성화
                    {
                        //사망한 몬스터 색상 변경
                        if (curMonster.IsDead)
                            WriteColored($"{i + 1}. {curMonster.Name} dead\n", ConsoleColor.Red);
                        else
                            Console.WriteLine($"{i + 1}. {curMonster.Name}  HP {curMonster.CurrentHp}");
                    }
                    else
                    {
                        if (curMonster.IsDead)
                            WriteColored($"{curMonster.Name}  dead\n", ConsoleColor.Red);
                        else
                            Console.WriteLine($"{curMonster.Name}  HP {curMonster.CurrentHp}");
                    }
                }
    
                Console.WriteLine();
                Console.WriteLine("[내정보]");
                Console.WriteLine($"{player.Name} (Lv.{player.Level})");
                Console.WriteLine($"HP {player.CurrentHp}/{player.MaxHp}");
                Console.WriteLine();
                Console.WriteLine("0. 나가기");
                Console.WriteLine("1. 공격");
                Console.WriteLine();
                Console.WriteLine("원하시는 행동을 입력해주세요.");
                Console.Write(">> ");
            }
    
            //플레이어 턴 동작 로직
            void PlayerTurn()
            {
                Console.WriteLine();
                Console.WriteLine("대상을 선택해주세요.");
                Console.Write(">> ");
                int targetIndex = Program.CheckValidInput(1, monsters.Length) - 1;
    
                if (monsters[targetIndex].IsDead)
                {
                    Program.DisplayError("잘못된 입력입니다.");
                    PlayerTurn();
                    return;
                }
    
                int damage = CalculateDamage(player.Atk + Program.GetItemAtkAmount()); //아이템 추가 공격력 반영
                monsters[targetIndex].CurrentHp -= damage;
    
                Console.WriteLine();
                Console.WriteLine($"{player.Name}의 공격!");
                Console.WriteLine($"{monsters[targetIndex].Name}을(를) 맞췄습니다. [데미지 : {damage}]");
                if (monsters[targetIndex].IsDead)
                {
                    Console.WriteLine($"{monsters[targetIndex].Name}");
                    Console.WriteLine("HP 0 -> Dead");
                }
                Console.WriteLine();
                Console.WriteLine("아무 키를 눌러 계속");
                Console.Write(">> ");
                Console.ReadLine();
            }
    
            //몬스턴 턴 동작 로직
            void MonsterTurn()
            {
                foreach (var monster in monsters)
                {
                    if (!monster.IsDead)
                    {
                        int damage = CalculateDamage(monster.Atk);
                        player.CurrentHp -= damage;
    
                        Console.WriteLine();
                        Console.WriteLine($"{monster.Name}의 공격!");
                        Console.WriteLine($"{player.Name}을(를) 맞췄습니다. [데미지 : {damage}]");
                        if (player.CurrentHp <= 0)
                            break;
                    }
                }
    
                Console.WriteLine();
                Console.WriteLine("아무 키를 눌러 계속");
                Console.Write(">> ");
                Console.ReadLine();
            }
            public int CalculateDamage(int baseAtk)
            {
                double errorRange = 0.1; // 10% 오차 범위
                double minMultiplier = 1 - errorRange; //최소 변동 배율
                double maxMultiplier = 1 + errorRange; //최대 변동 배율
    
                //변동 범위의 크기(max-min) 내의 랜덤한 실수값을 곱하여 변동 배율 결정
                double randomMultiplier = new Random().NextDouble() * (maxMultiplier - minMultiplier) + minMultiplier;
                //기본 공격력에 랜덤한 변동 배율을 곱해 최종 공격력 계산(반올림하여 정수 형태로)
                int finalAttack = (int)Math.Ceiling(baseAtk * randomMultiplier);
    
                return finalAttack;
            }
            //전투 종료 체크
            bool CheckBattleEnd()
            {
                bool allMonstersDead = true;
                foreach (var monster in monsters)
                {
                    if (!monster.IsDead)
                    {
                        allMonstersDead = false;
                        break;
                    }
                }
    
                if (allMonstersDead)
                {
                    Console.Clear();
                    WriteColored("Battle!! - Result", ConsoleColor.Yellow);
                    Console.WriteLine();
                    Console.WriteLine("Victory");
                    Console.WriteLine();
                    Console.WriteLine($"던전에서 몬스터 {monsters.Length}마리를 잡았습니다.");
                    Console.WriteLine();
    
                    Console.WriteLine($"{player.Name} (Lv.{player.Level})");
                    Console.WriteLine($"HP {player.CurrentHp}/{player.MaxHp}");
                    Console.WriteLine();
                    Console.WriteLine("아무 키를 눌러 계속");
                    Console.Write(">> ");
                    Console.ReadLine();
                    return true;
                }
    
                if (player.CurrentHp <= 0)
                {
                    Console.Clear();
                    WriteColored("Battle!! - Result", ConsoleColor.Yellow);
                    Console.WriteLine();
                    Console.WriteLine("You Lose");
                    Console.WriteLine();
                    Console.WriteLine($"{player.Name} (Lv.{player.Level})");
                    Console.WriteLine($"HP {player.CurrentHp}/{player.MaxHp}");
                    Console.WriteLine();
                    Console.WriteLine("아무 키를 눌러 계속");
                    Console.Write(">> ");
                    Console.ReadLine();
                    return true;
                }
    
                return false;
            }
    
            //모든 몬스터가 사망했는지 확인
            bool CheckAllMonstersDead()
            {
                foreach (var monster in monsters)
                {
                    if (!monster.IsDead)
                        return false;
                }
                return true;
            }
    
            //텍스트 색상 지정
            static void WriteColored(string text, ConsoleColor color)
            {
                ConsoleColor originalColor = Console.ForegroundColor;
                Console.ForegroundColor = color;
                Console.Write(text);
                Console.ForegroundColor = originalColor;
            }
        }
    }

     

     

    댓글