본문 바로가기

유니티 스파르타 캠프 2주차

개인 과제 개발 일지 (ver3 -> ver 5까지)

🔴 오늘의 내용    |

  1. 일정과 바뀐 내용  .
  2. 튜터 님의 강의(if - switch)  .
  3. 개인 과제 (ver 3 -> ver 5)  .
  4. 후기 및 내일 일정  .

1) 일정과 바뀐 내용

어제는 코드를 마저 짜면서 Notion에 정리한 내용을 튜터님께 여쭤본다고 했었지만, 실제로 오늘은 코드를 짜는데 급급했습니다. 내용 정리하기에도 정신이 없었기에 오늘 변동사항으로는 질문이 없었다가 되겠습니다.

 

2) 튜터 님의 강의(if - switch)

오늘의 강의 내용은 if문과 switch문입니다. 간단히 요약 정리해보겠습니다.

  • if문의 사용 예제
  • switch문의 사용 예제 (Enum을 활용한)

 

2)-1 if문의 사용 예제

if (_isGameOver)
{
    return;
}

if (string.IsNullOrEmpty(playerName))
{
    Console.WriteLine("잘못된 이름입니다.");
    Thread.Sleep(1000);
    Init(); // 재귀호출은 쓰면 안됨
}
else
{
    _player = new Player(playerName);
    Console.WriteLine($"{_player.name}님, 입장하셨습니다");
}

if문은 간단하게 그 값이 true인지 false인지 확인합니다. _isGameOver의 경우, bool 값으로 참조 받고 있기 때문에 비교 연산자가 없이도 true인가 false인가에 따라 코드가 실행됩니다. 아래에 string.IsNullorEmpty의 경우에는 (playerName)이 Null인지 비교하고 Null이라면 True를 반환하는 연산함수 입니다.

 

2)-2 switch문의 사용 예제 (Enum을 활용한)

public enum Job
{
    Warrior = 1,
    Wizzard,
    Archor
}

class Player
{
    public string name;
    public Job job; //Enum 값 대입

    public Player(string name)
    {
        this.name = name;
    }
}
class GameLogic
{
	private void Init()
    {
        int job = int.Parse(Console.ReadLine()); //값을 받아온다

        if (job >= 1 && job <= 3) // if(job is >= 1 and <3) 패턴매칭 최신 문법
        {
            _player.job = (Job)job; //숫자를 enum값으로 변환
            switch (_player.job)
            {
                case Job.Warrior:
                    Console.WriteLine($"{_player.job}를 선택했습니다");
                    break;
                case Job.Wizzard:
                    Console.WriteLine($"{_player.job}를 선택했습니다");
                    break;
                case Job.Archor:
                    Console.WriteLine($"{_player.job}를 선택했습니다");
                    break;

            }
        }
    }
}

if문과 switch문이 둘 다 쓰인 코드입니다. if문에서 job의 값이 1~3인지 확인하고 문제없으면 switch 코드로 넘어가 그것에 대응하는 코드를 실행시키는 구문입니다. 

 

3. 개인 과제 (ver.3 -> ver.5)

  • Ver3 - branch
  • Ver4 - branch
  • Ver5 - branch
  • 개인 과제 총평

 

3)-1. Ver3 - branch 

구현과제는 다음과 같습니다.

//구현 단계 :
    //장착관리 : O
    //상점 : O
    //아이템 구매 : O

장착관리

public void Equip(Item item) //장비 착용
{
    if (item.isEquip == false) //장비를 착용하지 않았다면, 
    {
        itemStats[item.itemStatType] += item.stat; //해당 능력치를 플레이어에게 부여한다.
        item.isEquip = true;
    }
    else
    {
        itemStats[item.itemStatType] -= item.stat;
        item.isEquip = false;
    }
}

아이템 구매

Console.WriteLine("[아이템 구매]를 선택하셨습니다.");
if(user.haveGold >= shopItem[indexcheck].price) //돈이 있나요?
{
    user.inventoryItem.Add(shopItem[indexcheck]); //돈이 있다면 상점의 아이템을 인벤토리에 추가
    user.haveGold -= shopItem[indexcheck].price; //돈을 차감합니다
    Console.WriteLine($"[{shopItem[indexcheck].itemName}]의 구매가 성공적으로 완료되었습니다.");
    shopItem.RemoveAt(indexcheck); //상점의 아이템은 삭제해줍니다.
}
else
{
    Console.WriteLine("금액이 부족하여 구매를 실패하셨습니다.");
}

