묻고 답해요
148만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
Retrieving array size but no array was provided 오류가 발생합니다.
property.arraySize이 문제인것 같은데private void DrawSkillDatas(){// Skill의 Data가 아무것도 존재하지 않으면 1개를 자동적으로 만들어줌if (skillDatasProperty.arraySize == 0){// 배열 길이를 늘려서 새로운 Element를 생성skillDatasProperty.arraySize++;// 추가한 Data의 Level을 1로 설정skillDatasProperty.GetArrayElementAtIndex(0).FindPropertyRelative("level").intValue = 1;}if (!DrawFoldoutTitle("Data"))return;EditorGUILayout.PropertyField(isAllowLevelExceedDatasProperty);// Level 상한 제한이 없다면 MaxLevel을 그대로 그려주고,// 상한 제한이 있다면 MaxLevel을 상한으로 고정 시키는 작업을 함if (isAllowLevelExceedDatasProperty.boolValue)EditorGUILayout.PropertyField(maxLevelProperty);else{// Property를 수정하지 못하게 GUI Enable의 false로 바꿈GUI.enabled = false;var lastIndex = skillDatasProperty.arraySize - 1;// 마지막 SkillData(= 가장 높은 Level의 Data)를 가져옴var lastSkillData = skillDatasProperty.GetArrayElementAtIndex(lastIndex);// maxLevel을 마지막 Data의 Level로 고정maxLevelProperty.intValue = lastSkillData.FindPropertyRelative("level").intValue;// maxLevel Property를 그려줌EditorGUILayout.PropertyField(maxLevelProperty);GUI.enabled = true;}EditorGUILayout.PropertyField(defaultLevelProperty); for (int i = 0; i < skillDatasProperty.arraySize; i++){var property = skillDatasProperty.GetArrayElementAtIndex(i);var isUseCastProperty = property.FindPropertyRelative("isUseCast");var isUseConcentrateProperty = property.FindPropertyRelative("isUseConcentration");var chargeDurationProperty = property.FindPropertyRelative("chargeDuration");var chargeTimeProperty = property.FindPropertyRelative("chargeTime");var needChargeTimeToUseProperty = property.FindPropertyRelative("needLeastChargeTime");var perfectDamageChargeTimeProperty = property.FindPropertyRelative("perfectDamageChargeTime");var perfectDamageChargeTimeDetectionProperty = property.FindPropertyRelative("perfectDamageTimeDetection");EditorGUILayout.BeginVertical("HelpBox");{// Data의 Level과 Data 삭제를 위한 X Button을 그려주는 Foldout Title을 그려줌// 단, 첫번째 Data(= index 0) 지우면 안되기 때문에 X Button을 그려주지 않음// X Button을 눌러서 Data가 지워지면 true를 return함if (DrawRemovableLevelFoldout(skillDatasProperty, property, i, i != 0)){// Data가 삭제되었으며 더 이상 GUI를 그리지 않고 바로 빠져나감// 다음 Frame에 처음부터 다시 그리기 위함EditorGUILayout.EndVertical();break;}EditorGUI.indentLevel += 1;if (property.isExpanded){// SkillData Property 내부로 들어감 -> Property == level field;property.NextVisible(true);DrawAutoSortLevelProperty(skillDatasProperty, property, i, i != 0);// Level Upfor (int j = 0; j < 2; j++){property.NextVisible(false);EditorGUILayout.PropertyField(property);}// PrecedingAction// Toggle Type일 때는 PrecedingAction을 사용하지 않을 것이므로,// Instant Type일 때만 PrecedingAction 변수를 보여줌property.NextVisible(false);if (useTypeProperty.enumValueIndex == (int)SkillUseType.Instant)EditorGUILayout.PropertyField(property);// Action And Settingfor (int j = 0; j < 8; j++){// 다음 변수의 Property로 이동하면서 그려줌property.NextVisible(false);EditorGUILayout.PropertyField(property);}// Castproperty.NextVisible(false);if (IsDrawPropertyAll && !isUseConcentrateProperty.boolValue)EditorGUILayout.PropertyField(property);elseproperty.boolValue = false;property.NextVisible(false);if (isUseCastProperty.boolValue)EditorGUILayout.PropertyField(property);// Chargeproperty.NextVisible(false);if (IsDrawPropertyAll && !isUseCastProperty.boolValue)EditorGUILayout.PropertyField(property);for (int j = 0; j < 5; j++){property.NextVisible(false);if (isUseConcentrateProperty.boolValue)EditorGUILayout.PropertyField(property);}// 최대 chargeTime 값을 chargeDuration 값으로 제한chargeTimeProperty.floatValue = Mathf.Min(chargeTimeProperty.floatValue, chargeDurationProperty.floatValue);// 최대 needChargeTime 값을 chargeTime 값으로 제한needChargeTimeToUseProperty.floatValue = Mathf.Min(chargeTimeProperty.floatValue, needChargeTimeToUseProperty.floatValue);perfectDamageChargeTimeProperty.floatValue = Mathf.Clamp(perfectDamageChargeTimeProperty.floatValue,needChargeTimeToUseProperty.floatValue,chargeTimeProperty.floatValue);// Effectproperty.NextVisible(false);EditorGUILayout.PropertyField(property); //// EffectSelector의 level 변수를 effect의 최대 level 제한함for (int j = 0; j < property.arraySize; j++){var effectSelectorProperty = property.GetArrayElementAtIndex(j);// Selector의 level Property를 가져옴var levelProperty = effectSelectorProperty.FindPropertyRelative("level");// Selector가 가진 effect를 가져옴var effect = effectSelectorProperty.FindPropertyRelative("effect").objectReferenceValue as Effect;var maxLevel = effect != null ? effect.MaxLevel : 0;var minLevel = maxLevel == 0 ? 0 : 1;levelProperty.intValue = Mathf.Clamp(levelProperty.intValue, minLevel, maxLevel);}}파란색인 부분에서 계속 걸리는데 무슨 문제인지 모르겠습니다. 파란색 뒷부분은 너무 길어져서 잘랐습니다.skillDatasProperty를 가져오는거라 Skill의 skillDatas가[SerializeField]private SkillData[] skillDatas;배열인것도 확인을 하였지만if (property.isArray){int size = property.arraySize;Debug.Log("사이즈 :" + size);}else{Debug.LogError("이 속성은 배열 또는 리스트가 아닙니다.");}이걸로 확인하니 배열이 아니라고 오는데 어떻게 배열이 아니게 된건지 모르겠습니다.
-
미해결[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
전투시스템과 결합하려고 합니다.
현재 강의에서는 탑뷰 형식의 AOS와 적합한 스킬 사용 방식이기에 제가 개발하려는 전투시스템에 맞춰서 공격 판정을 콜라이더를 주로 사용하려고 합니다.공격을 실행하면 애니메이션 시작과 동시에 스킬이 발동되게 하고 애니메이션 이벤트를 활용해서 특정 구간에 콜라이더를 활성화 해준 다음 비 활성화해주고 애니메이션이 끝나면 스킬이 종료되는 방식으로 공격을 구현하려고 하는데 많이 비효율적일까요. 학습한 내용을 이해한거로는 투사체 스킬은 Targetsearcher은 연관이 없고 인디케이터가 공격할 좌표만 정해주고 캐릭터를 해당 좌표를 향해 회전해주고 발사하는 것이기에 Targetsearcher와 인디케이터를 전부 없애버리고 투사체 스킬의 투사체 생성 대신에 소켓을 이용해서 공격판정 무기를 찾은 다음 해당 스킬 스크립트를 생성시켜준 다음에 애니메이션 이벤트로 콜라이더를 활성화 비 활성화로 공격 판정을 구현하고 공격 애니메이션이 끝나면 해당 스크립트를 삭제시켜주는 방식으로 구현을 생각중입니다.혼자서 독학 중이라 다른 사람들의 개발 방식을 볼 때마다 제 방식에 대한 확신을 가지지 못하는 것 같습니다.
-
미해결[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
uphandcast만 트리거를 쓰는건지 궁금합니다.
// 인자로 받은 animatorParameter가 bool Type이면 owner의 StateMachine으로 인자로 받은 command를 보냄// Transition이 Command를 받아들였으면, State로 UsingSKill Message와 Skill 정보를 보냄if (animatorParameter.type == AnimatorParameterType.Bool && ownerStateMachine.ExecuteCommand(command)) ownerStateMachine.SendMessage(EntityStateMessage.UsingSkill, (skill, animatorParameter));// 인자로 받은 animatorParameter가 trigger Type이면 행동에 제약을 주지 않을 것이므로 ToDefaultState Command를 보내고// Transition이 받아들였는지와 상관없이, State로 UsingSkill Message와 skill 정보를 보냄else if (animatorParameter.type == AnimatorParameterType.Trigger){ ownerStateMachine.ExecuteCommand(EntityStateCommand.ToDefaultState); ownerStateMachine.SendMessage(EntityStateMessage.UsingSkill, (skill, animatorParameter));} 왜 Uphandcast만 트리거 타입이 되는지와 행동에 제약을 받지않는건지 궁금합니다.
-
미해결[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
다른형식의 근접공격에 대해 질문할것이 있습니다.
다크소울과 몬스터헌터 같은 게임의 근접공격 구현은 스킬의 액션에 새로운 액션을 만든 다음 피격 판정이 있는 무기에 콜라이더를 넣어준 다음 기존의 투사체 공격에 있는 소켓을 이용해서 구현하면 될까요
-
해결됨[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
Input 스킬 구현 질문
안녕하세요! 좋은 강의 감사합니다. 리븐 Q와 아트록스 Q와 같이 Input형 스킬이면서 n번째 사용 스킬이 다른 경우(ex) 리븐 Q 3타 에어본) 어떻게 구현하면 좋을 지 질문 드립니다!
-
미해결[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
스텟 연동 관련해서 질문이 있습니다.
제가 기존에 개발해둔 아이템 시스템을 연동시키면서 아이템의 스텟을 정할때 Resources/Stat폴더를 참조하고 그걸 기반으로 Enum으로 해당 스텟들이 있는 스크립트 파일을 만든 다음에 아래에 있는 버튼을 눌러서 일일히 Enum 파일을 동기화시켰는데 너무 번거로운것 같습니다. 생성, 삭제, 수정할때마다 동기화시키면 작업할때마다 딜레이가 너무 커지는데 다른 방법이 있을까요if (dataType == typeof(Stat)){GUI.color = Color.yellow;// Data를 이름 순으로 정렬하는 Button을 그림if (GUILayout.Button($"Press it after you finish editing")){StatEnumSynchronizer.SyncStatEnum();EditorUtility.SetDirty(database);AssetDatabase.SaveAssets();}}아래는 해당 enum파일을 만드는 스크립트입니다.using System;using System.IO;using System.Linq;using UnityEditor;using UnityEngine;public static class StatEnumSynchronizer{private const string StatFolderPath = "Assets/Resources/Stat";[MenuItem("Tools/Sync Stat Enum")]public static void SyncStatEnum(){if (!Directory.Exists(StatFolderPath)){Debug.LogError($"Directory does not exist: {StatFolderPath}");return;}var statFiles = Directory.GetFiles(StatFolderPath, "*.asset", SearchOption.TopDirectoryOnly);var statCodeNames = statFiles.Select(file => AssetDatabase.LoadAssetAtPath<Stat>(file)).Where(stat => stat != null).Select(stat => stat.CodeName).Distinct().OrderBy(name => name).ToList();string enumPath = "Assets/Scripts/Enum/ItemStatusAttribute.cs";using (var writer = new StreamWriter(enumPath, false)){writer.WriteLine("public enum ItemStatusAttribute");writer.WriteLine("{");writer.WriteLine(" Default,");foreach (var codeName in statCodeNames){string enumName = SanitizeEnumName(codeName);writer.WriteLine($" {enumName},");}writer.WriteLine("}");}AssetDatabase.Refresh();Debug.Log("Stat Enum synchronized.");}private static string SanitizeEnumName(string name){return new string(name.Where(c => !char.IsWhiteSpace(c) && (char.IsLetterOrDigit(c) || c == '_')).ToArray()).Replace(" ", "_").Replace("-", "_").ToUpper();}}
-
미해결[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
텍스트 변환 Mark에 대하여 질문이 있습니다.
EffectAction에서public string BuildDescription(Effect effect, string description, int stackActionIndex, int stack, int effectIndex){ var stringsByKeyword = GetStringsByKeyword(effect); if (stringsByKeyword == null) return description; if (stack == 0) // ex. description = "적에게 $[EffectAction.defaultDamage.0] 피해를 줍니다." // defaultDamage = 300, effectIndex = 0, stringsByKeyword = new() { { "defaultDamage", defaultDamage.ToString() } }; // description.Replace("$[EffectAction.defaultDamage.0]", "300") => "적에게 300 피해를 줍니다." description = TextReplacer.Replace(description, "effectAction", stringsByKeyword, effectIndex.ToString()); else // Mark = $[EffectAction.Keyword.StackActionIndex.Stack.EffectIndex] description = TextReplacer.Replace(description, "effectAction", stringsByKeyword, $"{stackActionIndex}.{stack}.{effectIndex}"); return description;}여기 부분에서 왜 굳이 스택 0번째 인걸 구분하고 Effect에서 여기에서public string BuildDescription(string description, int effectIndex){ Dictionary<string, string> stringsByKeyword = new Dictionary<string, string>() { { "duration", Duration.ToString("0.##") }, { "applyCount", ApplyCount.ToString() }, { "applyCycle", ApplyCycle.ToString("0.##") } }; description = TextReplacer.Replace(description, stringsByKeyword, effectIndex.ToString()); description = Action.BuildDescription(this, description, 0, 0, effectIndex); // 여기부분에 질문이 있습니다 여기에서 0번째 것을 구분하고 var stackGroups = StackActions.GroupBy(x => x.Stack); foreach (var stackGroup in stackGroups) { int i = 0; foreach (var stackAction in stackGroup) description = stackAction.BuildDescription(this, description, i++, effectIndex); }// 여기에서 1스택이상 스킬의 텍스트를 전부 변환해주는데 return description;} EffectStackAction에서public string BuildDescription(Effect effect, string baseDescription, int stackActionIndex, int effectIndex) => action.BuildDescription(effect, baseDescription, stackActionIndex, stack, effectIndex);이렇게 까지 분리해서 텍스트로 변환하는지 여쭤보고 싶습니다.그냥 0번째부터 텍스트를 변환하면 안되는건지 질문이 있습니다.
-
미해결[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
스킬 구현 질문
잘 듣고 있습니다 강사님. 롤로 치면 그레이브즈 Q 를 구현하고 싶은데 Action을 Spawn Skill Object Action으로 하고 비슷한 파티클을 써서 해보니제 위치에서 나가는게 아니라 클릭한 기준으로 직선으로 나가더라구요 여러가지 방법을 시도 해보고 있는데 어떻게 해야할지 조언 부탁 드립니다!
-
미해결[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
Skill의 Custom Action 중 Action의 유니티 버그 레포트
안녕하세요 강의 잘 보고 있습니다.다름 아니라 Skill에 CustomAction의 Action 탭에서 Value값들을 바꿀려하면 유니티 버그 레포트가 나타나면서 유니티가 꺼지는 현상이 지속되고 있습니다.EX) Owner Or User를 Target으로 바꿀시 크래시...다른 Cast나 Charge, Preceding 쪽에선 문제가 발견되지 않습니다.스크립트상으론 강사님의 스크립트 그대로 가져와도 똑같은 문제가 발생하는 것 같은데 아님 혹시 제가 놓친 부분이 있나 싶어서 문의드립니다.
-
미해결[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
Statemachine 관련질문이 있습니다.
격투게임이나 몬스터헌터처럼 애니메이션의 특정 프레임 이후에 입력받아야 특정 애니메이션이 실행가능하게 하려하는데 이걸 어떻게 해야할까요?기존에는 애니메이션 이벤트를 이용해서 구현했었는데 어떻게 해야할까요?애니메이션 이벤트를 활용하려고 하니 오버로드된 함수들 때문에 이런 경고창이 뜨긴하는데 애니메이션 이벤트의 함수들은 오버로드된 함수들이 아니라 괜찮은가요?아니면 다른 좋은 방법이 있는지 알려주실수있을까요?
-
해결됨[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
StateMachine중에 이해가 안가는 부분이 있습니다
TryTransion함수에서if (transition.TransitionCommand != StateTransition<EntityType>.kNullCommand || !transition.IsTransferable) continue;이부분에서 나눠서 짜면if (커맨드있음) continue;if (전이불가) continue;인데 사실상 커맨드가 있으면 뒤에 전이상태는 상관이 없다고 생각을 하는데요.그런데 이제 StateMachine을 상속한 클래스에서 MakeTransition을 한것을 보면MakeTransition<ReadyState, CastingState>(SkillExecuteCommand.Use, state => Owner.IsUseCast);이런식으로 커맨드와 조건을 같이 써놓은것이 있는데, 뒤에는 작동이 안되지 않나라고 생각해서 제가 잘못해석한건지 알아보고자 질문드립니다.
-
미해결[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
스킬제작 질문입니다
리그오브레전드로 예시를 많이 드셨는데 바루스 Q나 비에고W같은 스킬은 어떻게 제작할 수 있을까요.간단한 힌트를 부탁드립니다..
-
미해결[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
DrawFoldoutTitle에 질문이 있습니다.
CustomEditorUtility에 있는public static bool DrawFoldoutTitle(string title, bool isExpanded, float space = 15f)와 public static bool DrawFoldoutTitle(IDictionary<string, bool> isFoldoutExpandedesByTitle, string title, float space = 15f)이 각각 있는데 첫번째 DrawFoldoutTitle에서 space 값을 변경해도 두번째의 DrawFoldoutTitle에있는isFoldoutExpandedesByTitle[title] = DrawFoldoutTitle(title, isFoldoutExpandedesByTitle[title], space);여기 때문에 변경이 안되는거고 타이틀이 여러개 있을경우를 대비해서 여러개 각각의 간격을 위한건가요
-
해결됨[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
deadstate 질문입니다
deadstate에서 에니메이션이 다 재생된후 destroy를 할려면 어떻게 해야할까요따로 함수를 만들어 애니메이션 트리거로 destroy를 하고 있는데 더 나은 방법이 있을까 해서 질문해봅니다
-
미해결[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
현업에서 OdinInpector도 자주 쓰이나요??
OdinInspector를 구매해 둔 김에 사용해보고 싶어서 강의를 따라가며 Editor코딩 부분을 Odin으로 해볼까 싶은데요일단 기본적인 Editor코딩 경험 vs OdinInspector사용 경험둘 중에 어떤 것을 더 추천하시나요??
-
해결됨[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
이런 스킬시스템을 저장하는 방법
강의 모두 잘들었습니다. 공부한 내용을 바탕으로 스킬시스템을 넣은 게임을 만드려고 합니다. 추가로 파이어베이스의 리얼타임 데이터베이스까지 사용해서 세이브/로드 기능도 구현하려고 하는데, 이때 Json으로 저장하는 부분에서 어려움을 느끼고 있습니다. 우선 제가 알고있는 수준은 string json = JsonUtility.ToJson(player); 같이 ToJson으로 변환해서 저장하는 정도입니다. 다만 이 방법으로는 int,string,float 같은 기본 자료형만 저장이 가능한데, 이때 Entity가 들고있는 저장해야하는 데이터들. 여기서는 SkillSystem과 Stats도 저장에 포함되어야겠죠 스탯과 스킬이니깐. 이런 것들은 어떤식으로 저장을 구현하는지 마땅히 떠오르지 않습니다..멀티 게임은 아니고 싱글게임에 서버만 입혀주는 정도기 때문에 Player(강의의 Entity)의 다양한 데이터를 저장하는 방법을 알아야 하는데, 이 강의에서 나오는 현재 플레이어가 보유한 스탯,스킬의 정보는 어떤식으로 저장을 해야하나요? 최대한 쉽게.. 알려주시면 정말 너무 감사하겠습니다 ㅠㅠ 어려워요..
-
해결됨[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
유니티 2023.1을 쓰는 이유가 따로 있는지 궁금합니다.
최신 버전이라서 쓰는 건가요? 아니면 최신 버전에서 지원하는 기능이 있는 걸까요?강의를 이제 듣기 시작해서 한 번 여쭤봅니다!
-
미해결[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
데이터베이스 생성 Resources 폴더를 써도 될까요?
현재 강의를 듣고나서 이를 바탕으로 저만의 시스템을 만들어보려고 합니다.강의 스킬시스템윈도우 영상에서 AssetDatabase.CreateFolder("Assets/Resources", "Database");와 같이 Resources 폴더를 생성하고 그 안에 SO를 차곡차곡 넣어주는데, 제가 어드레서블을 사용할 예정이라 Resources 폴더를 만들지 않고 필요한 리소스들을 어드레서블을 통해 관리할려고 했습니다.그런데, 이 강의에서 보면 리소스폴더에 SO 파일만 넣어주니깐 용량이 거의 있지도 않은 수준이던데 이렇게 강의처럼 에디터에서 생성한 SO만 들어간다면 리소스폴더를 사용해도 무방할까요? 혹은 애초에 굳이 리소스 폴더가 필요한지도 궁금합니다. 강의에선 특별히 Resources.Load 함수를 사용하지 않더라구요.
-
해결됨[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템
막힌 부분이 있어 질문드립니다
인디케이터가 원뿔형으로 하였을 때 중앙이 마우스 커서를 향하게 회전하게 할려면 어떻게 해야 하나요??private void RotateTowardsMouse(){Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);mousePosition.z = 0;Vector3 direction = mousePosition - transform.position;float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;transform.rotation = Quaternion.Euler(new Vector3(0, 0, angle));}2d 환경에서 인디케이터 코드에 이 코드를 추가하여 update에서 실시간으로 회전하게 만들었는데문제가 90도 일때는 45도 지점이 마우스 커서 방향을 향하게 되는데180도는 0도 지점270도일때는 135지점에 마우스커서가 향하게 할려했는데 315도 지점에 마우스커서가 향하게 됩니다실제로 탐색하는 부분인 searchArea 코드에서는ector2 requesterPosition = requesterObject.transform.position;Vector2 searchDirection = requesterObject.transform.forward;if (isRotateWithMouse){Vector2 mouseWorldPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);searchDirection = (mouseWorldPosition - requesterPosition).normalized;}// 중략//if (Vector2.Angle(searchDirection, direction) < (angle * 0.5f))targets.Add(entity.gameObject);이렇게 수정하였는데 이쪽은 원하는 범위를 제대로 탐색합니다요약하자면은 실제 탐색범위는 원하는 범위를 탐색을 하는데인디케이터의 범위가 실제 탐색범위와 일치하지 않습니다.제가 실력이 아직 부족하여 문제점을 제대로 해결하지 못해 질문드립니다.
-
해결됨제미니의 개발실무 - 지속 성장 가능한 소프트웨어를 만들어가는 방법
Business Logic!
좋은강의 정말 감사드립니다!말씀하신 비즈니스 로직이 정말 보기에도 좋고 깔끔하다고 생각되서 저도 똑같이 구현해보고 싶다고 생각하는데요외부에서 주입받도록 분리한 로직들은 어느 레이어에 위치시켜야 하는지 궁금합니다. 아직 비즈니스 로직강의만 듣고 질문을 남겨서 혹시 뒷 강의에서 이에 대한 해답이 나온다면 답변해주지 않으셔도 괜찮습니다!감사합니다!