ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 내일배움캠프 12일차 TIL - 팀 프로젝트 전투 기능 관련
    TIL/C# 2024. 4. 30. 20:57

     

    [학습목표]

    C# 강의를 완강하고, 팀 프로젝트 필수 구현기능을 구현한다.

    [학습내용]

    오늘은 오전에 C# 강의를 모두 끝냈다. 5주차 강의는 정렬 알고리즘과 탐색 알고리즘 등 주로 알고리즘에 관한 내용이었는데, 학교에서 다 배운 내용이어서 빠르게 들었다.

     

     

    오후부터 팀 프로젝트 진행을 시작했다. 원래는 각자 기능을 분담하여 구현하려 하였으나, 게임의 핵심인 몬스터와 전투 기능이 구현되지 않으면 다른 부분을 구현할 수 없었다. 그래서 핵심 파트 부분을 빠르게 구현할 수 있도록 다 같이 보며 구현을 진행하였다.

     

    오늘 구현해야 하는 기능은 전투 기능이었다. 우선 전투 관련 기능을 구현하기 위해 배틀 매니저를 만들었다.

    배틀 매니저
    싱글톤 형태로 사용할 것이다.

     

    그리고 이 배틀 매니저 안에 전투 관련 기능을 구현했다. 1 ~ 4마리의 몬스터들을 무작위로 생성한다거나, 데미지 계산을 하는 메서드 등을 구현했다.

    using OceanStory.Interfaces;
    using OceanStory.Monsters;
    
    namespace OceanStory
    {
        internal class BattleManager
        {
            public List<IMonster> monsters = new List<IMonster>();
            public int TargetIndex, TargetDamage;
            public double TargetBeforeHp;
    
            public void MakeEnemy()
            {
                monsters.Clear();
                Random random = new Random();
                
                int randomMake = new Random().Next(1, 5);
                for (int i = 0; i < randomMake; i++)
                {
                    int makeMonsters = random.Next(1, 4);
                    switch (makeMonsters)
                    {
                        case 1:
                            Minion minion = new("미니언");
                            monsters.Add(minion);
                            break;
                        case 2:
                            Voidling voidling = new("공허충");
                            monsters.Add(voidling);
                            break;
                        case 3:
                            CanonMinion canonMinion = new("대포미니언");
                            monsters.Add(canonMinion);
                            break;
                    }
                }
            }
            public void AttackDamage(int input)
            {
                TargetIndex = input - 1;
                TargetBeforeHp = monsters[input - 1].Hp;
                double monstersHp = monsters[input - 1].MaxHp;
                int attackDamage = new Random().Next((int)Program.Character.Atk - (int)Math.Ceiling(monstersHp / Program.Character.Atk), (int)Program.Character.Atk + (int)Math.Ceiling(monstersHp / Program.Character.Atk));
                TargetDamage = attackDamage;
                monsters[input - 1].Hp -= attackDamage;
                if (monsters[input - 1].Hp <= 0) monsters[input - 1].MonsterDead = true;
            }
        }
    }

     

    그리고 전투를 진행할 씬을 구성했다. 우선 핵심 구현 전투 기능에서 요구되는 씬은 총 5가지로 나눌 수 있었다.

    1. 전투 시작

    2. 공격할 몬스터 선택

    3. 플레이어의 공격 시도 결과

    4. 몬스터로부터 받은 공격 결과

    5. 승패 결과

    여기에서 1번과 2번은 화면 출력에 차이가 거의 없어 하나의 화면으로 구현하기로 했다. 3번과 4번을 합칠지는 조금 더 고민해봐야겠다.

     

    1번과 2번을 합치기 위해 아래의 코드를 사용했다.

    bool select = false;
    int input;
    if (!select)
    {
        Console.WriteLine("1. 공격");
        Console.WriteLine("2. 아이템 사용");
        Console.WriteLine("3. 도망가기");
        input = Program.SceneManager.GetUserInput(3);
        switch (input)
        {
            case 1:
                select = true;
                break;
            case 2:
                break;
            case 3:
                break;
            default:
                break;
        }
    
    }
    else
    {
        Console.WriteLine("0. 취소");
        input = Program.SceneManager.GetUserInput(Program.BattleManager.monsters.Count(), "대상을 선택해주세요.");
        if (0 <= input && input <= Program.BattleManager.monsters.Count())
        {
            if (input == 0)
            {
                select = false;
            }
            else
            {
                if (!Program.BattleManager.monsters[input - 1].MonsterDead)
                {
                    Program.BattleManager.AttackDamage(input);
                    Program.SceneManager.ChangeScene("AttackScene");
                }
            }
        }
    }

     

    여기에서 아이템 사용과 도망가기 등의 기능을 추가 구현하려면 아마 많은 수정이 필요할 것으로 보인다. 하지만 아직 거기까진 생각하지 않기로 하고 3번을 출력하는 걸 구현하기로 했다.

     

    namespace OceanStory.Scenes
    {
        internal class AttackScene : Scene
        {
            public override void RunScene()
            {
                while(true)
                {
                    Console.Clear();
                    Console.WriteLine("Battle!!");
                    Console.WriteLine($"\n{Program.Character.Name} 의 공격!");
                    Console.WriteLine("Lv.{0} {1} 을(를) 맞췄습니다. [데미지 : {2}]",
                        Program.BattleManager.monsters[Program.BattleManager.TargetIndex].Level,
                        Program.BattleManager.monsters[Program.BattleManager.TargetIndex].Name,
                        Program.BattleManager.TargetDamage);
                    Console.WriteLine("\nLv.{0} {1}",
                        Program.BattleManager.monsters[Program.BattleManager.TargetIndex].Level,
                        Program.BattleManager.monsters[Program.BattleManager.TargetIndex].Name);
                    Console.WriteLine("HP {0} -> {1}",
                        Program.BattleManager.TargetBeforeHp,
                        Program.BattleManager.monsters[Program.BattleManager.TargetIndex].MonsterDead ?
                        "Dead" : Program.BattleManager.monsters[Program.BattleManager.TargetIndex].Hp);
                    Console.WriteLine("\n0. 다음");
                    int input = Program.SceneManager.GetUserInput(0);
                    if (input == 0) return;
                }
            }
        }
    }

     

    3번은 어렵지 않았다. 그러나, Program.xxx.xxx 이렇게 접근하는 게 많다. 이걸 namespace라던가로 줄인다거나, 더 좋은 방법이 있을지 고민해봐야겠다.

    [결과물]

    BattleScene
    BattleScene2
    공격 성공화면
    공허충이 Dead가 된 모습
    죽은 몬스터는 공격할 수 없다

     

    [회고]

    코드가 점점 스파게티 코드가 되는 것 같다. 모든 걸 클래스화 시키고 시작하면 조금 더 편했을 거 같단 생각이 든다. 설계하는 작업이 얼마나 중요한지 조금 느끼게 된다. 프로젝트가 잘 진행될 수 있을지 조금 걱정된다.