상점 가기

public enum PlaceType //VillageSection
{
    Village, //마을 
    Inventory, //인벤토리
    Shop, //상점
    Status, //능력치 확인
    Dungeon, //던전입장
    Rest //휴식하기
}
case 3:
    Console.WriteLine(">> [상점]을 선택하셨습니다.");
    Console.Write("\n");

    place = PlaceType.Shop;

 

코드 설명 요약

1번째 코드는 Bool 값을 활용하여 아이템의 착용 여부를 확인하여 쉽게 코드를 작성할 수 있었습니다. 2번째 코드는 비교적 내용이 길어졌고 오늘 마지막쯤에 깨달음인데 코드를 변수값에 넣어주면 가독성이 훨씬 좋아질 거 같습니다. 3번째 코드는 그냥 상점을 갈 수 있도록 추가하는 코드였기에 enum에 PlaceType에다가 Shop을 추가하여 갈 수 있도록 만들었습니다. 

제작 과정에서 생긴 문제

2번째 코드에서 쓰인 shopItem의 경우, 정해진 크기의 Index 값이기에 배열을 쓸 예정이었으나, 이 코드에서 쓰인 RemoveAt처럼 배열을 지울 수 있는 코드가 없었기에 List를 활용하게 됐습니다. 

Branch에서 생긴 문제

코드 구현에 필요한 클래스나 변수들의 경우, 가져다가 사용했기에 코드를 합치는 과정에서 큰 오류는 없었습니다.  

 

3)-2. Ver4 - branch

구현과제는 다음과 같습니다.

//1. 휴식기능 추가하기 : O
    //500G를 주면 체력을 회복
    //현재 체력과 맥스체력을 비교하기 위해 status 필요
//2. 아이템 판매하기 기능 추가 : O
    //판매하려면 판매하기 누를 시 내 인벤토리 오픈.
    //장착중인 아이템이면 해제 후 판매
//3. 장착 개선 O
    //플레이어에게 Enum으로 Bool값 추가

휴식기능

Console.WriteLine("[휴식하기]를 선택하셨습니다.");
Console.Write("\n");
if(true)
{
    user.curStats[0] = user.stats[0]; 
    user.haveGold -= 500;
    Console.WriteLine("휴식하여 체력을 회복하셨습니다.");

    Console.WriteLine("추가적인 회복이 필요하십니까?");
    Console.Write("\n");
    //선택지
    Console.WriteLine("1. [휴식하기]");
    Console.WriteLine("0. [나가기]");
    Console.Write("\n");

    //플레이어 입력대기
    Console.WriteLine("원하시는 행동을 선택해주세요.");

아이템 판매 & 장착 개선

int check = int.Parse(Console.ReadLine());

if (check > 0 && check < 10)
{
    
    int indexCheck = check - 1;

    if (user.inventoryItem[indexCheck].isEquip == true)
    {
        user.Equip(user.inventoryItem[indexCheck]);
    }
    user.haveGold += (int)(user.inventoryItem[indexCheck].price * 0.85f);

    Console.WriteLine($"[{user.inventoryItem[indexCheck].itemName}]의 판매가 성공적으로 완료되었습니다.");
    user.inventoryItem.RemoveAt(indexCheck);
    break;
}
 public void IsEquip(ItemType itemType) //해당 부위가 장착이 되었는가 코드
 {
     if (equipItem[(int)itemType] != null) //아이템이 해당부위에 착용하고 있다면,
     {
         Equip(equipItem[(int)itemType]); //장비를 해제하고
         equipItem[(int)itemType] = null; //비워라 그 공간을.
     }
 }

 public void Equip(Item item) //장비 착용
 {
     Console.WriteLine("라인 테스트");
     if (item.isEquip == false) //장비를 착용하지 않았다면, 
     {
         item.isEquip = true; //장비를 착용할 수 있다.
         IsEquip(item.itemType); //해당 부위에 장비를 착용했는지 확인, 만약 착용되어있다면 착용을 해제한다.
         int itemStat = 0 + item.stat;

         itemStats[(int)item.itemStatType] += itemStat; //해당 능력치를 플레이어에게 부여한다.\
         StatReload(item.itemStatType, item.stat);

         equipItem[(int)item.itemType] = item;
     }
     else
     {
         int itemStat = 0 - item.stat;

         itemStats[(int)item.itemStatType] += itemStat;
         StatReload(item.itemStatType, itemStat);
         item.isEquip = false;
     }
 }

코드 설명 요약

1번째 코드는 간단하게 돈이 있다면, HP를 MaxHP로 회복시켜주고 돈을 내는 것입니다. 2, 3번째 코드는 구현과제가 밀접한 코드였습니다. Equip 코드를 통해 장비를 착용한 여부를 먼저 확인하고, 다음은 착용할 위치에 장비가 착용되어있는지 확인하는 구문입니다. 

문제

IsEquip과 Equip을 보면 서로 호출하고 있어서 잘못하면 무한반복되는 코드가 완성이 됩니다. 저는 실제로 그런 문제를 겪었고 Stack overflow가 발생했습니다. 이에 Console.WirteLine("라인테스트")라는 구문을 넣어 문제점을 확인했습니다.

현재 내용은 수정된 내용이기에 큰 문제가 없어보이는데 코드의 구조는 Equip에서 실행되어 isEquip을 갔다가 다시 Equip으로 오는 구조입니다. 그러나, IsEquip에서 if(item.isEquip ==false)가 계속 True라면 무한반복이 실행됩니다. 

해결 방법

위 내용을 이어서 말하자면 그렇기에 item.isEquip을 IsEquip을 실행하기 전에 옮겨주어 IsEquip이 해당 장비를 착용하고 있기에 초기화하는 과정에서 False가 반복되는 것을 막아주었습니다. 단, 코드적으로 가독성이 굉장히 떨어지고 구조적으로 좋지 않게 보이기 때문에 내일 추가적으로 작업하는 와중에 수정할 예정입니다. 

Branch에서 생긴 문제

Branch 과정에서 생각지도 못한 큰 문제를 받았는데 그것이 위에 해당하는 문제입니다. 코드의 순서는 다음과 같습니다.
1) 아이템 착용 -> 2) 같은 타입의 아이템 착용 -> 3) 착용한 아이템 판매 -> 4) 판매한 아이템 타입의 아이템 착용

