[TIL] C# - 클래스 형변환 , 퀘스트 구현

MINO·2024년 5월 2일
0
post-thumbnail

2024-05-02


Quest 클래스

퀘스트를 튜토리얼과 미션으로 나눠서 구현하기 위해 Quest 부모 클래스를 상속 받은 Tutorial 자식 클래스와 Mission 자식 클래스를 생성해주었다.

//GameManager.cs

static public List<Quest> quests = new List<Quest>();

로 선언해주었고, 게임 초기화 단계에서 퀘스트 타입에 따라

quests.Add(new Tutorial( ... ));
quests.Add(new Mission( ... ));

로 인스턴스를 추가하여 사용할 수 있었다.


문제점 1 : 클래스의 형변환

퀘스트를 다루는 Guild 스크립트에서 List<Quest> 인 quests 를 순회하면서 정보를 추출하는 for 문을 사용하였다.

Quest 클래스에서 선언한 멤버 변수의 경우는 Tutorial 인스턴스와 Mission 인스턴스에 관계 없이 잘 출력되었다.

Mission 클래스에 따로 선언한 변수가 있었는데, 여기서 문제가 발생했다.

class Mission : Quest
{
	int id; // 몬스터의 id 를 받아올 변수
    int cnt; // 처치해야하는 해당 몬스터 수
    int cur; // 현재 처치한 해당 몬스터 수
    
    ...
}

foreach 문을 순회할 때, List<Quest> 에서 Quest 인스턴스를 하나씩 꺼내오는데, 자식 클래스인 Mission 의 멤버 변수에 접근할 수 없다는 오류가 발생하였다.

형변환을 위해 여기저기 캐스팅을 해보기도 하고,
List<Quest> 가 아닌 각자의 List 를 생성해야 하는지 고민도 해봤지만,
코드의 가독성이 떨어지고 비효율적인 방법인 것 같아 튜터님을 찾아갔다.


해결 1 : 클래스의 형변환

예상한대로, 클래스의 형변환으로 해결할 수 있는 문제였다.

클래스의 형변환은 많이 접해보지 못하여 ( ) 괄호의 범위를 잘못 지정해줘서
원하는 형변환이 일어나지 않았던 것이다.

foreach (Quest q in quests)
{
    int cur;
	...
    
    cur = ((Mission)q).cur; // 클래스의 적절한 형변환
    
    // 시도해봤지만 실패한 형변환 케이스
    // cur = (Mission)q.cur;
    // cur = (Mission)(q).cur;
    // cur = (Mission)(q.cur);
}
   

적절한 위치의 괄호를 통해 클래스의 형변환을 할 수 있었다는 것을 배울 수 있었다.

자식 클래스의 인스턴스 = ((자식 클래스)부모 클래스의 인스턴스);

문제점 2 : 저장 및 불러오기

이렇게 List<Quest> 에 Tutorial 과 Mission 타입의 Quest 를 삽입하여, 퀘스트 기능을 구현하여 정상적으로 퀘스트를 진행할 수 있었다.

게임을 저장하고 다시 불러왔을 때,
현재까지 진행한 퀘스트와 완료한 퀘스트의 정보를 기억해야하기 때문에,
기존의 SaveData( ) 와 LoadData( ) 함수를 수정할 필요가 있었다.

playerJson, ItemJson 과 유사한 방법으로 구현하는 데에 어려움은 없었지만,
불러온 questJson 을 List<Quest> 에 삽입하고 foreach 문에서 불러오는 과정에서 오류가 발생했다.


원인 : 저장 및 불러오기

처음 게임이 시작될 때(세이브가 없을 때)는 List<Quest> quests 를 선언하고,
게임 초기화 단계에서

quests.Add(new Tutorial( ... ));
quests.Add(new Mission( ... ));

다음과 같이 삽입해주었지만,

세이브 파일을 불러오는 과정에서는, Tutorial 인스턴스인지, Mission 인스턴스인지 확인하지 않은 채로, List<Quest> quests 에 삽입되었다.

quests.Add(new Tutorial( ... )); // 이렇게 삽입한 Tutorial 의 인스턴스가 Quest 인스턴스로 quests 에 삽입
quests.Add(new Mission( ... ));// 이렇게 삽입한 Mission 의 인스턴스가 Quest 인스턴스로 quests 에 삽입

그러다보니, 세이브 파일에서 불러온 List<Quest> quests 에는 Mission 인스턴스가 존재하지 않았던 것이다.


해결 2 : 상속 관계 포기

Mission 클래스와 Tutorial 클래스를 상속으로 구현한 이유는

  • 공통적인 멤버 변수 (퀘스트의 이름, 정보, 보상 골드, 수락 여부, 성공 여부 등)
  • 공통적인 메서드 (퀘스트 성공, 퀘스트 상세 보기 등)
  • Mission 과 Tutorial 퀘스트의 다른 메서드

이었지만, 멤버 변수에 type 이라는 변수를 하나 추가하여 Mission 과 Tutorial 을 구분하고,
각각의 Print 메서드를 만들어줘서 해결할 수 있었다.


결과물

아직 UI 는 작업하지 않고 기능만 작업해둬서 초라해보이지만,
기능은 정상적으로 작동한다.

  • 퀘스트 선택 시, 자세한 정보를 보여주며 수락과 거절을 선택하기
  • 튜토리얼과 임무 를 구분해서 출력하기
  • 적 처치(임무) 의 경우, 처치해야 하는 적의 수와 현재 처치한 수를 표시

TIL 마무리

오후에 C# 배치고사와 Interface, 추상 클래스 체크리스트를 진행하느라
프로젝트에 시간을 많이 쏟지 못하였다.

프로젝트에 기능을 하나 추가할 때마다, 무수히 생겨나는 분기와 예외로 인해
조금 힘들긴 하지만 해결했을 때의 쾌감과 하나씩 배워가는 재미가 있는 것 같다.

어찌저찌 퀘스트 기능을 구현하긴 했지만, 상당히 야매로 구현한 것 같아 아쉽다.
프로젝트 발표회를 통해 다른 사람들은 퀘스트 기능을 어떻게 구현했는지, 더 효율적인지 비교해보고 더 배워나가고 싶다.

profile
안녕하세요 게임 개발하는 MINO 입니다.

0개의 댓글