Recreate enemy management

Remove separate states
Will contain all states in one type of enemy management script
Will use enemy manager to update states of each enemy and spawning
This commit is contained in:
Nico 2025-07-09 02:04:27 -07:00
parent 0107e33438
commit 4fe5fb18e8
21 changed files with 1401 additions and 826 deletions

View File

@ -184,8 +184,7 @@ GameObject:
m_Component:
- component: {fileID: 8417872790204748578}
- component: {fileID: 5305176699257483480}
- component: {fileID: 1850048545141446338}
m_Layer: 7
m_Layer: 0
m_Name: HitBox
m_TagString: EnemyHitBox
m_Icon: {fileID: 0}
@ -244,33 +243,6 @@ CapsuleCollider2D:
m_Offset: {x: 0, y: 0}
m_Size: {x: 1, y: 2}
m_Direction: 0
--- !u!50 &1850048545141446338
Rigidbody2D:
serializedVersion: 5
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6288550889223568635}
m_BodyType: 1
m_Simulated: 1
m_UseFullKinematicContacts: 0
m_UseAutoMass: 0
m_Mass: 1
m_LinearDamping: 0
m_AngularDamping: 0.05
m_GravityScale: 1
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_Interpolate: 0
m_SleepingMode: 1
m_CollisionDetection: 0
m_Constraints: 0
--- !u!1 &6411951171763069002
GameObject:
m_ObjectHideFlags: 0
@ -280,7 +252,6 @@ GameObject:
serializedVersion: 6
m_Component:
- component: {fileID: 243343966221896818}
- component: {fileID: 4923938647755769837}
- component: {fileID: 3140493390153182690}
- component: {fileID: 9193586887117248369}
m_Layer: 0
@ -308,24 +279,6 @@ Transform:
- {fileID: 8417872790204748578}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &4923938647755769837
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6411951171763069002}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a5c89455bac58d943a2e1e110d37a24d, type: 3}
m_Name:
m_EditorClassIdentifier:
WalkState: {fileID: 11400000, guid: 5cd5bb6e94e95cf4c961f0433390f59f, type: 2}
ChaseState: {fileID: 11400000, guid: 71a5afe255167e14cad7bb413bdef532, type: 2}
AttackState: {fileID: 11400000, guid: 0b9cc3e63ee0125479639c05b89977f8, type: 2}
DamagedState: {fileID: 0}
ChaseDistance: 7
AttackDistance: 1
--- !u!70 &3140493390153182690
CapsuleCollider2D:
m_ObjectHideFlags: 0

File diff suppressed because it is too large Load Diff

View File