순서를 보면 2)에서 문제가 생겨야 될 거 같지만, 정상적이었고 문제는 4) 에서 발생했습니다. 예측해볼 수 있는 상황으로는 기존에도 오버플로우가 되고 있었지만 어떤 빈틈 때문에 코드가 정상적으로 돌아가는 거처럼 보였고 이후 RemoveAt을 통해 함수가 없어지자 문제가 발생한 것으로 추측됩니다.

 

3)-3. Ver5

//구현 단계
//
//1. 레벨업 기능 추가 : O
    //레벨에 대한 정보는 플레이어에게
    //던전 클리어 시 레벨업 : O
    //레벨업 시 기본 공격력 0.5, 방어력 1 증가 : O
//2. 던전입장 기능 추가 : O PlaceType.Dungeon
    //난이도 추가 : O
    //방어력으로 던전 판별 : O
    //던전 클리어 : O
    //공격력으로 클리어 골드 : O
//3. 채팅창 클리어 Console.Clear

레벨업 기능

private int Level = 1;
private int Exp = 0;

public void DungeonClear()
{
    Exp += 1;
    if(Exp == Level)
    {
        Exp = 0;
        Level++;
        stats[1] += 0.5f;
        stats[2] += 1.0f;
    }
}

던전 클리어

public enum DungeonStage
{
    Easy = 0,
    Normal,
    Hard
}

void DungeonClear(int dungeon)
{
    float DungeonValue = 0;
    int clearGold = 0;

    switch (dungeon)
    {
        case (int)DungeonStage.Easy:
            DungeonValue = 5;
            clearGold = 1000;
            break;
        case (int)DungeonStage.Normal:
            DungeonValue = 11;
            clearGold = 1700;
            break;

        case (int)DungeonStage.Hard:
            DungeonValue = 17;
            clearGold = 2500;
            break;
    }

    float def = user.curStats[(int)StatType.Def];
    float atk = user.curStats[(int)StatType.Atk];
    bool isClear = false;
    if (def >= 5)
    {
        isClear = true;
    }
    else
    {
        Random rand = new Random();
        float fail = rand.Next(0, 100);
        if (fail < 40)
        {
            user.curStats[(int)StatType.Hp] -= 50;
        }
    }
    if (isClear == true)
    {
        Random rand = new Random();
        float damage = rand.Next(20, 35) + 1 - (def - 5);
        user.curStats[(int)StatType.Hp] -= damage;

        int percent = rand.Next(10, 20); // 1, 2
        user.haveGold = clearGold * (int)(atk * percent * 0.1f);
    }
}

