-
사용해본 패턴 중 기억나는것 하나를 설명해주세요.
-
제네릭에 대해 설명해주세요.
-
느슨한 결합이 가지는 결과에 대해 설명해 주실 수 있으신가요??
-
Adressable 시스템을 사용한 이유가 무엇인가요?? 비동기에 대해서도 설명해주세요.
-
진행한 최적화 방법에 대해 설명해주세요.
-
게임이 복합 장르인데다 덱 빌딩요소까지 섞여있다보니 많은 버그가 필연적으로 발생했을 것 같은데, 어떤 버그가 가장 기억에 남고 어떻게 해결 했는지 말씀해주세요.
사용해본 패턴 중 기억나는것 하나를 설명해주세요. : 전략 패턴
저희 게임은 특성상 다양한 종류의 카드를 추가 하기 때문에 전략 패턴을 사용했습니다. 이를 효율적으로 관리하기 위해, 특정 데이터를 수정하는 다양한 효과들을 ScriptableObject로 미리 정의해 두고 카드를 만들때 이 효과들을 조합하여 만들었습니다.
예를 들어, 카드A를 만들 때 몬스터의 스트레스를 조절하는 효과를 주고 싶다면, 해당 전략을 카드 데이터의 ScriptableObject에 추가하면 됩니다.그런데 나중에 카드 A의 기능이 변경되어 스트레스와 배고픔 모두 조절해야한다면 배고픔 관련 전략을 추가해주기만 하면 됩니다.
이런 방식으로 전략 패턴을 사용함으로써, 게임의 변경과 확장에 매우 유연하게 대응할 수 있었습니다.
제네릭에 대해 설명해주세요.
-
C# 프로그래밍에서 데이터 타입의 유연성을 향상시키는 방법입니다. 이를 통해 개발자는 타입 안전성을 유지하면서도 다양한 데이터 타입에 대해 동일한 코드를 재사용할 수 있게 됩니다.
ObjectManager에서 Object를 꺼내오거나 다시 Pool에넣을때 의 메서드들을 제네릭 메서드로 만들었습니다.
그래서 여러 다른 GameObject를 풀에서 꺼낼때 GameObject가 아닌 T타입으로 컴포넌트를 가져와 오브젝트를 사용하는 쪽에서 GetComponent를 따로 해줘야 할 필요없이 사용할 수 있었고
T타입을 반환하지 못하는 경우 컴파일 시점에서 알 수 있기 때문에 타입 안정성을 향상 시킬 수 있었습니다.
느슨한 결합이 가지는 결과에 대해 설명해 주실 수 있으신가요??
느슨한 결합을 통해서 각 컴포넌트의 독립성이 증가하고 변경사항이 다른 컴포넌트에 미치는 영향이 줄어들어 유지보수, 확장성, 재사용 측면에서 이점을 얻었습니다. MVC 패턴을 사용한 느슨한 결합으로 각각의 controller는 model,view를 참조하지만 model과 view는 각자의 역할만을 수행합니다 구조를 개선하고나서 책임을 가진 controller, view를 수정하는것으로 버그를 해결할 가능성이 높아져 변경, 유지 관리에서 이점이 생겼고 비슷한 기능을 구현할 때 controller를 재사용할 수 있는 가능성이 높아져 효율성이 증가했습니다.
결합이 강하면?
처음에 MVC 패턴을 적용하지 않았을때는 강한 결합으로 인해서 버그를 찾는데 어려움을 겪었습니다.
예를 들어 MVC 패턴을 적용하지 않았을 때는 Cage 오브젝트는 Cage에 데이터에 대한 정보와 데이터를 수정하는 Cage 클래스와 CageInfo, MonsterInfo, CageCard, CageItem 등의 각각의 UI들을 변경하는 컴포넌트를 가지고 있었습니다
그리고 예를 들어 Cage에 아이템을 추가해 온도 정보가 변한다면 CageItem -> Cage -> CageInfo의 순서로 접근을 하게 됬고 그러기 위해서는 Cage 클래스와 각각의 기능 클래스들이 서로 참조하도록 강하게 결합되어 있었습니다.
그렇기 때문에 버그가 발생했을때 기능을 수정하면 연결된 기능들을 전부 확인해야 되고 결국은 수정과 테스트를 여러번 거쳐서 새로운 버그가 발생하지 않도록 작업을 진행해야 했습니다.
그런 과정을 통해 결합도가 강하여 변경이 어렵고, 유지 관리가 어렵다라는 생각이 들었고 MVC 패턴을 적용하며 느슨한 결합을 고려하게 되었습니다.
Adressable 시스템을 사용한 이유가 무엇인가요?? 비동기에 대해서도 설명해주세요
Adressable 시스템을 사용해보게 된 이유에는 편의성, 비동기로딩,로드 상태 추적, 메모리 관리, 원격 관리등에서의 이점에서 있습니다.
비동기 로딩 측면에서 Addressable은 비동기 로딩이 권장되는데 Addressable의 비동기 로딩 기능은 게임의 메인스레드를 방해하지 않고 백그라운드로 리소스를 로드해 게임이 끊김 없이 진행되도록 할 수 있다는 장점이 있습니다.
=> 캐치씬에서 몬스터들을 생성할떄 미리 몬스터들을 오브젝트 풀에 생성 해둔후 방으로 나누어진 맵에서 어떤 방에 들어가면 풀링을 통해서 몬스터가 생성되도록 했습니다. ================================================= 또 Addressable은 Handle을 통해서 리소스가 로드 작업을 추적하고 상태를 파악할 수있습니다.
이를 통해서 중복 로드를 방지할 수 있습니다.
또 프로젝트에서는 활용하지는 못했지만
메모리 관리 측면에서 Resources는 언로드 방법이 사용하지 않고 있는 모든 리소스를 언로드 하는 방법밖에 없지만 Addressable은 각자의 리소스에 대해서 언로드를 제어할 수 있다는 장점이 있고
리소스를 원격서버로 관리할 수 있고 그렇기 때문에 ScriptableObject의 단점인 빌드를 해야한다는 점을 해소할 수 있다는 장점도 있습니다.
Addressable을 어떤 식으로 사용하고 있는지 설명해주세요.
Addressable Groups에 Addressable로 사용하기 위한 리소스들을 등록해주고
id관리하는 리소스들의 주소를 호출하기 쉽도록 Type_ID 로 맞춰주었습니다.
그리고 ResourceManager에서 AssetReference나 string key (주소)를 통해서 리소스의 로드를 호출하면 Dictionary<string, AsyncOperationHandle> 을 가지는 handleDic에
해당 리소스의 핸들이 있는지 확인하고 리소스를 비동기로 로드하거나 이미 로드된 리소스를 반환하도록 해서 로드 중이거나 로드 완료된 리소스를 중복 로드하지 않도록 하고 있습니다.
진행한 최적화 방법에 대해 설명해주세요.
캐치씬과 메인씬을 반복해서 이동하게 되는 프로젝트의 특성상 오브젝트의 파괴와 생성이 불필요하게 많이 일어날 것이라고 생각했습니다.
그래서 오브젝트 풀링을 사용하여 오브젝트를 재사용하여 오브젝트의 생성과 파괴를 줄였습니다.
게임이 복합 장르인데다 덱 빌딩요소까지 섞여있다보니 많은 버그가 필연적으로 발생했을 것 같은데, 어떤 버그가 가장 기억에 남고 어떻게 해결 했는지 말씀해주세요.
-
Cage측에서 여러가지 요소들을 엮어서 관리해야 했고 그 과정에서 어려움들이 있었습니다. 1. 분리 되지 않은 데이터, 2. 코드가 복잡해서 생긴 문제
-
[분리 되지 않은 데이터 예시] 게임 개발 중 각 기능을 개발하고 통합하면서, Cage가 Item, Card, Monster 등을 직접 참조하게 되었습니다.이로 인해 데이터 직렬화 과정에서 문제가 발생했으며, 일부 데이터가 직렬화 불가능한 상태가 되었습니다.
-
[코드가 복잡해서 문제해결이 어려워진 예시] 예를 들면, 데이터 로드 후 버튼 작동 문제(버그)가 발생했습니다.데이터를 로드하는 부분의 코드를 수정해도 버그를 해결할 수 없었습니다. 이유는 코드의 복잡성과 강한 결합도 때문에 문제 해결이 어려웠습니다. => GameObject, 이미지 파일등의 리소스는 직렬화할수 없다? 어렵다?
=> 게임을 플레이중 데이터를 로드했더니 버튼들이 제대로 동작하지 않는 버그가 발생했습니다. 세이브& 로드 작업을 하고 있던 도중이여서 로드부분이 잘못됬다고 생각하고 의심되는 부분들을 수정했지만 버그를 해결할 수 없었습니다. 그러던 중에 DOTween을 사용하는 버튼들만이 동작하지 않는다는 것을 깨닫고 TimeScale 부분을 확인했습니다. 일시정지 버튼을 눌러 TimeScale이 0이 된 상태에서 데이터를 로드해 게임을 다시 시작하니 그대로 TimeScale이 0인 상태라서 DOTween을 사용하는 버튼들이 동작하지 않는 것이였고 버그를 찾아 해결할 수 있었습니다. 이떄 버그를 찾기 힘들었던 이유가 코드의 복잡성과 강한 결합도 때문이라고 생각했고 이후에도 생기는 자잘한 버그들을 해결하기 위해서 느슨한 결합을 생각하게 되었습니다.
- [해결] 케이지 구조를 MVC 패턴을 고려해 개선했고, 세이브를 위한 모델 부분에서 직렬화가 불가능한 데이터를 ID 형식으로 저장했습니다. 컨트롤러에서 데이터를 로드하고, 데이터를 모델에 할당, 뷰에 반영했습니다. 결과적으로 느슨한 결합을 통해 세이브 및 로드 로직의 수정 어려움을 해결하고, 버그를 해결하기 쉬운 구조로 변경할 수 있었습니다.