소개
안녕하세요, 게임을 사랑하고 개발을 사랑하는 게임 프로그래머 Developer G입니다.
저는 어떻게하면 깔끔하고 체계적인 코드를 작성할 수 있을지 항상 고민하는데요,
제 고민의 결과물들을 여러분들에게 아낌없이 가르쳐드리겠습니다!
강의
수강평
- [유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
- [유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
- [유니티 레벨 업!] 모듈식으로 개발하는 퀘스트&업적 시스템
게시글
질문&답변
아이템 및 시너지 효과 스킬 적용
수강해주셔서 감사힙니다.적을 처형하는 효과의 경우, onDealDamage처럼 Skill의 Owner가 자기가 공격한 대상을 알려주는 event를 만든 뒤, Effect의 Start 함수 같은 곳에서 해당 event를 구독하여 Owner가 때린 대상의 체력을 확인해서 처형하면 됩니다. 예를 들면 다음과 같습니다.EffectAction.cs public override Start(Effect effect) { effect.Owner.Owner.onDealDamage += OnDealDamage; } public override bool Apply(Effect effect) { // event를 통해 효과를 적용했다면, 상태를 리셋하고 true를 return if (isApplied) { isApplied = false; return true; } return isApplied; } public override Release(Effect effect) { effect.Owner.Owner.onDealDamage -= OnDealDamage; } public void OnDealDamage(Entity target, float damage); { // 대상 체력이 현재 체력의 10퍼 이하라면 처형 if (target.Stats.GetValue(hpStat) 위는 예시이기에, 원하시는 기능을 정확히 구현하시려면 Effect의 동작을 좀 더 확장하실 필요가 있을 수도 있습니다.주의하실 점은 이 경우 스킬, 아이템, Entity가 움직인 오브젝트 등 Entity가 소유한 객체들이 준 Damage도 모두 Entity의 onDealDamage event를 호출할 수 있도록 구조를 만들어주셔야 합니다.예를 들어, 지금처럼 target의 TakeDamage 함수를 개별적으로 직접 호출하는게 아니라Owner를 통해서 Damage를 주게 만들어야 합니다.// param1: causer(여기선 Effect), param2: target, param3: damageOwner.DealDamage(this, target, 9999f);TakeDamage 대신 위와 같은 함수를 호출하고, DealDamage 함수 내부에서{// ...중략, 공격 강화 스킬 대한 처리와 데미지 공식을 통해 데미지 확정.// TakeDamage가 방어 공식을 통해 확정한(=실제로 받은) Damage를 onDealDamage 함수에 넘겨줌float appliedDamage = target.TakeDamage(this, causer, finalDamage);onDealDamage?.Invoke(target, appliedDamage);}이런 식으로 처리해줄 필요가 있습니다.감사합니다.
- 0
- 1
- 14
질문&답변
퀘스트 클리어해도 동일 퀘스트가 노출됩니다
안녕하세요, 보내주신 프로젝트를 확인하였습니다.QuestTracker Script의 UpdateTaskDescriptors 함수를 보시면(사진)Instantiate로 생성한 Clone의 UpdateText 함수를 실행하는게 아니라 Prefab의 UpdateText 함수를 실행하고 있습니다. 그래서 생성된 taskDescriptors의 Text가 Update되지 않아 생긴 문제구요,(사진)다음과 같이 Clone의 UpdateText 함수를 실행해주시면 정상적으로 Text가 출력이 됩니다.감사합니다.
- 1
- 3
- 28
질문&답변
퀘스트 클리어해도 동일 퀘스트가 노출됩니다
수강해주셔서 감사합니다.Quest가 정상적으로 진행이 된다면 UI 문제로 보이며, 정보가 부족해서 판단을 내리기 어려운 점 죄송합니다.동일 Text가 반복 노출된다는게 첫번째 Task의 Text가 쭉 나온다는 얘기이실까요?(모든 Task의 Text가 첫번째 Task의 Text로 출력됨),아니면 다음 Task에서 이전 Task의 Text가 노출된다는 얘기이실까요?(2번 Task에서 1번 Task의 Text가 출력, 3번 Task에서 2번 Task의 Text가 출력) (사진)지금 의심할 수 있는 부분은 QuestTracker Script의 UpdateTaskDescriptors 함수구요, 첫번째 foreach문의 내용이 정확히 일치하신다면 muramasa666@naver.com로 프로젝트를 압축해서 보내주시면 직접 확인해드리겠습니다.감사합니다.
- 1
- 3
- 28
질문&답변
퀘스트 누적 클리어 횟수에 대해 여쭤보고싶습니다.
수강해주셔서 감사합니다.Quest에 completedCount 변수를 만드시고, 저장 Data를 만드는 ToSaveData 함수에서 QuestSaveData를 만들 때 함께 기록해줍니다.return new QuestSaveData { codeName = codeName, state = State, taskGroupIndex = currentTaskGroupIndex, taskSuccessCounts = CurrentTaskGroup.Tasks.Select(x => x.CurrentSuccess).ToArray(), completedCount = completedCount; };저장 Data를 불러오는 LoadFrom 함수에서는 저장했던 completedCount를 가져옵니다.completedCount = saveData.completedCount;Quest 완료 시 QuestSystem의 completedQuests 목록에 같은 ID의 Quest가 존재한다면 그 Quest의 completedCount를 1 증가시켜주고, 완료한 Quest는 보관하지 않고 Destroy로 부숩니다.이러면 QuestSystem이 completedQuests를 저장할 때 Quest들의 completedCount가 같이 저장되고(ToSaveData 함수를 사용해 저장함으로), 불러올 때 completedCount도 같이 불러와집니다.(LoadFrom 함수를 사용해 정보를 불러옴으로)또한 강의의 QuestSystem은 기본적으로 완료한 Quest는 무조건 completedQuests 목록에 보관하므로 따로 수정하지 않으셨다면 completedQuests에 특정 ID를 가진 Quest가 몇 개 있는지 확인하여 누적 클리어 횟수를 확인할 수 있습니다.감사합니다.
- 0
- 1
- 21
질문&답변
프로퍼티 사용 질문입니다.
수강해주셔서 감사합니다.어떤 Property를 얘기하시는건지 모른채로 답변드리기가 애매해서 혹시 어떤 Property를 보고 그런 생각을 하셨을까요?
- 0
- 1
- 33
질문&답변
Task 에서 Object 타입 사용
수강해주셔서 감사합니다.같은 질문이 있었던지라 답변을 똑같이 드리겠습니다.(https://www.inflearn.com/community/questions/450293/tasktarget%EC%9D%84-%ED%95%A0%EB%96%84)C# 문법서나 강의에서 제대로 설명하지 않아서 가장 많은 오해를 많이 받는게 object형의 퍼포먼스인데요, object형이 퍼포먼스에 안좋다는 이유는 Value 타입의 변수를 박싱할 때 heap에 값의 재할당이 일어나고 언박싱을 할 때 캐스팅한 타입으로 박싱한 것인지 확인 후 다시 value 타입으로 값의 재할당이 일어나기 때문입니다.(마이크로스프트 공식 문서 https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/types/boxing-and-unboxing)하지만 애초에 heap에 할당된 reference 타입의 변수들은 재할당(박싱/언박싱)이 일어나지 않기 때문에 퍼포먼스에 문제가 없습니다.이는 char형을 랩핑한 class인 string형도 포함되는 사항입니다.(마이크로소프트에서 제공하는 Type 정보를 보면 string형은 reference 타입으로 되어있습니다. https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/built-in-types)그러니 int, float, struct 같은 Value Type의 변수들을 쓰는 것이 아니라면 퍼포먼스에 대해서는 걱정하지 않으셔도 되구요, 설사 Value Type을 박싱/언박싱하더라도 박싱/언박싱이 매 프레임마다 다수 일어나는 것이 아닌 특정 상황에서 소수로 일어나는 것이기 때문에 퍼포먼스 이슈가 생길 가능성은 극히 낮습니다. 남용해선 안되겠지만 반대로 너무 사용을 두려워하실 필요도 없습니다. 필요한 곳에 쓰라고 만든 것이니까요.감사합니다.
- 0
- 2
- 29
질문&답변
Dead 애니메이션 출력 오류입니다.
수강해주셔서 감사합니다. Transition Setting을 보시면 CanTransitionToSelf Option이 켜져있는데, 이러면 isDead가 true일 때 Dead Animation이 무한히 재실행되게 됩니다. 해당 Option을 꺼주시면 해결이 될 것 같습니다.다만, 이미지만으로는 정확한 문제를 판단하기 힘들기 때문에 위 방법으로 해결이 안되신다면 수업 자료로 첨부된 완성된 프로젝트를 여셔서 Animator 설정을 비교해보시는게 빠른 해결에 도움이되실 것 같습니다. 추가적인 도움이 필요하시거나 질문이 있으시면 언제든지 글을 써주세요. 감사합니다.
- 0
- 3
- 37
질문&답변
커스텀 에디터에 관하여 질문이 있습니다.
수강해주셔서 감사합니다.따로 작업해주실건 없구요, 게임 플레이시에 enum 값이 안바뀐다는 얘기시면 Quest의 Clone 함수에서 new TaskGroup을 만들 때 enum 값도 복사하시는지 확인해보셔야할 것 같습니다.public Quest Clone(){var clone = Instantiate(this);clone.taskGroups = taskGroups.Select(x => new TaskGroup(x)).ToArray();return clone;}감사합니다.
- 0
- 2
- 41
질문&답변
스킬 구현 질문입니다
수강해주셔서 감사합니다. 말씀하신 방식대로 진행을 하셔도 되구요, Effect의 duration을 0(=무한)으로 설정하고 종료 Option을 DurationEnded로 설정하면 무한히 적용되니, Buff를 준 영웅이 죽었을 때 다른 Entity들에 적용된 Buff를 삭제할 방법만 마련하시면 이 방법을 쓰셔도 됩니다. 삭제 방법으로는 Effect 자체에 종료 조건을 Module로 만들어서 Effect Owner Skill의 Owner Entity가 죽으면 끝나게하거나 영웅이 죽을 때 다른 Entity들을 조사해서 영웅이 준 Buff를 삭제하는 식 등이 있습니다. 답은 없으니 낫다고 생각되는 방향으로 진행하시면 됩니다. 감사합니다.
- 0
- 2
- 42
질문&답변
파일 저장방식, 퀘스트 실패에 관하여 여쭤보고싶습니다.
수강해주셔서 감사합니다.Json을 이용하면 추후에 BaaS Service와 연동하여 서버에 Data를 저장할 경우, 그 외 서버와의 통신이 필요할 경우 Json Data를 서버로 보내기만 하면 되기 때문에 서버 작업에 용이합니다.Playerprefs가 아닌 File 형태로 저장할 경우 저장 Data의 변조가 매우 쉬워지기 때문에 큰 차이가 없긴 하지만 조금이나마 변조가 귀찮으라고 Playerprefs를 이용해 저장하는거구요, 유료로 판매하는 게임에서는 File 형태로 저장해도 무방합니다. Playerprefs에 저장된 Data를 보고 싶으시다면 Playerprefs에 저장된 값들을 Unity Window 창으로 볼 수 있게 해주는 에셋들이 많이 있습니다. 말씀하신 아이디어는 굳이 새로운 class를 만들지 않고 그냥 Quest와 TaskGroup 자체에 생각하신 아이디어를 적용해서 확장하시면 될거 같구요, 웬만하면 상속보다는 먼저 확장(or 모듈화)을 고려해서 설계를 하시는게 좋습니다. Complete시에 TaskGroup들의 성공 여부에 따라 Quest의 성공 단계가 결정되고(=대성공, 성공, 실패, 대실패) Reward에서 성공 단계에 따라 차등 보상을 주는 아이디어는 좋아보입니다.상속은 엄청난 리스크를 동반하는 행위구요, 기능 몇 개가 추가된다고 상속으로 자식 class를 만들어버리면 나중에 여기저기 중복 Code가 생기면서 Code가 꼬일 가능성이 높습니다. Achievement class 가 약간 오해를 드린 것 같은데요, 강의에서 Quest를 상속 받는 Achievement를 만든건 Quest와 업적이 보편적으로 역활이 나눠지기 때문에 편의성을 위해 나눈 것이지 Achievement class 자체는 없어도 상관이 없습니다.만약 제가 Quest의 기능을 더 확장한다고하면 Quest 자체를 확장하고, Quest class가 너무 커진다 싶으면 정리를 해서 더 세심하게 모듈화를 하지 웬만하면 Quest를 상속 받는 무언가를 만들 일은 없을 것 같습니다.감사합니다.
- 0
- 1
- 48