코드 설명 요약

1번째 코드는 던전 클리어 시 경험치가 쌓이고 경험치가 레벨과 같아지면, 레벨을 올리고 경험치를 초기화하는 간단한 코드입니다. 2번째 코드는 들어간 던전에 따라 보상과 난이도를 정하고 그에 따른 보상을 받는 코드입니다. 

문제

코드가 짧은만큼 큰 어려움은 없었습니다.

Branch에서 생긴 문제

아직 Branch 전 단계이지만 코드 접합에서 문제가 있을 거 같지는 않습니다.

 

3)-4. 개인과제 총평

오늘 튜터님 강의에서 enum을 잘 활용하는 예제를 보여주셔서 이번 개인 과제 기능구현이 끝나면 그것으로 다시금 이쁘게 코드를 정리할 예정입니다. 현재 기능적으로는 다 구현이 가능하도록 작성을 하였지만, 코드가 너무 난잡하고 실행의 순서를 알기 어려울 정도로 가독성이 좋지 않은 코드이기에 가독성만 고친다면 기능은 완벽하니 좋은 코드가 될 수 있을 거 같습니다. 또한 알게된 점으로는 코드를 짜기전에 주석처리를 하여 간단한 구상을 하였는데 어떤 것을 제작하기 전에 구상을 하고 순서를 짜는 것은 굉장히 코드적으로 접근하기에 좋은 방식인 거 같습니다. 

 

4. 후기 및 내일 일정

오늘은 어제 정리한 아직 이해 못한 내용 및 이해했던 내용도 주석에 싹 정리하면서 확인했습니다. TIL에 여태 큰 의미를 두지 않았었지만 이번에 혼자 TIL 작성한 내용을 보며 생각해보는 시간을 가지니 상당히 가치있는 학습이 되었습니다. 그것을 하나씩 확인하며 개인 과제를 순차적으로 진행할 수 있었으며, Ver.6까지 할 것을 저장해놓았습니다.

다음은 Ver.6에 예정되어 있는 현재 작성해놓은 코드를 깔끔하고 보기 좋게 나누고 정리하고 하는 시간이 될 거 같습니다. 가장 오래 걸릴 작업인만큼 내일 안에 못할 수도 있다는 가정은 두고 있지만, 다 할 수 있다면 다 정리한 이후 끝까지 활용해보지 못한 코드에 대해서 튜터님께 질문해보는 시간을 가져봅시다.

//ver 1 과 ver 2에서 구현한 내용
//Player class
//변수 : 이름, 능력치, 

//인벤토리로 변경 가능한 데이터
//변수 : 장착한 아이템 능력치, 인벤토리 아이템
//함수 : 아이템 장착, 아이템 추가
//위 내용을 Inventory 클래스로 선언하여 Delegate를 활용해서 옮겨보자.

//Item Class
//변수 : 아이템 이름, 아이템 스텟 종류, 아이템 스텟 증가치, 아이템 설명, 아이템을 장착했는지?
//함수 : 아이템 값형으로 재생성, 아이템 정의

//Enum
//스탯(능력치) 종류 : 레벨, 이름, 직업, 공격력, 방어력, 체력, 골드
//환경(장소) 종류 : 마을, 인벤토리, 상점, 능력치 확인

//Main 
//변수 : 플레이어, 장소, 아이템의 총 종류

//Main 함수
//GameManager : place에 따른 불러오는 값 변화
//LineTap : 줄 내리기 간편 함수

//Intro() : 시작 설명
//NameSelect() : 이름 입력값 받기

//모든 장소는 행동에 따라 return 값으로 place를 받음.
//Village : 마을과 마을 내에서 가능한 행동 
//Inventory : 플레이어의 인벤토리창 확인
//Shop : 상점 내 시스템
//Status : 플레이어가 능력치창을 볼 수 있음.



//구현해야하는 필수 기능 : 
//게임시작 화면 : O
//상태보기 : O
//인벤토리 : O
//장착관리 : X
//상점 : X
//아이템 구매 : X

