현재 제가 인벤토리를 만들어 사용할 스킬을 등록하는 스킬창과 획득한 스킬을 보관하는 스킬보관창을 만들었습니다 스킬창에 있는 스킬은 활성화되고 보관창에 있는것은 비활성화되게 할려는 의도입니다. 액티브 스킬의 경우 키를 눌러 발동하기 때문에 문제가 없는데 패시브 스킬의 경우 스킬보관창에 있어도 계속해서 발동됩니다. 그래서 패시브 타입인 경우 스킬보관창에 있을 경우 비활성화되게 하기 위해 skill 스크립트와 skillsystem 스크립트에 비활성화 변수를 추가하여 의도한 목적을 제작할려고 하였는데 어디에서 제어해야 하는지 감이 안잡혀 이 부분에 대해 어딜 수정해야 되는지에 대한 조언을 부탁드립니다...
안녕하세요, Extension 관련 구현에 대해 연습해보다가 궁금증이 생겨 질문드립니다. 예를들어 우리가 구현한 Extension 클래스 안에 AddUIEvent 와 같은 메소드를 추가해주면서 this GameObject go 라는 문법을 써줌으로서, 마치 GameObject 의 함수인 것처럼 쓸수 있게되었는데요, 만약 Extension 클래스안에 ToString 이라는 함수를 만들어주면, 실제로는 최상위 클래스인 Object 클래스 내에 구현된 ToString 이 호출되는 것을 확인했습니다. 이럴 경우, 이름을 적절히 잘 회피해서 쓰는 방법 이외에 내가 구현한 함수를 쓰도록 하는 다른 방법이 있을까요?
안녕하세요. 강습 잘 시청하고 있는데 궁금한 부분이 생겨서요. object 형을 많이 쓰던데, 실제 게임을 이 상태로 스킬을 구현해서 출시한다고 한다면 박싱 언박싱에 관련해서 성능 이슈는 없을까요? (ex : Stat.cs Dictionary<object, Dictionary< object , float>> bonusValuesByKey .. ) IdentifiedObject.cs 에서 object Clone() => Instantiate(this); 무시할 만한 수준일지 , 코드가 더 복잡하게 구현될 요지가 있어서 강의용으로 범용적으로 작업하셨는지 해서요. 아님 object 형이 아닌 좀 더 나은 방향이 있다면 의견 듣고 싶습니다. 감사합니다.
InventoryItemDataList에 유저가 보유한 아이템 목록이 저장하고 해당 리스트 객체를 PlayerPrefs로 저장할 때 JSON을 쓰는 이유는 PlayerPrefs에서 int, float, string만 저장할 수 있기 때문에로 이해했는데 맞나요? 그리고 굳이 JSON 이여 할 이유가 있나요? 또 PlayerPrefs에 유저가 접근이 가능하다고 하는데 그러면 악의적으로 변형이 가능하지 않나요? 이 경우는 어떻게 대비하나요?
저번에 조언해주신것을 통해서 다른 에셋들과 오픈소스들을 뜯어가면서 저만의 프레임워크를 설계중입니다. 그런데 다른 객체의 스텟을 가져와서 영향을 주려면 Getcomponent종류를 사용 할 수밖에 없는 것 같아 보이고 강의에서도 Getcomponent스킬의 대상을 지정할때 사용되는것을 보고 질문을 드립니다. Getcomponent가 프레임 단위로 실행되는 수준만 아니면 되는건가요? 그러면 혹시 다른 게임에서의 예시로 버그, 해킹 툴의 요소로 공격속도를 비정상적으로 올라간상태로 공격시 프레임드랍이 걸리는 이유가 이때 Getcomponent같은게 실행이 많이 되어서 그런걸까요? ex)다크소울이었는지 엘든링이었는지 기억이 나질 않지만 버그로 인해 프레임단위로 공격하고 렉을 유발시켰던걸로 기억합니다. public void SearchTargets() { var result = TargetSearcher.SearchTargets(Owner, Owner.gameObject); Targets = result.targets.Select(x => x.GetComponent<Entity>()).ToArray(); TargetPositions = result.positions; }
콜라이더를 이용해서 하는 공격은 무기 콜라이더 액션을 만든다음에 public class WeaponColliderAction : SkillAction { [SerializeField] private string weaponSocketName; public override void Apply(Skill skill) { var socket = skill.Owner.GetTransformSocket(weaponSocketName); socket.AddComponent<SkillCollider>(); } public override object Clone() { return new WeaponColliderAction() { weaponSocketName = weaponSocketName }; } } 무기에 해당하는 오브젝트를 찾아서 스킬 콜라이더 스크립트를 추가하고 public class SkillCollider : MonoBehaviour { private Entity owner; private Skill skill; public void Setup(Entity owner, Skill skill) { this.owner = owner; // 현재 Skill의 Level 정보를 저장하기 위해 Clone을 보관 this.skill = skill.Clone() as Skill; } private void OnTriggerEnter(Collider other) { if (other.GetComponent<Entity>() == owner) return; var entity = other.GetComponent<Entity>(); if (entity) entity.SkillSystem.Apply(skill); Destroy(gameObject); } } 해당 스킬이 적용되게 구현을 하였는데 이런식으로 구현하는지 맞는지와 TargetSearcher없이 자기 자신에게 사용하는 스킬을 Skill스크립트에 있는 public Entity Owner { get; private set; }와 public IReadOnlyList<Entity> Targets { get; private set; }을 활용하면 될것같은데 어떻게 구현하는지 모르겠어서 질문드립니다. 너무 많은 질문을 드리는것같아 죄송합니다.
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 Up for (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 Setting for (int j = 0; j < 8; j++) { // 다음 변수의 Property로 이동하면서 그려줌 property.NextVisible(false); EditorGUILayout.PropertyField(property); } // Cast property.NextVisible(false); if (IsDrawPropertyAll && !isUseConcentrateProperty.boolValue) EditorGUILayout.PropertyField(property); else property.boolValue = false; property.NextVisible(false); if (isUseCastProperty.boolValue) EditorGUILayout.PropertyField(property); // Charge property.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); // Effect property.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와 인디케이터를 전부 없애버리고 투사체 스킬의 투사체 생성 대신에 소켓을 이용해서 공격판정 무기를 찾은 다음 해당 스킬 스크립트를 생성시켜준 다음에 애니메이션 이벤트로 콜라이더를 활성화 비 활성화로 공격 판정을 구현하고 공격 애니메이션이 끝나면 해당 스크립트를 삭제시켜주는 방식으로 구현을 생각중입니다. 혼자서 독학 중이라 다른 사람들의 개발 방식을 볼 때마다 제 방식에 대한 확신을 가지지 못하는 것 같습니다.
혹시 게임개발이나 개발관련 추천하시는 책이 있으실까요? 최근 남는시간에 책을 읽고있는데 지식공유자님이 추천하시는 책을 읽어보고 싶어서 질문드립니다! 책을 많이 읽었던편이 아니라 유명한거라도 괜찮습니다 있으시다면 2-3가지 정도 부탁드립니다 (_ _) 감사합니다 추가로 섹션6에 업적/미션 Pt.2에 Resources/Textures 폴더를 누락하신 듯 합니다. 때문에 IconGolds와 IconGems 텍스쳐를 불러오지 못하네요 통합강의자료에는 있어서 그걸로 임포트 했습니다!
새싹부터 시작하는 Unity 게임 개발 섹션3 2D게임 애니메이션 강의 시청 중 점프 애니메이션이 적용되지 않습니다. 유니티에서 transition은 아래와 같이 설정한 상태이고 코드는 강의와 똑같이 작성한 상태입니다. if (Input.GetKeyDown(KeyCode.Space) && isJump == false) { anim.SetTrigger("isJUMP"); isJump = true; anim.SetBool("isIDLE", false); anim.SetBool("isRUN", false); rigidbody.velocity = new Vector2(rigidbody.velocity.x, jumpPower); } if(rigidbody.velocity.y == 0f ) { isJump = false; } JUMP 애니메이션만 다시 재생해보면 점프하는 동작이 나오긴 합니다. 강의를 듣던 중 GetComponent<SpriteRenderer>().sprite = JumpSprite; 이런 코드가 있던데 저는 작성하지 않은 상태입니다 혹시 이 코드가 점프가 안 되는 이유 중 하나인지 질문 남깁니다.
지금 Command 패턴강의 까지 수강중인데, 멀티쓰레드 파트에서 배웠던 락 처리는 Thread t 이렇게 만들고 시작해야 멀티쓰레드 환경으로 변하는걸로 알고있습니다 하지만 지금까지 보면 멀티쓰레드를 고려하여 락을 계속 추가해주고 있는데요 args.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted) 이것때문에 다른 쓰레드들이 풀리기 시작한건가요? 궁금합니다
// 인자로 받은 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만 트리거 타입이 되는지와 행동에 제약을 받지않는건지 궁금합니다.