Reputation: 1
So for a game I'm developing I'm using a inheritance based finite state system for enemy and boss AI. However I'm coming across a few issues with its implementation.
The base state class is inherited by each state, i.e. AirPatrol state inherits from "EnemyState". However, the AirPatrol class has variables such as "moveSpeed", "aggroDistance" etc (that EnemyState does not have) that I want to access from outside the class, so I made them public. In the enemy's state machine class (Where switching states is managed etc) I have a public "currentState" variable which keeps the current state.
But, if I try to reference currentState.moveSpeed I get a "does not contain definition" error. I understand why this does not work as not every EnemyState will have the "moveSpeed" variable, but is there a simple/reliable way to do these kind of variable modifications without having to add the speed variable to the base EnemyState class?
Here are the scripts:
SECURITY DRONE AI CLASSS (HERE IS MY PROBLEM)
public class SecurityDroneAI : EnemyController
{
[Header("Movement")]
public float patrolSpeed = 3.0f;
public float aggroSpeed = 5.0f;
[SerializeField] float aggroDistance = 20f;
public float acceleration = 2f;
[Header("Pathfinding")]
public float pathUpdateDelay = 1.0f;
public List<Vector2> patrolPositions = new List<Vector2>();
public float nextWaypointDistance = 2;
Animator anim;
Rigidbody2D rb;
private void Start()
{
rb = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
SetNextState(new AirPatrol());
nextState.speed = 1; // HERE IS MY PROBLEM >:(
}
private void Update()
{
anim.SetBool("seeking", currentState.GetType() == typeof(AirPatrol));
float rot = -rb.velocity.x * 3;
}
}
ENEMY STATE CLASS
public class EnemyState
{
public Rigidbody2D rb;
public Animator anim;
public Transform transform;
public EnemyController controller;
public virtual void OnEnter(EnemyController parentController)
{
controller = parentController;
rb = controller.GetComponent<Rigidbody2D>();
anim = controller.GetComponentInChildren<Animator>();
transform = controller.transform;
}
public virtual void OnUpdate()
{
}
public virtual void OnFixedUpdate()
{
}
public virtual void OnLateUpdate()
{
}
public virtual void OnExit()
{
}
}
AIR PATROL CLASS
public class AirPatrol : EnemyState
{
public List<Vector2> patrolPositions = new List<Vector2>(); // List of positions to patrol between
public float speed = 3.0f; // Movement speed
public float acceleration = 2f; // Acceleration
public float pathUpdateDelay = 1.0f; // How often to update the path
public float nextWaypointDistance = 1.5f; // How far the patroller should check for the next waypoint
int positionIndex; // Index of the target position in patrolPositions
int currentWaypoint = 0; // Index of the current target waypoint on the current path
float timeSinceUpdatedPath; // Time since last generated path
Path path; // Current path
public Seeker seeker; // Seeker component for A* pathfinding
public override void OnEnter(EnemyController parentController)
{
base.OnEnter(parentController);
positionIndex = 0;
timeSinceUpdatedPath = 0;
}
public override void OnUpdate()
{
base.OnUpdate();
if (timeSinceUpdatedPath <= 0) // Update path if delayed enough
{
UpdatePath();
timeSinceUpdatedPath = pathUpdateDelay;
}
timeSinceUpdatedPath += Time.deltaTime;
if (path == null) { return; }
if (currentWaypoint >= path.vectorPath.Count) // At end of the path
{
positionIndex = positionIndex + 1 >= patrolPositions.Count ? 0 : positionIndex + 1; // Iterate to next patrol point
UpdatePath();
return;
}
MoveAlongPath();
}
private void MoveAlongPath()
{
Vector2 direction = (path.vectorPath[currentWaypoint] - transform.position).normalized;
rb.velocity = Vector3.Lerp(rb.velocity, direction * speed, acceleration * Time.deltaTime);
float distance = Vector2.Distance(rb.position, path.vectorPath[currentWaypoint]);
if (distance < nextWaypointDistance)
{
currentWaypoint++;
}
}
private void UpdatePath()
{
if (!seeker.IsDone()) { return; }
seeker.StartPath(transform.position, patrolPositions[positionIndex], SetPath);
}
private void SetPath(Path p)
{
if (p.error) { return; }
path = p;
currentWaypoint = 0;
}
}
ENEMY CONTROLLER CLASS
public class EnemyController : MonoBehaviour
{
public EnemyState mainStateType;
public EnemyState currentState;
public EnemyState nextState;
private void Update()
{
if (nextState != null)
{
SetState(nextState);
}
if (currentState != null) { currentState.OnUpdate(); }
}
private void LateUpdate()
{
if (currentState != null) { currentState.OnLateUpdate(); }
}
private void FixedUpdate()
{
if (currentState != null) { currentState.OnFixedUpdate(); }
}
private void SetState(EnemyState newState)
{
nextState = null;
if (currentState != null)
{
currentState.OnExit();
}
currentState = newState;
currentState.OnEnter(this);
}
public void SetNextState(EnemyState newState)
{
if (newState != null)
{
nextState = newState;
}
}
}
Any kind of help would be appreciated! :)
Upvotes: -1
Views: 53
Reputation: 1
Haha for anyone else needing an answer I thought of one almost immediately after posing this, funny how the world works!
The solution was to cast the new state to a specific AirPatrol variable then modify that. Better ways to do this are appreciated if someone could teach me!
Changed Scripts:
private void Start()
{
rb = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
AirPatrol patrolState = new AirPatrol();
patrolState.speed = 1;
SetNextState(patrolState);
}
Upvotes: 0