Hello, please help, cause I’m starting to lose my mind.
I followed a video on youtube
to create the FSM. This is a second try (different guy - Table Flip Games) on creating such system, so I think I kind of understood how it all works, so then I can properly modify it.
This code I have here, is kind of a mashup of what I learned from Table Flip Games initially, then created the structure using the video I linked, filled in the blanks with the things I learned from Table Flip Games and then modified it different things. Although EnemyPatrolState is mostly the same since Table Flip Games video (I deleted unnecessary parts)
Short description of what I want to achieve - I need a FSM for an enemy, with states such as: IDLE, PATROL, SUSPICIOUS, CAUGHT
The last two are irrelevant to my problem currently, I only explain for the sake of giving you some context.
The system itself seems to work just fine, it switches to IDLE, and then to PATROL.
But here’s the catch - there are PatrolPoints (GameObjects with a PatrolPoint script containing a piece of code that is not needed at this moment, but the script is necessary to be able to reference the objects in the Inspector). While writting this, I just thought that maybe that’s the point - why not just make a list of GameObjects, I don’t really need to reference the script, but the GameObject that will be the target of Enemy GameObject.
So, here’s the FSM code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyStateManager : MonoBehaviour
{
EnemyBaseState currentState;
public EnemyIdleState IdleState = new EnemyIdleState();
public EnemyPatrolState PatrolState = new EnemyPatrolState();
public EnemySuspiciousState SuspiciousState = new EnemySuspiciousState();
public EnemyFollowingState EnemyFollowingState = new EnemyFollowingState(); //TODO: change name
public GameObject[] _patrolPoints;
//[SerializeField]
//List<EnemyBaseState> _validStates;
// Start is called before the first frame update
void Start()
{
//starting state for FSM
currentState = IdleState;
//"this" is a reference to the context(this EXACT Monobehaviour script
currentState.EnterState(this);
}
private void OnCollisionEnter(Collision collision)
{
currentState.OnCollisionEnter(this, collision);
}
// Update is called once per frame
void Update()
{
currentState.UpdateState(this);
}
public void SwitchState(EnemyBaseState state)
{
currentState = state;
state.EnterState(this);
}
public GameObject[] PatrolPoints
{
get
{
return _patrolPoints;
}
}
}
Here’s the BaseState code:
using UnityEngine;
using UnityEngine.AI;
public abstract class EnemyBaseState
{
public abstract void EnterState(EnemyStateManager state);
public abstract void UpdateState(EnemyStateManager state);
public abstract void OnCollisionEnter(EnemyStateManager state, Collision collision);
protected NavMeshAgent _navMeshAgent;
protected Enemy _enemy;
protected EnemyStateManager _esm;
protected FieldOfView _fieldOfView;
protected Vector3 enemyTransform;
protected bool _suspicious = false;
protected LayerMask obstructionMask;
protected Vector3 directionToTarget;
protected float distanceToTarget;
}
I don’t think the IDLE STATE code is necessary since it works fine, so here’s PATROL STATE code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyPatrolState : EnemyBaseState
{
GameObject[] patrolPoints;
int patrolPointIndex;
[SerializeField]
float _totalDuration = 2f;
float _timer;
public override void EnterState(EnemyStateManager state)
{
//EnteredState = false;
if (state)
{
//Grab and store patrol points
patrolPoints = _esm.PatrolPoints;
if (patrolPoints == null || patrolPoints.Length == 0)
{
Debug.LogError("PatrolState: Failed to grab patrol points from the NPC");
}
else
{
if (patrolPointIndex < 0)
{
//Doing this only the first time to choose a patrol point, because otherwise it would have completely random path -> thus OnEnable contains patrolPointIndex = -1
patrolPointIndex = UnityEngine.Random.Range(0, patrolPoints.Length);
}
else
{
patrolPointIndex = (patrolPointIndex + 1) % patrolPoints.Length;
}
SetDestination(patrolPoints[patrolPointIndex]);
Debug.Log("Patrolpoint" + patrolPointIndex);
}
}
}
public override void UpdateState(EnemyStateManager state)
{
//code unnecessary, as currently I only have a problem with EnterState method
//I have a logic prepared, and some basic functionality code, but for now, I just need the Enemy to go to PatrolPoint
}
*/
public override void OnCollisionEnter(EnemyStateManager enemy, Collision collision)
{
}
private void SetDestination(GameObject destination)
{
if (_navMeshAgent != null && destination != null)
{
_navMeshAgent.SetDestination(destination.transform.position);
}
}
}
I guess what I’m missing here is actually that reference. It seems my way of doing it, by
public GameObject[] PatrolPoints
{
get
{
return _patrolPoints;
}
}
and then assigning it in EnemyPatrolState, via “patrolPoints = _esm.PatrolPoints;”
while _esm is declared in EnemyBaseState - isn’t really what Unity needs.
I tried doing that in a separate script called Enemy, but I got the same results.
I think I provided all the necessary stuff, to speed up the response time, maybe I forgot about something but at this point my brain can’t think straight…


