5. 내일배움캠프 5일차 TIL : TextRPG 제작2

woollim·2024년 9월 25일
0

내일배움캠프TIL

목록 보기
4/20
post-thumbnail

■ 학습 개요

○ 오늘 계획

  1. 팀원들과 과제원들과 과제 생각해보기
  2. 코드 개선하기
  3. TIL에서 코드 리뷰하기

○ 학습 회고

  1. 새벽에는 어제 짠 코드가 맘에 안 들어서 코드를 클래스로 모듈화 시키는 작업을 했다. 아직 마음에 안 들지만 어제보단 좀 더 객체지향에 맞는 프로젝트로 수정한 것 같다.
    조금 더 수정해서 과제를 제출할까 고민했지만, 어디를 어떻게 고쳐야할지 감이 잡히지 않아서 일단 제출했다.

  2. 오전부터 저녁전까지는 아직 과제 제출 하지 않으신 팀원분께 코드를 설명 하며 과제에 대해 다시 생각하는 시간을 가졌다. 머릿속으로 생각하던 것과 달리 말로 정리하여 설명을 하다보니 내가 어느 부분에 이해가 덜 된 것인지 파악이 됐다.
    C#의 클래스와 내부 메소드 이용에 대한 것과 어떻게 변수를 선언해야 보안상 이점이 되는지 등 구체적으로 어느 부분이 더 공부가 필요한지 알 수 있었다.

  3. 어제 혼자 코드를 작성하며 궁금했던 점과 오늘 팀원분과 코드 얘기를 하며 헛갈렸던 부분들을 정리하여 튜터님께 질문을 했다. 튜터님이 상세하게 알려주신 덕분에 궁금했던 것 그 이상을 배우게되었다.

  4. 저녁이후론 튜터님이 알려주신 깃허브 프로젝트를 참고하여 코드를 분석했다. 지금 까지는 공부한 C# 이론들을 어떻게 적용하여 구현해야될지 감이 잡히지 않았는데, 실례를 보니 이해가 더 쏙쏙 잘됐다.
    옛날에 혼자 독학할때는 이런 좋은 자료들을 찾는 법을 몰랐었는데 확실히 가르쳐주시는 분이 있으니 지식 이상의 꿀팁들도 배울 수 있어서 좋다!

○ TIL 작성계획

  1. 오늘까지 작성한 코드 중 특정 코드에 대해 어떻게 개선하면 좋을지 사견이 담긴 개선방안을 적으려 한다.
  2. 본인이 중요하게👍 생각하거나 헛갈리는 부분😥을 집중 작성할 계획


■ 질문 답변 정리

Q1. 접근지정자 internal의 범위는 어디까지 인가요?

  • VS에서는 같은 프로젝트 내에서만 접근 가능

Q2. 인터페이스에서 프로퍼티를 선언할 때 get, set 둘다 필요한가요?

  • 상관 없지만. get만 있는 것이 보안적으로 좋음
  • 상속받는 클래스에서 추가적으로 set을 붙일 수 있음

Q3. 프로퍼티 접근 지정자

  • set 만 접근 지정자를 private로 하여 보안에 좋은 구조로 만들 수 있음
    public string name { get; private set; }
  • 변수 자체를 건드는 것보단 input 함수를 만들어 초기화 시키는것이 좋음.

Q4. 게임 프로그래밍 패턴 참고하기 좋은 깃허브 링크



■ TextRPG 코드 분석

○ 전체적 클래스 구성 및 기능 요약

  • 과제로 제출한 깃허브 링크 입니다. 과제 TextRPG

  • 인터페이스

    • ICharacter : 게임 내 캐릭터(몬스터, 유저) 인터페이스
    • IUser : 유저 인터페이스
  • 클래스

    • Action : 던전 메소드, 휴식 메소드, 레벨업 메소드
    • GameManager : 게임시작창 메소드, 마을행동선택 메소드, 캐릭터선택 메소드, 게임데이터저장 메소드
    • InputCheck : 모든 번호 선택 입력 유효성 검사 메소드
    • Inventory : 인벤토리창 메소드, 장착관리 메소드
    • Store : 상점이용창 메소드, 아이템 구매 메소드, 아이템 판매 메소드, 훔치기 기능(도둑이스터에그)
    • Item : 아이템정보 프로퍼티, 아이템클래스 배열, 아이템초기화 메소드
    • Main : 메인 메소드
    • Monster : 몬스터정보 프로퍼티
    • User : 유저정보 프로퍼티, 상태창 메소드
  • 변수명 규칙이 제각각이어서 통일이 필요하다.

