148 lines
4.8 KiB
C#
148 lines
4.8 KiB
C#
using System.Collections;
|
|
using UnityEngine;
|
|
using UnityHFSM; // Import the required classes for the state machine
|
|
|
|
namespace UnityHFSM.Samples.Sample3d
|
|
{
|
|
public class EnemyController : MonoBehaviour
|
|
{
|
|
private StateMachine fsm;
|
|
public float playerScanningRange = 4f;
|
|
public float ownScanningRange = 6f;
|
|
|
|
float DistanceToPlayer()
|
|
{
|
|
// This implementation is an example and may differ for your scene setup
|
|
Vector3 player = PlayerController.Instance.transform.position;
|
|
return Vector3.Distance(transform.position, player);
|
|
}
|
|
|
|
void MoveTowardsPlayer(float speed)
|
|
{
|
|
// This implementation is an example and may differ for your scene setup
|
|
Vector3 player = PlayerController.Instance.transform.position;
|
|
transform.position = Vector3.MoveTowards(transform.position, player, speed * Time.deltaTime);
|
|
}
|
|
|
|
void RotateAtSpeed(float speed)
|
|
{
|
|
transform.eulerAngles += new Vector3(0, 0, speed * Time.deltaTime);
|
|
}
|
|
|
|
IEnumerator SendData(CoState<string> state)
|
|
{
|
|
while (state.timer.Elapsed < 2)
|
|
{
|
|
RotateAtSpeed(100f);
|
|
// Wait until the next frame
|
|
yield return null;
|
|
}
|
|
|
|
while (state.timer.Elapsed < 4)
|
|
{
|
|
RotateAtSpeed(-100f);
|
|
yield return null;
|
|
}
|
|
|
|
state.timer.Reset();
|
|
// Because needsExitTime is true, we have to tell the FSM when it can
|
|
// safely exit the state
|
|
state.fsm.StateCanExit();
|
|
}
|
|
|
|
void Start()
|
|
{
|
|
fsm = new StateMachine();
|
|
|
|
// This is the nested state machine
|
|
StateMachine extractIntel = new StateMachine(needsExitTime: false);
|
|
fsm.AddState("ExtractIntel", extractIntel);
|
|
|
|
extractIntel.AddState("SendData",
|
|
onLogic: (state) =>
|
|
{
|
|
// When the state has been active for more than 5 seconds,
|
|
// notify the fsm that the state can cleanly exit
|
|
if (state.timer.Elapsed > 5)
|
|
state.fsm.StateCanExit();
|
|
|
|
// Make the enemy turn at 100 degrees per second
|
|
RotateAtSpeed(100f);
|
|
},
|
|
// This means the state won't instantly exit when a transition should happen
|
|
// but instead the state machine waits until it is given permission to change state
|
|
needsExitTime: true
|
|
);
|
|
|
|
// Unity Coroutines
|
|
// extractIntel.AddState("SendData", new CoState(
|
|
// this
|
|
// onLogic: SendData,
|
|
// needsExitTime: true
|
|
// ));
|
|
|
|
// Class based architecture
|
|
// extractIntel.AddState("SendData", new CustomSendData(this));
|
|
|
|
extractIntel.AddState("CollectData",
|
|
onLogic: (state) =>
|
|
{
|
|
if (state.timer.Elapsed > 5) state.fsm.StateCanExit();
|
|
},
|
|
needsExitTime: true
|
|
);
|
|
|
|
// A transition without a condition
|
|
extractIntel.AddTransition("SendData", "CollectData");
|
|
extractIntel.AddTransition("CollectData", "SendData");
|
|
extractIntel.SetStartState("CollectData");
|
|
|
|
fsm.AddState("FollowPlayer",
|
|
onLogic: (state) =>
|
|
{
|
|
MoveTowardsPlayer(1);
|
|
if (DistanceToPlayer() < ownScanningRange)
|
|
{
|
|
fsm.RequestStateChange("ExtractIntel");
|
|
}
|
|
}
|
|
);
|
|
|
|
fsm.AddState("FleeFromPlayer",
|
|
onLogic: (state) => MoveTowardsPlayer(-1)
|
|
);
|
|
|
|
// This configures the entry point of the state machine
|
|
fsm.SetStartState("FollowPlayer");
|
|
|
|
fsm.AddTransition(
|
|
"ExtractIntel",
|
|
"FollowPlayer",
|
|
(transition) => DistanceToPlayer() > ownScanningRange);
|
|
|
|
fsm.AddTransition(
|
|
"FollowPlayer",
|
|
"ExtractIntel",
|
|
(transition) => DistanceToPlayer() < ownScanningRange);
|
|
|
|
fsm.AddTransition(
|
|
"ExtractIntel",
|
|
"FleeFromPlayer",
|
|
(transition) => DistanceToPlayer() < playerScanningRange);
|
|
|
|
fsm.AddTransition(
|
|
"FleeFromPlayer",
|
|
"ExtractIntel",
|
|
(transition) => DistanceToPlayer() > playerScanningRange);
|
|
|
|
// Initialises the state machine and must be called before OnLogic() is called
|
|
fsm.Init();
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
fsm.OnLogic();
|
|
}
|
|
}
|
|
}
|