From d0349c8d995143bc8c36a1052a1804b99f22185c Mon Sep 17 00:00:00 2001 From: Nico Date: Fri, 25 Jul 2025 01:05:46 -0700 Subject: [PATCH] Implement basics of enemy's attack states --- .../Prefabs/Characters/Enemies/Gobler.prefab | 2 +- Assets/Scenes/HomeTown.unity | 153 +++++++++++++++++- .../Scripts/Runtime/AI/EnemyManager/Gobler.cs | 58 ++++--- .../Player/Classes/MeleeFighterClass.cs | 22 ++- ProjectSettings/TagManager.asset | 2 + 5 files changed, 198 insertions(+), 39 deletions(-) diff --git a/Assets/Prefabs/Characters/Enemies/Gobler.prefab b/Assets/Prefabs/Characters/Enemies/Gobler.prefab index 917571e..b22e97d 100644 --- a/Assets/Prefabs/Characters/Enemies/Gobler.prefab +++ b/Assets/Prefabs/Characters/Enemies/Gobler.prefab @@ -354,7 +354,7 @@ NavMeshAgent: m_Enabled: 1 m_AgentTypeID: 0 m_Radius: 0.5 - m_Speed: 3.5 + m_Speed: 2.5 m_Acceleration: 30 avoidancePriority: 50 m_AngularSpeed: 120 diff --git a/Assets/Scenes/HomeTown.unity b/Assets/Scenes/HomeTown.unity index 40e2e33..3d2272c 100644 --- a/Assets/Scenes/HomeTown.unity +++ b/Assets/Scenes/HomeTown.unity @@ -195493,6 +195493,11 @@ Tilemap: e31: 0 e32: 0 e33: 1 +--- !u!4 &931976748 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 7536677720423648120, guid: d00d92ab204198c489965b31a298b234, type: 3} + m_PrefabInstance: {fileID: 2316236105731553357} + m_PrefabAsset: {fileID: 0} --- !u!1 &972432121 GameObject: m_ObjectHideFlags: 0 @@ -200846,6 +200851,75 @@ Transform: m_Children: [] m_Father: {fileID: 267352325} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1314549305 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1314549306} + - component: {fileID: 1314549307} + m_Layer: 0 + m_Name: HurtBox + m_TagString: SuperSpecialGem + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1314549306 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1314549305} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1341160148} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!70 &1314549307 +CapsuleCollider2D: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1314549305} + m_Enabled: 1 + serializedVersion: 3 + m_Density: 1 + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_ForceSendLayers: + serializedVersion: 2 + m_Bits: 4294967295 + m_ForceReceiveLayers: + serializedVersion: 2 + m_Bits: 4294967295 + m_ContactCaptureLayers: + serializedVersion: 2 + m_Bits: 4294967295 + m_CallbackLayers: + serializedVersion: 2 + m_Bits: 4294967295 + m_IsTrigger: 1 + m_UsedByEffector: 0 + m_CompositeOperation: 0 + m_CompositeOrder: 0 + m_Offset: {x: 0.003704071, y: 0.8854513} + m_Size: {x: 0.9819565, y: 1.7847328} + m_Direction: 0 --- !u!1 &1320165402 GameObject: m_ObjectHideFlags: 0 @@ -200970,7 +201044,7 @@ GameObject: - component: {fileID: 1341160151} m_Layer: 0 m_Name: SuperSpecialGem - m_TagString: Untagged + m_TagString: SuperSpecialGem m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 @@ -200991,6 +201065,7 @@ Transform: - {fileID: 972432122} - {fileID: 1273554248} - {fileID: 399472911} + - {fileID: 1314549306} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!50 &1341160149 @@ -201027,7 +201102,7 @@ CapsuleCollider2D: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1341160147} - m_Enabled: 1 + m_Enabled: 0 serializedVersion: 3 m_Density: 1 m_Material: {fileID: 0} @@ -202257,6 +202332,75 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2031312102} m_CullTransparentMesh: 1 +--- !u!1 &2059963722 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2059963723} + - component: {fileID: 2059963724} + m_Layer: 9 + m_Name: Hurtbox + m_TagString: PlayerHurtBox + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2059963723 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2059963722} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 931976748} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!70 &2059963724 +CapsuleCollider2D: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2059963722} + m_Enabled: 1 + serializedVersion: 3 + m_Density: 1 + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_ForceSendLayers: + serializedVersion: 2 + m_Bits: 4294967295 + m_ForceReceiveLayers: + serializedVersion: 2 + m_Bits: 4294967295 + m_ContactCaptureLayers: + serializedVersion: 2 + m_Bits: 4294967295 + m_CallbackLayers: + serializedVersion: 2 + m_Bits: 4294967295 + m_IsTrigger: 1 + m_UsedByEffector: 0 + m_CompositeOperation: 0 + m_CompositeOrder: 0 + m_Offset: {x: 0, y: -0.07961166} + m_Size: {x: 1.0406871, y: 2.3430235} + m_Direction: 0 --- !u!1 &2076322255 GameObject: m_ObjectHideFlags: 0 @@ -202410,7 +202554,10 @@ PrefabInstance: objectReference: {fileID: 0} m_RemovedComponents: [] m_RemovedGameObjects: [] - m_AddedGameObjects: [] + m_AddedGameObjects: + - targetCorrespondingSourceObject: {fileID: 7536677720423648120, guid: d00d92ab204198c489965b31a298b234, type: 3} + insertIndex: -1 + addedObject: {fileID: 2059963723} m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: d00d92ab204198c489965b31a298b234, type: 3} --- !u!1001 &3495966144891276242 diff --git a/Assets/Scripts/Runtime/AI/EnemyManager/Gobler.cs b/Assets/Scripts/Runtime/AI/EnemyManager/Gobler.cs index 598283d..cc14b1b 100644 --- a/Assets/Scripts/Runtime/AI/EnemyManager/Gobler.cs +++ b/Assets/Scripts/Runtime/AI/EnemyManager/Gobler.cs @@ -13,7 +13,7 @@ public class Gobler : Alive { [Header("Mechanics Attributes")] [SerializeField] public float ChaseDistance = 10f; [SerializeField] public float ChaseDistanceBuffer = 1f; - [SerializeField] public float AttackDistance = 1f; + [SerializeField] public float AttackDistance = 1.5f; [SerializeField] public float AttackMaskDiameter = 1f; @@ -107,7 +107,8 @@ public class Gobler : Alive { protected void SetPriorityState() { - if (CurrentState == State.AttackPlayer) return; + if (CurrentState == State.PrepareAttack) return; + if (CurrentState == State.FinalizeAttack) return; if (CurrentState == State.ChasePlayer) return; if (CurrentState == State.TakeDamage) return; if (CurrentState == State.Die) return; @@ -133,8 +134,8 @@ public class Gobler : Alive { case State.GoToCrystal: - PathAgent.SetDestination(CrystalPos); - + if (DistFromCrystal <= AttackDistance) + SetState(State.PrepareAttack); break; @@ -143,9 +144,9 @@ public class Gobler : Alive { case State.ChasePlayer: - if (DistFromPlayer > ChaseDistance + ChaseDistanceBuffer) - SetState(State.GoToCrystal); - if (DistFromPlayer > ChaseDistance + ChaseDistanceBuffer) + if (DistFromPlayer <= AttackDistance) + SetState(State.PrepareAttack); + else if (DistFromPlayer > ChaseDistance + ChaseDistanceBuffer) SetState(State.GoToCrystal); else PathAgent.SetDestination(PlayerPos); @@ -158,28 +159,20 @@ public class Gobler : Alive { case State.TakeDamage: - Rigidbody.linearVelocity = DirectionOfDamage * Knockback * 10 * InvincibilityLeft; - MaterialColorOverlay.SetFloat("_FlashAmount", InvincibilityLeft); - if (!IsInvincible) SetState(State.None); + if (IsInvincible) { + Rigidbody.linearVelocity = DirectionOfDamage * Knockback * 10 * InvincibilityLeft; + MaterialColorOverlay.SetFloat("_FlashAmount", InvincibilityLeft); + } + if (!IsFrameFrozen) SetState(State.None); break; case State.PrepareAttack: - MaterialColorOverlay.SetFloat("_FlashColor", 1 - InvincibilityLeft); - if (!IsInvincible) SetState(State.FinalizeAttack); + MaterialColorOverlay.SetFloat("_FlashAmount", 1 - FrameFreezeLeft); + if (!IsFrameFrozen) SetState(State.FinalizeAttack); break; - case State.FinalizeAttack: - Collider2D[] hits = Physics2D.OverlapCircleAll(MyPos + Vector2.down, 4); - foreach (var hit in hits) { - if (!hit.CompareTag("PlayerHurtBox")) continue; - - //GameObject parent = hit.transform.parent?.gameObject; - //Vector2 knockbakDirection = ActiveClass.HitBoxDraw.Directional ? PrevDirection : parent.transform.position - transform.position; - //EnemySpawnerData.EnemyMap[parent].Damage(ActiveClass.HitBoxDraw.Damage, knockbakDirection, ActiveClass.HitBoxDraw.Knockback); - } - break; } @@ -198,6 +191,7 @@ public class Gobler : Alive { case State.GoToCrystal: + PathAgent.SetDestination(CrystalPos); break; @@ -222,10 +216,24 @@ public class Gobler : Alive { case State.PrepareAttack: PathAgent.isStopped = true; + FrameFrozenUntil = Time.time + 1; MaterialColorOverlay.SetColor("_FlashColor", Color.purple); break; + case State.FinalizeAttack: + Collider2D[] hits = Physics2D.OverlapCircleAll(MyPos + Vector2.down, 4); + foreach (var hit in hits) { + if (!hit.CompareTag("PlayerHurtBox") || !hit.CompareTag("SuperSpecialGem")) continue; + Debug.Log("Player hit"); + //GameObject parent = hit.transform.parent?.gameObject; + //Vector2 knockbakDirection = ActiveClass.HitBoxDraw.Directional ? PrevDirection : parent.transform.position - transform.position; + //EnemySpawnerData.EnemyMap[parent].Damage(ActiveClass.HitBoxDraw.Damage, knockbakDirection, ActiveClass.HitBoxDraw.Knockback); + } + SetState(State.None); + break; + + case State.Die: UnsubscribeToEvent(); SpawnableEnemyInfo.DestroyEnemy(Owner, Enemies.Gobler); @@ -281,6 +289,11 @@ public class Alive { protected float InvincibilityLeft => Math.Max(0, InvincibleUntil - Time.time); protected float InvincibilityDuration = 0.1f; + public bool IsFrameFrozen => Time.time < FrameFrozenUntil; + protected float FrameFrozenUntil = 0f; + protected float FrameFreezeLeft => Math.Max(0, FrameFrozenUntil - Time.time); + protected float FrameFreezeDuration = 0.3f; + public event Action OnTakeDamage; public event Action OnDeath; public event Action OnHeal; @@ -295,6 +308,7 @@ public class Alive { CurrentHealth -= amount; CurrentHealth = Mathf.Max(CurrentHealth, 0); InvincibleUntil = Time.time + InvincibilityDuration; + FrameFrozenUntil = Time.time + FrameFreezeDuration; DamageTaken = amount; Knockback = knockback; diff --git a/Assets/Scripts/Runtime/Characters/Player/Classes/MeleeFighterClass.cs b/Assets/Scripts/Runtime/Characters/Player/Classes/MeleeFighterClass.cs index b7e855b..47013a1 100644 --- a/Assets/Scripts/Runtime/Characters/Player/Classes/MeleeFighterClass.cs +++ b/Assets/Scripts/Runtime/Characters/Player/Classes/MeleeFighterClass.cs @@ -81,13 +81,7 @@ public class MeleeFighterClass : ClassBase { case AttackState.BasicAttack3: if (ComboTimeElapsed <= 0.1) break; - ChangeState(AttackState.KineticSurgeRelease); - AttackAnimator?.MeleeResetKineticCharge(); - AttackAnimator?.MeleeKineticSurge(); - AnimationToPlay = "Attack1"; - //TextPopUp.SpawnFloatingText("KineticSurge", Color.red, 3); - AllowBladeVortex = ComboTimeElapsed <= 0.1f; - CreateHitBoxOffset(0, 5, 0.1f, 0.1f, (10 * DamageMultiplier) + DamageOffset, 20); + ChangeState(AttackState.KineticSurgeRelease, -1, -1, -1); break; @@ -215,11 +209,10 @@ public class MeleeFighterClass : ClassBase { if (ComboTimeElapsed < Cooldown) return; CurrentState = state; - Player.MoveSpeedDampener = (state == AttackState.None) ? 1 : 1 / decreasedSpeed; + if (decreasedSpeed != -1) Player.MoveSpeedDampener = (state == AttackState.None) ? 1 : 1 / decreasedSpeed; if (state == AttackState.None) return; - Cooldown = cooldown; - //TextPopUp.SpawnFloatingText(state.ToString(), Color.red, 3); - ComboResetTime = resetTime; + if (resetTime != -1) ComboResetTime = resetTime; + if (cooldown != -1) Cooldown = cooldown; LastComboTime = Time.time; RotateTowardsMouse(); @@ -246,12 +239,12 @@ public class MeleeFighterClass : ClassBase { case AttackState.BasicAttack1: AnimationToPlay = "Attack1"; - CreateHitBoxOffset(4, 4, 0.1f, 0.1f, (2 * DamageMultiplier) + DamageOffset, 10); + CreateHitBoxOffset(4, 4, 0.1f, 0.1f, (2 * DamageMultiplier) + DamageOffset, 20); break; case AttackState.BasicAttack2: AnimationToPlay = "Attack2"; - CreateHitBoxOffset(4, 4, 0.1f, 0.1f, (4 * DamageMultiplier) + DamageOffset, 15); + CreateHitBoxOffset(4, 4, 0.1f, 0.1f, (4 * DamageMultiplier) + DamageOffset, 20); break; case AttackState.BasicAttack3: @@ -260,7 +253,10 @@ public class MeleeFighterClass : ClassBase { break; case AttackState.KineticSurgeRelease: + AttackAnimator?.MeleeResetKineticCharge(); + AttackAnimator?.MeleeKineticSurge(); VfxKineticSurgeHandler.PlayAll(Player.transform.position); + CreateHitBoxOffset(0, 5, 0.1f, 0.1f, (10 * DamageMultiplier) + DamageOffset, 20); break; } } diff --git a/ProjectSettings/TagManager.asset b/ProjectSettings/TagManager.asset index 8f9cf97..6c44c34 100644 --- a/ProjectSettings/TagManager.asset +++ b/ProjectSettings/TagManager.asset @@ -9,6 +9,8 @@ TagManager: - HitBox - EnemyHitBox - EnemyHurtBox + - SuperSpecialGem + - PlayerHurtBox layers: - Default - TransparentFX