오늘 배운 내용
-
로드 이후 객체 초기화? 문제
-
로드 이후 스킬 사용 문제
로드 이후 객체 초기화? 문제
제일 먼저 생긴 문제는 로드를 한 이후에 불러온 데이터들이 제대로 출력되지 않는 문제였다.
문제가 생긴 이유는 원래 GamaManager측에서 player를 먼저 생성하고 Scene들을 생성해 SceneManager에 넣어주면서
Scene 클래스들의 생성자에서 GameManager.Instance.player를 받아와서 게임 진행 도중에 player에 접근할 일이 있으면
Scene클래스가 가진 _player를 통해 접근하게 구현을 했었다.
그런데 게임 도중 Json을 로드해서 GameManager.Instance.player에 할당하게되면
Scene클래스가 가진 player와 정보를 불러온 GameManager.Instance.player는 서로 다른 객체가 되는 문제였다.
그래서 정상적으로 불러온 데이터들을 적용시키기 위해서는 로드 이후에 player를 받아와야했다.
그래서 Scene클래스의 _player에 진짜 player를 할당하기 위해서 Scene들의 장면을 전환하는 DrawScene() 메서드를 호출할 떄 마다
_player = GameManager.Instance.player; 를 해주는 것으로 계속해서 player를 갱신해주었고 문제를 해결할 수 있었다.
그런데 이제와서 생각해보니까 로드를 하는 순간만 문제가 됬기 때문에 그 순간에 모든 객체들을 Scene으로 할당을 해주면
더 쉽게 해결할 수 있지 않았나 싶다…
public virtual void SceneInit()
{
_player = DungeonGame.Instance.player;
Console.Clear();
DrawUI();
}
팀 과제 스킬 기능 수정
스킬 기능은 부모클래스 Skill을 상속받는 각각의 스킬 클래스를 정의하고
이 클래스들을 Player를 상속받은 Player의 직업들마다 객체를 생성할때 각자의 스킬을 SKill배열에 추가 해주었다.
각 Skill의 기능은 부모클래스의 가상 메서드 Use()를 override해서 각자의 Use()메서드로 구현을 해두었다.
그런데 문제가 생긴 점은 나는 skill배열을 통해 Skill의 형태로 모든 스킬들을 저장하고 있었고,
이걸 JSON직렬화를 통해서 저장하고 불러오기를 하고나니 SKill의 자식 객체들이 자신이 누군지를 잊었다?
Skill배열의 객체들은 불러오기 이후 모두 부모인 SKill클래스의 가상메서드 Use()를 호출하기 시작했고
너무나도 당연하게 아무런 기능도 작동하질 않았다…
그래서 머리를 굴리다가 생각한게 어? 그러면 그냥 Player가 Use()메서드들을 델리게이트로 들면 안되나??
그렇게 두 번째 삽질이 시작되었다.
플레이어에서 Skill의 객체를 저장할 배열과 메서드를 관리할 델리게이트 배열을 둘다 선언해주었다.
그리고 동작 가능하게 다른 부분을 수정하고 실행한 결과…
오류가 발생했다. chat gpt에게 오류를 보여주니 순환참조, 무한루프 뭐 이런 비슷한 말들을 했고
결국엔 뭐 델리게이트를 그냥 아무런 조치없이 JSON으로 만들수는 없었나 보다..
그래서 다음으로 생각한건 리플렉션 다시 리플렉션 기능이였다.
왜 다시냐면 사실 제일 처음으로 생각한 방법이였는데 각 SKill 객체에 클래스 이름을 string 으로 저장한뒤
Skill클래스의 Use를 호출하게 되면 그 클래스 이름을 이용해서 객체를 캐스팅을 하면 되지않을까? 라는 생각으로 처음에 시도를 했었는데
리플렉션으로 생성한 객체는 Object형태였고 이걸 동적인 타입으로 변환할 방법이 내가 생각하기로는 없었다.
그래서 포기하고 다른 방법을 생각했던 건데 이번에는 두번쨰 방법과 엮어서
Use()만 실행시킬수 있으면 되는거 아닌가? 그러면 메서드를 빼오면 되지! 라는 생각이 들어서
한번 더 도전했다. 빼온 메서드를 어떻게 인자를 줘서 실행시키지? 등의 문제가 있었지만
chat gpt와 함께 해결할 수 있었고, 뭐 결론적으로는 성공했다. 이제는 불러오기 이후에도 스킬이 제대로 작동한다!!
그런데 다 만들고 보니 괜히 쓸데없이 복잡해진 Player, skill 클래스가 눈에보였고,
그때 든 생각은 그냥…. 따로 스킬 리스트를 관리할 객체를 만들고 플레이어에서는 스킬의 이름만 가지고 있다가
호출하면 되는거 아닌가?? 였지만 뭐 그래도 이것저것 기능들을 사용하려고 해봐서 재밌었다고 생각한다.
[Serializable]
public class Skill
{
public string name;
public string skillInfo;
public int mp;
public int targetCount;
public float skillRate;
public SkillType type;
public string className;
public Skill(string name, string skillInfo, float _skillRate, int mp, int targetCount, SkillType type, string className)
{
this.name = name;
this.skillInfo = skillInfo;
skillRate = _skillRate;
this.mp = mp;
this.targetCount = targetCount;
this.type = type;
this.className = className;
}
// 스킬 마다 클래스로 생성
// 플레이어의 Skill Ation에 등록해서 사용한다.
// Skill은 스킬마다 대상 몬스터가 달라지니까 Unit의 List를 받는다.
public void Use(List<Unit> _units, int _attack, Skill skill)
{
Type type = Type.GetType($"TextBased_Dungeon_Game.{skill.className}");
object instance = Activator.CreateInstance(type);
MethodInfo method = type.GetMethod("Use");
object[] parameters = new object[] { _units, _attack, skill };
method.Invoke(instance, parameters);
}
}