AI/ActionSystem/AgentActionController.cs
using System;
using System.Text.Json.Serialization;
namespace HC3;
public sealed class AgentActionController : Component
{
/// <summary>
/// The agent
/// </summary>
[Property] public Agent Agent { get; set; }
/// <summary>
/// Random
/// </summary>
public Random Random { get; } = new Random( Guid.NewGuid().GetHashCode() );
/// <summary>
/// A list of actions for this agent
/// </summary>
public IEnumerable<AgentAction> Actions { get; private set; }
[Sync( SyncFlags.FromHost )]
private AgentAction _current { get; set; }
[Property, ReadOnly, JsonIgnore]
public AgentAction CurrentAction => _current;
protected override void OnStart()
{
Actions = GetComponentsInChildren<AgentAction>();
// Stagger initial tick so 500 agents don't all score on the same frame
NextTick = Random.Float( 0f, FixedRate );
}
RealTimeUntil NextTick;
const float FixedRate = 0.25f;
public void Tick()
{
if ( !Networking.IsHost )
return;
if ( !NextTick )
return;
if ( Actions == null )
return;
if ( !Agent.Controller.IsGrounded )
return;
NextTick = FixedRate;
var best = default(AgentAction);
float bestScore = float.MinValue;
foreach ( var a in Actions )
{
var score = a.ScoreInternal();
if ( score > bestScore )
{
bestScore = score;
best = a;
}
}
if ( best != _current )
{
ClearAction();
_current = best;
_current?.StartInternal();
}
if ( _current != null )
{
var result = _current.TickInternal();
if ( result != AgentAction.Status.Running )
{
ClearAction();
}
}
}
public void ClearAction()
{
if ( !_current.IsValid() )
return;
// safety so things can call ClearAction from inside an Action's StopAction without trouble
var c = _current;
_current = null;
c.StopInternal();
}
}