○ 인터페이스

  • 캐릭터 인터페이스 : 게임내 캐릭터인 몬스터, 유저가 공통으로 갖는 필수 요소 지정역할
    public interface ICharacter
    {
        String Name { get; } // 이름
        String Tribe { get; } // 종족 : User or Monster
        int Health { get; } // HP
        int Attack { get; } // 생존여부
        bool IsDead { get; } // 공격력
        void TakeDamage(int damage); // HP 차감 함수
    }
  • 유저 인터페이스 : 게임내 유저가 공통으로 갖는 필수 요소 지정역할
public interface IUser
{
    public string UserClass { get; } // 직업
    public int Level { get; } // 레벨
    public int DefensivePower { get; } // 방어력
    public int Gold { get; } // 골드
    public int ClearCount { get; } // 던전 클리어 횟수
    public int EquipArmorStatusNum { get; } // 장착 갑옷 상태수치
    public int EquipWeaponStatusNum { get; } // 장착 갑옷 상태수치
}

○ Main() : 메인 메소드

  • 현재는 메인 코드에 저장파일 불러오기 기능 코드가 첨가 되어있다.
  • 따로 GameManager클래스 내에 게임 정보 불러오기 메소드를 만들어 분리하려 한다.
  • 여기서 else문 안에서만 new를 사용하여 클래스 객체를 생성했는데, 문제가 없을지 궁금하다.
    아직까지는 오류나 따로 문제가 없지만 제대로 된 코드인지 의문이 든다.
// ------------------- 게임 시작 전 데이터 관리  -------------------
// 객체생성
GameManager gameManager = new GameManager();

User user;
Item gameItem;

// 로딩창
gameManager.LodingScreen();

// 저장 파일이 있으면 불러오기
if (File.Exists(gameManager.filePath1))
{
    string jsonData1 = File.ReadAllText(gameManager.filePath1); // 유저 정보
    string jsonData2 = File.ReadAllText(gameManager.filePath2); // 아이템 정보

    // 객체 생성, 데이터 불러오기
    user = JsonConvert.DeserializeObject<User>(jsonData1);
    gameItem = JsonConvert.DeserializeObject<Item>(jsonData2);
}
else // 없으면 이름부터 적기
{
    // 객체 생성
    user = new User();
    gameItem = new Item();
    gameItem.AddItem();
    // 최초 시작창
    gameManager.StartScreen(user);
}

// ------------------- 게임 플레이 -------------------
gameManager.GamePlay(user, gameItem);

○ ItemEquip() : 장착 관리 메소드

  • Inventory 클래스에 포함된 메소드, 인벤토리 창에서 장착을 관리한다.
  • 현재 매우 지저분한 상태로 정리가 필요해 보인다
  • 특히, 아이템 정렬 부분은 인벤토리창 메소드와 동일하므로 따로 분리하여 메소드로 만드는 것이 좋을 것 같다.
  • 아이템 장착, 해제로 인한 유저 정보 값이 변하는 부분도 따로 빼서 메소드로 만드는 것이 좋을 것 같다.
  • 현재 장착 아이템을 찾는 for문이 성능적으로 안좋아 보인다. 다른 좋은 코드가 없을지 생각해보아야겠다.
