Hi everyone, I have a tiny training project I’ ve done some time ago where I tried to simulate a couple of states just to get to know how a FSM works. The only problem is that some states don’t work as they should. For example when generating some children instead of creating one instance of the prefab, sometimes it creates like a bunch at once. even though it should only create one.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class MakeLove : State
{
// Reference type leitet von eine Klasse ab (Unity)
// Object kann man refernzieren (grün angezeigt), value type (blau angezeigt) nicht.
private CowFSM target = null;
private NavMeshAgent agent = null;
float minMakeLoveDistance;
public MakeLove(CowFSM _cowFSM, float _minMakeLoveDistance) : base(_cowFSM)
{
if (agent == null)
{
// out for value type, ref for reference type
if (cowFSM.TryGetComponent(out NavMeshAgent _agent))
{
agent = _agent;
}
{
}
}
minMakeLoveDistance = _minMakeLoveDistance;
cowFSM.CurrentCowState = CowFSM.CowState.MAKELOVE;
OnEnter();
}
public override void OnEnter()
{
base.OnEnter();
target = FindBestCow();
}
public override void OnUpdate()
{
if (target == null)
{
target = FindBestCow();
return;
}
if(Vector3.Distance(target.transform.position, cowFSM.transform.position) <= minMakeLoveDistance)
{
cowFSM.MakeLove(target);
target = null;
return;
}
if (agent.isActiveAndEnabled && !agent.hasPath && !agent.pathPending)
{
if (agent.isActiveAndEnabled && !agent.SetDestination(target.transform.position))
{
if (NavMesh.SamplePosition(target.transform.position, out NavMeshHit hit, float.PositiveInfinity,
NavMesh.AllAreas))
{
_ = agent.SetDestination(hit.position);
}
}
}
}
public override void CheckTransition()
{
if (cowFSM.Hunger > 20)
{
cowFSM.ChangeState(new Eat(cowFSM, cowFSM.feedDistance, 10));
}
}
private CowFSM FindBestCow()
{
List<CowFSM> allCows = CowFSM.AllCows;
float closestDistance = float.PositiveInfinity;
CowFSM closestCow = null;
float temp;
foreach (var cow in allCows)
{
if (cow == cowFSM || !(cow.CurrentState is MakeLove))
{
continue;
}
temp = Vector3.SqrMagnitude(cow.transform.position - cowFSM.transform.position);
if (temp < closestDistance)
{
closestDistance = temp;
closestCow = cow;
}
}
return closestCow;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CowFSM : MonoBehaviour
{
// Für Debug.
public CowState CurrentCowState;
public enum CowState
{
IDLE, HUNGRY, MAKELOVE
}
public static List<CowFSM> AllCows = new List<CowFSM>();
[Header("General")]
[SerializeField] private float maxHunger;
[SerializeField] private float hungerRate;
[SerializeField] private GameObject cowPrefab;
[SerializeField] private float hunger;
// Hier ändert sich die aktive State dauerhaft in der Update.
private State currentState;
[Header("Idle")]
[SerializeField] private float rotateSpeed;
[Header("Eat")]
public float feedDistance;
[Header("MakeLove")]
public float mateDistance;
public float Hunger
{
get => hunger;
set
{
hunger = value;
if (hunger >= maxHunger)
{
Debug.Log(cowPrefab.gameObject.name + " is dead.");
//Destroy(gameObject);
gameObject.SetActive(false);
}
}
}
public State CurrentState => currentState;
private void Awake()
{
AllCows.Add(this);
currentState = new Idle(this, rotateSpeed);
}
private void OnDestroy()
{
AllCows.Remove(this);
}
private void Update()
{
// Every second cowFSM gets 2 points of hunger.
Hunger += hungerRate * Time.deltaTime;
currentState.CheckTransition();
currentState.OnUpdate();
}
public void ChangeState(State _state)
{
currentState.OnExit();
currentState = _state;
currentState.OnEnter();
}
public void EatFood(Food _food)
{
Debug.Log(cowPrefab.gameObject.name + " Has eaten the food");
// hunger -= _food.RefillFood(Hunger);
Hunger -= _food.RefillFood(Hunger);
}
public void MakeLove(CowFSM _cow)
{
Hunger += 5;
_cow.Hunger += 5;
// To solve a problem where the children would spawn frozen till death.
CowFSM child = Instantiate(cowPrefab, transform.position, transform.rotation).GetComponent<CowFSM>();
if (!AllCows.Contains(child))
{
AllCows.Add(child);
}
child.currentState = new Idle(child, child.rotateSpeed);
// This fixes a problem where the children would spawn with Hunger at 10 and stay frozen till death.
child.Hunger = 15;
}
private void OnDrawGizmos()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.transform.position, mateDistance);
Gizmos.color = Color.blue;
Gizmos.DrawWireSphere(transform.transform.position, feedDistance);
}
}