//구현해야하는 도전 기능 :
//아이템 정보를 클래스/구조체 활용 : O 
//아이템 정보 배열로 관리하기 : O
//아이템 추가 - 나만의 새로운 아이템 : O
//휴식기능 추가하기 : X
    //500G를 주면 체력을 회복
//아이템 판매하기 기능 추가 : X
//장착 개선
    //타입별로 아이템 하나만 착용 가능 : X
    //Player Class에 Bool 추가하면 쉬울듯
//레벨업 기능 추가 : X
    //던전 클리어 시 레벨업
    //레벨업 시 기본 공격력 0.5, 방어력 1 증가
//던전입장 기능 추가 : X
    //난이도 추가 : X
    //방어력으로 던전 판별 : X
    //던전 클리어 : X
    //공격력으로 클리어 골드 : X
//게임 저장하기 기능 : X

//배운 것 중 주요사용 기능 :
    //1주차 :
        //코드 컨벤션 : O
        //포멧팅 : O
    //2주차 :
        //배열을 이용한 맵 구현 : X
        //컬렉션(배열)의 종류와 구조 : List 활용
            //HashSet으로 상점 구현해보면 재밌을 듯.
        //오버로딩 : O / Item 정의 할때
    //3주차        
        //생성자 : O / 소멸자 : X
            //소멸자를 쓸 일이 거의 없다
            //던전 만들면서 쓸 수도?
        //프로퍼티 : O / 접근 제한 설정을 했다.
        //다형성 : X
            //가상 메소드, 추상 메소드
            //virtual, override | abstract
        //제너릭 : X
            //동일한 조건을 가지는 다른 타입의 변수를 관리하는 거 같은데
            //player와 item의 조건은 많이 다르다.
        //out, ref : X
            //void 함수를 쓰기전에는 ref 값을 활용하려 해봤으나,
            //Enum 값이 제대로 작동하지 않아 그만두었다.
            //반드시 바뀌어야한다는 점은 좋게 작용할 수도 있지만,
            //검증 단계라면 모를까 꼭 필요한 코드는 아니라는 느낌.
    //4주차
        //인터페이스 : X
            //제너릭과 마찬가지로 똑같이 쓸 함수가 없음.
        //Enum : O
        //예외 처리  : X
            //쓸 예정 : 값을 받을 때, 숫자가 아닐 경우 버그가 나지 않고 다시 입력하도록.
        //값형과 참조형 : new 키워드를 활용하여 해결하는 방법을 써봄
        //박싱/ 언박싱 : X
            //new 키워드를 쓰지 않았다면 object를 써볼만도 했는데..
        //델리게이트 : X
            //메서드(함수) 내에 값을 전달하는 매개체
            //메서드 내에 데이터를 저장하고 필요시 호출하는 방식으로 사용.
            //선언된 클래스를 활용해야하기에 Main에서만 활용 가능.
            //Main - void에서 플레이어의 능력치를 기반으로
            //아이템을 가져온다는 코드를 써볼 수 있을 것으로 보임.
        //Func, Action : X
            //Main 내에서 선언하여 만들 수 있는 델리게이트로
            //메서드를 저장한다.
            //Func는 맨 뒤에 값을 반환하고
            //Action은 반환 값 없이 활용한다. 
        //람다식, LinQ : X
            //람다 아직도 모르겠다. 질문 내용 : 1 
            // LINQ 굳이 안 써도 될거 같다. 
                //for문과 if문으로 해소 가능하다.
        //Nullable : X
            //데이터 값이 빈것을 확인할 때 사용하는 구문인데, List를 사용하기 때문에 굳이 없어도 된다.
        //문자열 빌더 : X
            //콘솔이 더러워지는 것을 방지하기 위해 이번에 사용해볼 예정이다.


    //우선순위 :
        //1.기본 필수과제 
        //구현에 필요한 Player 클래스와 변수는 가져온다. 
        //또한 Player 클래스가 Item 클래스를 활용하고 있기에 가져온다.
            //장착관리 
                //inventory에서 1을 선택 시,
            //상점 
            //아이템 구매 
        //2.문자열 빌더로 콘솔창 깨끗하게 //로 할려했었으나, 튜터님 강의에서 쓰인 내용으로 수정할 예정
        //3.배열을 활용하여 콘솔창 꾸미기 //이 또한 수정 시에 내용이 많이 바뀔 거 같아 나중으로 미룸.

    //구현 단계 :
        //장착관리 : O
        //상점 : O
        //아이템 구매 : O