public void ItemEquip(User user, Item gameItem)
 {
     Inventory inventory = new Inventory();
     bool exit = false;
     while (!exit)
     {
         Console.Clear();

         Console.WriteLine("[인벤토리 - 장착관리]");
         Console.WriteLine("보유 중인 아이템 장착을 관리할 수 있습니다.");
         Console.WriteLine();
         Console.WriteLine("[아이템 목록]");
         Console.WriteLine();

         int itemCount = 1;
         for (int i = 0; gameItem.item[i] != null; i++)
         {
             if (gameItem.item[i].buy == true)
             {
                 gameItem.item[i].listNum = itemCount++;
                 Console.Write($"- {gameItem.item[i].listNum} ");
                 Console.Write($"{gameItem.item[i].name}");
                 if (gameItem.item[i].equip == true)
                     Console.Write("[E]");
                 else
                     Console.Write("\t");
                 Console.Write($"\t| {gameItem.item[i].effect} +{gameItem.item[i].effectIfo}\t| {gameItem.item[i].func}");
                 Console.WriteLine();
             }
         }

         Console.WriteLine();
         Console.WriteLine("0. 나가기");
         Console.WriteLine();
         Console.WriteLine("원하시는 행동을 입력해주세요.");
         Console.Write(">> ");

         int select = InputCheck.Check(0, itemCount - 1);

         if (select == 0)
             break;
         else if (select == -1)
             continue;
         else
         {
             int nCnt = 0;
             while (true) // 아이템 배열에서 선택된 아이템 찾는 중, nCnt에 해당 인덱스 저장
             {
                 if (select == gameItem.item[nCnt].listNum)
                     break;
                 else
                     nCnt++;
             }

             // 장착 개선 
             if (gameItem.item[nCnt].equip == true)
             {
                 // 장착 해제
                 gameItem.item[nCnt].equip = false;
                 if (gameItem.item[nCnt].effect == "방어력")
                 {
                     user.EquipArmorStatusNum = 0;
                 }
                 else if (gameItem.item[nCnt].effect == "공격력")
                 {
                     user.EquipWeaponStatusNum = 0;
                 }
             }
             else
             {
                 // 장착
                 // 이전 장착한 아이템에 관련된 후처리
                 for (int i = 0; gameItem.item[i] != null; i++)
                 {
                     // 장착외 아이템은 장착 해제 처리
                     if (gameItem.item[nCnt].effect == gameItem.item[i].effect && gameItem.item[i].equip == true)
                     {
                         gameItem.item[i].equip = false;
                         if (gameItem.item[nCnt].effect == "방어력")
                         {
                             user.EquipArmorStatusNum = 0;
                         }
                         else if (gameItem.item[nCnt].effect == "공격력")
                         {
                             user.EquipWeaponStatusNum = 0;
                         }
                     }
                 }
                 // EquipArmorStatusNum -> 유저가 장착한 방어구 수치 저장 변수
                 gameItem.item[nCnt].equip = true;
                 if (gameItem.item[nCnt].effect == "방어력")
                 {
                     user.EquipArmorStatusNum = gameItem.item[nCnt].effectIfo;
                 }
                 else if (gameItem.item[nCnt].effect == "공격력")
                 {
                     user.EquipWeaponStatusNum = gameItem.item[nCnt].effectIfo;
                 }
             }

             // 목록 번호 초기화
             for (int i = 0; gameItem.item[i] != null; i++)
             {
                 gameItem.item[nCnt].listNum = -1;
             }
         }
     }
 }

○ BuyItem() : 상점-구매 메소드

  • 위 인벤토리 메소드와 마찬가지로 아이템 정렬 코드가 비슷하다. 이 또한 분리하여 메소드로 만들 방안을 궁리해보아야겠다
Console.WriteLine("[상점 - 아이템 구매]");
Console.WriteLine("구매할 아이템을 선택하세요,");
Console.WriteLine();
Console.WriteLine("[아이템 목록]");
Console.WriteLine();

int itemCount = 1;
for (int i = 0; gameItem.item[i] != null; i++)
{
    itemCount++;
    Console.Write($"- {i + 1} ");
    Console.Write($"{gameItem.item[i].name}\t| ");
    if (gameItem.item[i].buy == true)
        Console.Write("구매완료");
    else
        Console.Write($"{gameItem.item[i].price} G  ");
    Console.Write($"\t| {gameItem.item[i].effect}+{gameItem.item[i].effectIfo}\t| {gameItem.item[i].func}");
    Console.WriteLine();
}

○ Item 클래스 : 아이템정보 관련 클래스

  • 현재는 AddItem에서 직접적으로 입력된 정보들만 아이템이 추가되고 있다. 수동으로 추가할 수 있는 메소드를 추가로 만들어보고싶다
  • 프로퍼티 변수명을 C#의 프로퍼티 변수명으로 자주쓰이는 카스칼 형식으로 바꾸고 set 은 제외하고 추후 만들 input함수를 통해서만 값 입력 할 수 있게끔 바꾸려한다. -> 캡슐화, 보안성 향상이 목표
    public class Item
    {
        public string name { get; set; }
        public bool equip { get; set; } // 장착 여부
        public bool buy { get; set; } // 구매 여부
        public string effect { get; set; } // 효과
        public int effectIfo { get; set; }  // 효과 수치
        public string func { get; set; } // 기능
        public int price { get; set; }
        public int listNum { get; set; } // 목록 번호 

        public Item[] item; // 아이템 배열

        public Item(string name, bool equip, bool buy, string effect, int effectInfo, string func, int price)
        {
            this.name = name;
            this.equip = equip;
            this.buy = buy;
            this.effect = effect;
            this.effectIfo = effectInfo;
            this.func = func;
            this.price = price;
            this.listNum = 0;
        }

        public Item()
        {
            this.name = "void";
            this.equip = false;
            this.buy = false;
            this.effect = "void";
            this.effectIfo = 0;
            this.func = "void";
            this.price = 0;
            this.listNum = 0;
        }

        // 초기화
        public void AddItem()
        {
            item = new Item[100];
            item[0] = new Item("수련자 갑옷", false, false, "방어력", 5, "수련에 도움을 주는 갑옷입니다.", 1000);
            item[1] = new Item("무쇠 갑옷", false, true, "방어력", 9, "무쇠로 만들어져 튼튼한 갑옷입니다.", 2000);
            item[2] = new Item("전설의 갑옷", false, false, "방어력", 15, "스파르타의 전사들이 사용했다는 전설의 갑옷입니다.", 3500);
            item[3] = new Item("낡은 도끼", false, true, "공격력", 2, "쉽게 볼 수 있는 낡은 검 입니다.", 600);
            item[4] = new Item("청동 도끼", false, false, "공격력", 5, "어디선가 사용됐던거 같은 도끼입니다.", 1500);
            item[5] = new Item("전설의 창", false, true, "공격력", 7, "스파르타의 전사들이 사용했다는 전설의 창입니다.", 2000);
            item[6] = new Item("두꺼운 책", false, false, "공격력", 1, "모서리로 맞으면 꽤 아플거 같은 책입니다.", 500);
        }
    }

