[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
₩198,000
중급이상 / Unity, modules
5.0
(10)
다양한 게임에 적용할 수 있는 [스킬 시스템]과 개발 생산성을 높여주는 [툴 제작], 구조적인 프로그래밍 방식인 [모듈식 프로그래밍]을 배우는 강의입니다.
중급이상
Unity, modules
안녕하세요, 게임을 사랑하고 개발을 사랑하는 게임 프로그래머 Developer G입니다.
저는 어떻게하면 깔끔하고 체계적인 코드를 작성할 수 있을지 항상 고민하는데요,
제 고민의 결과물들을 여러분들에게 아낌없이 가르쳐드리겠습니다!
[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
₩198,000
중급이상 / Unity, modules
5.0
(10)
다양한 게임에 적용할 수 있는 [스킬 시스템]과 개발 생산성을 높여주는 [툴 제작], 구조적인 프로그래밍 방식인 [모듈식 프로그래밍]을 배우는 강의입니다.
중급이상
Unity, modules
[유니티 레벨 업!] 모듈식으로 개발하는 퀘스트&업적 시스템
₩36,300
초급 / Unity
4.8
(47)
다양한 게임에 적용할 수 있는 [퀘스트 시스템]과 개발의 생산성을 높이기 위한 [모듈식 프로그래밍]을 배우는 강의입니다.
초급
Unity
질문&답변
Elite와 Normal Germ의 IsTarget()질문입니다.
수강해주셔서 감사합니다.GameObjectTarget Script의 IsEqual 함수의 내용을 보시면,return targetAsGameObject.name.Contains(value.name);라고 되어있습니다.그럼 결론적으로 코드는 아래와 같이 됩니다.EliteGermSlimeTarget.Contains(GermSlimeTarget)EliteGermSlimeTarget이라는 문장에 GermSlimeTarget이 포함되어있으므로 true를 return하는거죠.GermSlimeTarget의 이름을 NormalGermSlimeTarget으로 바꾸시면 문제가 해결될겁니다.강의에서도 말씀드렸지만, 이름으로 객체를 비교하는건 이러한 문제가 생길 수 있으므로 본격적으로 자신의 게임에 QuestSystem을 적용하실 때는 좀 더 확실히 GameObject들을 비교할 방법을 만들어주시는게 좋습니다.감사합니다.
질문&답변
MMORPG라면 서버가 어떤 기능들을 가져가야 할까요?
수강해주셔서 감사합니다.이건 뭐가 답이 있는건 아니구요,클라에서 로직을 실행하고, 판정만 서버쪽에 넘기면 서버 부담은 적어지겠지만 보안이 취약해질거고,서버에서 로직을 실행하고, 클라는 상태에 대한 동기화만 한다면 서버 부담은 커지겠지만 보안은 강해질겁니다.풀 서버 방식으로 만든다고하면, 말씀하신 것들을 전부 서버에서 처리하고 클라는 Skill과 Entity의 상태에 대한 동기화만 해주는게 맞을 것 같습니다.다만, TargetSearcher의 경우에는 클라의 입력을 서버로 보내서 서버가 클라의 입력이 올바른지 검증할 필요가 있습니다.감사합니다.
질문&답변
공격할 때마다 일정 확률로 추가 투사체 발사
수강해주셔서 감사합니다.공격할 때마다 일정 확률로 추가 투사체 발사라는게, 3발 쏘던 원거리 공격이 확률적으로 4발이 나갈 수 있다는 얘기신지, 아니면 그냥 근접 공격처럼 아무 공격이나 하다보면 발사체가 나갈 수가 있다는 얘기신지 애매하네요.어쨌거나, '일정 확률로 추가 투사체 발사'는 Buff 효과로 적용할 수 있을 것 같습니다. 전에 Effect를 event와 연결해서 Apply 시키는 법을 설명드렸었는데, 그 내용을 똑같이 적용하면 됩니다.Skill의 발동 단계마다 event를 만들고(onStart, onPrecedingActionStart, onApply 등등) , 원하는 event에 Effect를 연결해서 랜덤으로 투사체를 발사해주면 되겠죠.예를 들어, 투사체를 3발 발사하는 Skill이라면 Skill의 onApply event에 Effect의 Callback 함수를 연결해서 Skill의 발동을 감지하여 랜덤으로 추가 발사체를 발사하면 될겁니다.그런데 평소에는 3발로 나가다가, 4발로 나갈 때 원뿔형으로 예쁘게 쏘게 만들고 싶을 수 있습니다. 단순히 이후에 발사체 하나를 추가로 더 쏘는 경우에는 4개가 예쁘게 정렬된 형태로 쏠 수가 없죠? 이 경우에는 SpawnProejectileAction에 발사체 수를 계산하는 action을 만들어서 Effect가 SpawnProejectileAction에 직접 접근하여 이 action에 callback 함수를 연결해주면 됩니다.이게 글로 설명드리자니 잘 이해가 안되실거 같은데,foreach (var calc in CalcDamage.GetInvocationList().Cast()) damage = calc.Invoke(this, damage);Invocation을 통해서 연결된 Action들을 순회하면서 최종 값을 구할 수 있습니다.(복잡한 RPG 게임에서 매우 많이 사용되는 방법입니다.)예를 들어, 초기 Damage가 10이고CalcDamage Action에 (damage) => damage - 2; // 10 - 2(damage) => damage - 2; // 8 -2(damage) => damage - 1; // 6 - 1이렇게 세 개의 Callback 함수가 연결되어 있으면foreach문을 돌았을 때 최종 Damage가 5가 되는거죠.이걸 이용해서 다음처럼 발사체 수를 그때그때 바꿔주면 되는겁니다.(projectileCount) => projectileCount + Random.Range(0, 2);말씀하신 Condition을 계속 확인하는 것도 나쁘지 않습니다. 성능이 걱정되시는 것 같은데, 성능 문제는 실제로 그 문제가 터지고나서 생각해봐야 합니다. 매 Frame 조건 검사 좀 한다고 큰 문제가 생길거라 생각이 들 것 같지도 않구요. 섣부른 최적화는 하지 말라는 말이 있으며, 당장 다른 해결책이 안떠오른다면 최선을 방법을 선택하는게 맞다고 생각이 듭니다.감사합니다.
질문&답변
공격력 주문력 스킬 계수 레벨 적용
수강해주셔서 감사합니다.저 같은 경우는 RPG 개발용 개인 Framework를 가지고 있는데요,이 프레임워크에는 강의의 Database를 Excel/GoogleSheet로 Export/Import 하는 기능이 갖춰져 있습니다.그래서 아주 간단한 게임의 경우에는 그때그때 Framework에서 바로 Data를 수정하구요, 좀 규모가 크면 Excel/GoogleSheet로 Data를 수정한 뒤 Framework로 Import합니다.이런 기능을 구현해놓으면 나중에 Data 변조를 방지를 위해서 실제 Database(SQL)에서 정보를 받아와야할 때도 편합니다.Excel로 Export/Import 기능은 이 강의에 넣을까하다가 괜히 DB 구조 작성에 대해 어설프게 이해시켜드릴까봐 뺐는데(좀 복잡하기도 하구요.), 해당 내용에 대한 요청이 많아서 다음 강의로 예정된 ItemSystem 강의에서 다루게 될 것 같습니다.더 간단한 해결책으로는, 지금 SkillSystemWindow에서 목록에 있는 Skill을 누르면 우측에 Skill의 Custom Editor가 뜨는 것처럼, Skill의 EffectSelector에 Effect를 넣으면 Foldout 형태로 Effect의 Custom Editor가 그려지게 만들면 됩니다.(보통 InlineEditor라고 부릅니다.) 그럼 Effect Tab으로 이동할 필요 없이 바로 확인이 가능하겠죠. 직접 만들기 힘드시다면, Odin Inspector나 InlineEditor Attribute를 제공하는 Open Source Custom Atrrbiute들이 많기 때문에 그쪽으로 찾아보시면 될 것 같습니다. 아니면 Effect의 정보를 다 그리지말고 Level별 Action 정보들만 그려서 바로 수정할 수 있게 해준다던가, 여러가지로 Tool 내에서 직관성을 개선할 방법이 많습니다. 툴 프로그래머들이 하는게 이런 일들입니다.다만, 확실히 아셔야할건 Excel을 이용하든, Tool의 기능을 강화하든 모듈식 구조는 정보가 여기저기 흩어져있기 때문에 직관성이 떨어지는건 어쩔 수 없는 부분입니다, RPG의 Quest 같은 것들도 제대로 만들면 Quest 하나를 구성하기 위해 많은 수의 Table이 필요하구요, 기획자나 프로그래머나 이 구조를 설계하고 익숙해지는데 많은 시간이 들어갑니다.감사합니다.
질문&답변
아이템 및 시너지 효과 스킬 적용
수강해주셔서 감사힙니다.적을 처형하는 효과의 경우, 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);}이런 식으로 처리해줄 필요가 있습니다.감사합니다.
질문&답변
퀘스트 클리어해도 동일 퀘스트가 노출됩니다
안녕하세요, 보내주신 프로젝트를 확인하였습니다.QuestTracker Script의 UpdateTaskDescriptors 함수를 보시면(사진)Instantiate로 생성한 Clone의 UpdateText 함수를 실행하는게 아니라 Prefab의 UpdateText 함수를 실행하고 있습니다. 그래서 생성된 taskDescriptors의 Text가 Update되지 않아 생긴 문제구요,(사진)다음과 같이 Clone의 UpdateText 함수를 실행해주시면 정상적으로 Text가 출력이 됩니다.감사합니다.
질문&답변
퀘스트 클리어해도 동일 퀘스트가 노출됩니다
수강해주셔서 감사합니다.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로 프로젝트를 압축해서 보내주시면 직접 확인해드리겠습니다.감사합니다.
질문&답변
퀘스트 누적 클리어 횟수에 대해 여쭤보고싶습니다.
수강해주셔서 감사합니다.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가 몇 개 있는지 확인하여 누적 클리어 횟수를 확인할 수 있습니다.감사합니다.
질문&답변
프로퍼티 사용 질문입니다.
수강해주셔서 감사합니다.어떤 Property를 얘기하시는건지 모른채로 답변드리기가 애매해서 혹시 어떤 Property를 보고 그런 생각을 하셨을까요?
질문&답변
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을 박싱/언박싱하더라도 박싱/언박싱이 매 프레임마다 다수 일어나는 것이 아닌 특정 상황에서 소수로 일어나는 것이기 때문에 퍼포먼스 이슈가 생길 가능성은 극히 낮습니다. 남용해선 안되겠지만 반대로 너무 사용을 두려워하실 필요도 없습니다. 필요한 곳에 쓰라고 만든 것이니까요.감사합니다.