@ -1,40 +0,0 @@
using System;
using Unity.IO.LowLevel.Unsafe;
using UnityEngine;
namespace AI.Base {
abstract public class StateManager : MonoBehaviour {
protected IState CurrentState;
virtual protected void Update() {
CurrentState?.Tick();
IState next = GetNextState();
if (next != null && next != CurrentState) {
CurrentState.Stop();
CurrentState = next;
CurrentState.Start();
}
}
abstract protected IState GetNextState();
}
public class StateNode : ScriptableObject, IState {
protected Transform Owner;
virtual public void Initialize(Transform ownerTransform) {
Owner = ownerTransform;
}
virtual public StateNode InitializeCopy(Transform ownerTransform) {
Owner = ownerTransform;
return ScriptableObject.CreateInstance(this.GetType()) as StateNode;
}
virtual public void Start() {}
virtual public void Stop() {}
virtual public void Tick() {}
virtual public IState GetNextState() => this;
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using Unity.IO.LowLevel.Unsafe;
using UnityEngine;
namespace AI.Base {
public class EnemyStateManager : MonoBehaviour {
public static EnemyStateManager Instance;
public static List<GoblerStateManager> Goblers = new List<GoblerStateManager>();
public static Action UpdateTick;
public static Action GizmoTick;
public void Awake() {
Instance = this;
}
public void Update() {
UpdateTick?.Invoke();
}
public static void OnDrawGizmos() {
GizmoTick?.Invoke();
}
}
}

View File

@ -0,0 +1,179 @@
using AI.Base;
using NUnit.Framework.Internal.Execution;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using UnityEngine;
public class GoblerStateManager {
[Header("Mechanics Attributes")]
[SerializeField] public float ChaseDistance = 10f;
[SerializeField] public float ChaseDistanceBuffer = 1f;
[SerializeField] public float AttackDistance = 1f;
[SerializeField] public float AttackMaskDiameter = 1f;
[Header("Enemy Attributes")]
[SerializeField] public float Attack = 10f;
[SerializeField] public float Speed = 10f;
[SerializeField] public float Health = 10f;
[SerializeField] public float Energy = 10f;
private Player Player { get { return Player.Instance; } }
private Transform PlayerTransform { get { return Player.transform; } }
private Vector2 PlayerPos { get { return PlayerTransform.position; } }
private GameObject Owner;
private Transform MyTransform { get { return Owner.transform; } }
private Vector2 MyPos { get { return MyTransform.position; } set { MyTransform.position = value; } }
public GoblerStateManager(GameObject owner) {
Owner = owner;
EnemySpawnerManager.UpdateTick += Update;
EnemySpawnerManager.GizmoTick += OnDrawGizmos;
}
public float DistFromPlayer { get; private set; }
public float DistFromCrystal { get; private set; }
public State CurrentState;
public enum State {
None,
GoToCrystal,
AttackCrystal,
ChasePlayer,
AttackPlayer,
Damaged,
Die
}
protected void SetPriorityState() {
if (CurrentState == State.ChasePlayer) return;
if (CurrentState == State.Damaged) return;
if (CurrentState == State.Die) return;
if (DistFromPlayer > ChaseDistance) return;
SetState(State.ChasePlayer);
}
private bool IsUpdating;
protected void Update() {
if (Owner == null) return;
if (IsUpdating) return;
IsUpdating = true;
DistFromPlayer = Vector2.Distance(MyPos, PlayerPos);
SetPriorityState();
switch (CurrentState) {
case State.None:
break;
case State.GoToCrystal:
break;
case State.AttackCrystal:
break;
case State.ChasePlayer:
MyPos += (PlayerPos - MyPos).normalized * Speed * Time.deltaTime;
if (DistFromPlayer >= ChaseDistance + ChaseDistanceBuffer)
SetState(State.AttackCrystal);
else if (DistFromPlayer < 1)
SetState(State.Die);
break;
case State.AttackPlayer:
break;
case State.Damaged:
break;
case State.Die:
EnemySpawnerManager.RemoveGobler(this);
SetState(State.None);
break;
}
IsUpdating = false;
}
protected void SetState(State newState) {
CurrentState = newState;
switch (CurrentState) {
case State.None:
break;
case State.GoToCrystal:
break;
case State.AttackCrystal:
break;
case State.ChasePlayer:
break;
case State.AttackPlayer:
break;
case State.Damaged:
break;
case State.Die:
break;
}
}
protected void OnDrawGizmos() {
if (Owner == null) return;
DrawChaseDistance();
DrawAttackDistance();
DrawAttackReach();
}
private void DrawChaseDistance() {
Gizmos.color = Color.green;
Vector2 center = MyPos;
Gizmos.DrawWireSphere(center, ChaseDistance);
}
private void DrawAttackDistance() {
//Gizmos.color = CurrentState == (IState)AttackState ? Color.red : Color.green;
//Vector2 center = this.transform.position;
//Gizmos.DrawWireSphere(center, AttackDistance);
}
private void DrawAttackReach() {
//if (CurrentState != (IState)AttackState) return;
Gizmos.color = Color.red;
Vector2 direction2D = (PlayerPos - MyPos).normalized;
Vector2 point2D = MyPos + direction2D * AttackDistance;
Gizmos.DrawWireSphere(point2D, AttackMaskDiameter);
}
}

View File

@ -1,10 +0,0 @@
using AI.Base;
using System;
using UnityEngine;
[CreateAssetMenu(menuName = "AI/Enemy/EnemyAttackState")]
public class EnemyAttackState : StateNode {
override public void Start() { Debug.Log("Entering Idle"); }
override public void Tick() { }
override public void Stop() { Debug.Log("Exiting Idle"); }
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 8dc550eabcee2e84e94ae0bc898d72b4

View File

@ -1,33 +0,0 @@
using AI.Base;
using System;
using UnityEngine;
[CreateAssetMenu(menuName = "AI/Enemy/EnemyChaseState")]
public class EnemyChaseState : StateNode {
public float Speed = 3f;
private Transform Player;
override public StateNode InitializeCopy(Transform ownerTransform) {
var copy = ScriptableObject.CreateInstance(this.GetType()) as EnemyChaseState;
copy.Owner = ownerTransform;
copy.Speed = Speed;
return copy;
}
override public void Start() {
Player = GameObject.FindGameObjectWithTag("Player").transform;
Debug.Log("Entering Chase");
}
override public void Tick() {
if (Owner != null && Player != null) {
Vector2 dir = (Player.position - Owner.position).normalized;
Owner.position += (Vector3)dir * Speed * Time.deltaTime;
}
}
override public void Stop() {
Debug.Log("Exiting Chase");
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 5f2962a1dddd6484e913c62893d7e512

View File

@ -1,10 +0,0 @@
using AI.Base;
using System;
using UnityEngine;
[CreateAssetMenu(menuName = "AI/Enemy/EnemyDamagedState")]
public class EnemyDamagedState : StateNode {
override public void Start() { Debug.Log("Entering Idle"); }
override public void Tick() { }
override public void Stop() { Debug.Log("Exiting Idle"); }
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 7457688d54e7e8342b034cc3b7fd589e

View File

@ -1,71 +0,0 @@
using AI.Base;
using UnityEngine;
public class EnemyStateManager : StateManager {
[Header("States")]
[SerializeField] public StateNode WalkState;
[SerializeField] public StateNode ChaseState;
[SerializeField] public StateNode AttackState;
[SerializeField] public StateNode DamagedState;
[Header("Attributes")]
[SerializeField] private float ChaseDistance = 10f;
[SerializeField] private float AttackDistance = 1f;
private Transform Player;
protected void Start() {
Player = GameObject.FindGameObjectWithTag("Player")?.transform;
WalkState = ScriptableObject.CreateInstance(WalkState.GetType()) as StateNode;
WalkState = WalkState.InitializeCopy(this.transform);
ChaseState = ChaseState.InitializeCopy(this.transform);
//DamagedState = DamagedState.InitializeCopy(this.transform);
AttackState = AttackState.InitializeCopy(this.transform);
AttackState.Initialize(this.transform);
CurrentState = WalkState;
CurrentState.Start();
}
protected void InitializeStateNode(StateNode node){
node = ScriptableObject.CreateInstance(node.GetType()) as StateNode;
node.Initialize(this.transform);
}
override protected IState GetNextState() {
if (Player == null)
return CurrentState;
float dist = Vector2.Distance(transform.position, Player.position);
if (dist < AttackDistance)
return AttackState.GetNextState();
else if (dist < ChaseDistance)
return ChaseState.GetNextState();
else
return WalkState.GetNextState();
}
private void OnDrawGizmos() {
DrawChaseDistance();
DrawAttackDistance();
}
private void DrawChaseDistance() {
Gizmos.color = CurrentState == (IState)ChaseState ? Color.yellow : Color.green;
Vector2 center = this.transform.position;
Gizmos.DrawWireSphere(center, ChaseDistance);
}
private void DrawAttackDistance() {
Gizmos.color = CurrentState == (IState)AttackState ? Color.red : Color.green;
Vector2 center = this.transform.position;
Gizmos.DrawWireSphere(center, AttackDistance);
}
}

View File

@ -1,12 +0,0 @@
using AI.Base;
using System;
using UnityEngine;
[CreateAssetMenu(menuName = "AI/Enemy/EnemyWalkState")]
public class EnemyWalkState : StateNode {
override public void Start() { Debug.Log("Entering Idle"); }
override public void Tick() { }
override public void Stop() { Debug.Log("Exiting Idle"); }
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: e82366a8b141d1c4587e07f13e902f29

View File

@ -8,6 +8,8 @@ using UnityEngine.UI;
[SelectionBase]
public class Player : MonoBehaviour {
public static Player Instance { get; private set; }
[Header("Asset/Prefab")]
[SerializeField] public BuilderManager Builder;
@ -74,6 +76,7 @@ public class Player : MonoBehaviour {
private enum Directions { Left, Right, Up, Down }
void Awake() {
Instance = this;
Builder = GetComponent<BuilderManager>();
VfxDashHandler = new VfxHandlerBase(VfxDash, 5, 5);

View File

@ -0,0 +1,48 @@
using AI.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UIElements;
public class EnemySpawnerManager : MonoBehaviour {
public static EnemySpawnerManager Instance;
[SerializeField] public GameObject GoblerPreFab;
public static Dictionary<GoblerStateManager, GameObject> Goblers = new Dictionary<GoblerStateManager, GameObject>();
public static Action UpdateTick;
public static Action GizmoTick;
public static int MaxGoblers = 1;
public class Gobler {
public GameObject Object;
public GoblerStateManager Manager;
public Gobler(GameObject obj, GoblerStateManager manager) {
Object = obj;
Manager = manager;
}
}
public void Awake() {
Instance = this;
}
public static void RemoveGobler(GoblerStateManager goblerManager){
Destroy(Goblers[goblerManager]);
Goblers.Remove(goblerManager);
}
public void Update() {
if (Goblers.Count() < MaxGoblers){
var gobler = Instantiate(GoblerPreFab, this.transform.position, Quaternion.identity);
Goblers.Add(new GoblerStateManager(gobler), gobler);
}
UpdateTick?.Invoke();
}
public static void OnDrawGizmos() {
GizmoTick?.Invoke();
}
}

View File

@ -1,23 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
public class SpawnManager : MonoBehaviour {
public static bool IsPaused { get; private set; }
[SerializeField] private GameObject pauseMenuUI;
void Update() {
if (Input.GetKeyDown(KeyCode.Escape))
TogglePause();
}
public void TogglePause() {
IsPaused = !IsPaused;
Time.timeScale = IsPaused ? 0f : 1f;
AudioListener.pause = IsPaused;
pauseMenuUI.SetActive(IsPaused);
}
}