○ User 클래스 : 유저 정보 클래스

  • 위 아이템 클래스와 마찬가지로 캡슐화 진행할 예정
public class User : ICharacter, IUser
{
    // ------------------ 캐릭터 인터페이스 공통 ------------------
    public String Name { get; set; } // 이름
    public String Tribe { get; set; } // 종족 : 인간, 고블린 등등
    public int Health { get; set; } // HP
    public bool IsDead { get; set; } // 생존여부
    public int Attack { get; set; } // 공격력

    public void TakeDamage(int damage) // 피해 입었을 때
    {
        Health -= damage;
        Console.WriteLine("플레이어가 {0}의 데미지를 입었습니다.", damage);
    }

    // ------------------ 유저 인터페이스 공통 ------------------
    public string UserClass { get; set; } // 직업
    public int Level { get; set; } // 레벨
    public int DefensivePower { get; set; } // 방어력
    public int Gold { get; set; } // 골드
    public int ClearCount { get; set; } // 던전 클리어 횟수

    public int EquipArmorStatusNum { get; set; } // 장착 갑옷 상태수치
    public int EquipWeaponStatusNum { get; set; } // 장착 갑옷 상태수치


    // ------------------ 전사 고유 ------------------
    //public event AttackHandle OnAttack; // 공격 델리게이트 트리거
    public void WarriorAttack(int Attack) // 유저가 공격할때
    {
        //OnAttack?.Invoke(Attack); // 공격 델리게이트 트리거 - 몬스터의 TakeDamage와 묶임
    }

    // 상태창 메소드
    public void State(User user, Item item)
    {

        bool exit = false;
        while (!exit)
        {
            Console.Clear();

            Console.WriteLine("[상태보기]");
            Console.WriteLine("캐릭터의 정보가 표시됩니다.");
            Console.WriteLine();
            Console.WriteLine("레  벨 : {0} Lv", user.Level);
            Console.Write("직  업 : {0}", user.UserClass);

            if (user.UserClass == "좀도둑")
                Console.WriteLine("       ...왜 하필 이런 직업을... 특이하시군요. ㅍvㅍ");
            else
                Console.Write("\n");

            Console.WriteLine("방어력 : {0} (+ {1})", user.DefensivePower, user.EquipArmorStatusNum);
            Console.WriteLine("공격력 : {0} (+ {1})", user.Attack, user.EquipWeaponStatusNum);
            Console.WriteLine("체  력 : {0}", user.Health);
            Console.WriteLine("골  드 : {0} G", user.Gold);
            Console.WriteLine();
            Console.WriteLine("0. 나가기");
            Console.WriteLine();
            Console.WriteLine("원하시는 행동을 입력해주세요.");
            Console.Write(">> ");

            int select = InputCheck.Check(0, 0);

            switch (select)
            {
                case 0:
                    exit = true;
                    break;
                default:
                    continue;

            }
        }
    }

    public User()
    {
        this.Name = "홍길동";
        this.Tribe = "인간";
        this.UserClass = "전사";
        this.Health = 100;
        this.IsDead = false;
        this.Attack = 10;

        this.Level = 1;
        this.DefensivePower = 10;
        this.Gold = 1500;
        this.ClearCount = 0;

        this.EquipArmorStatusNum = 0; // 장착 갑옷 상태수치
        this.EquipWeaponStatusNum = 0; // 장착 갑옷 상태수치
    }
}

0개의 댓글