Godot refuses to set CharacterBody3D positions correctly unless you call ResetPhysicsInterpolation immediately after
99 lines
2.7 KiB
C#
99 lines
2.7 KiB
C#
using Chickensoft.AutoInject;
|
|
using Chickensoft.Introspection;
|
|
using Godot;
|
|
using Godot.Collections;
|
|
using System.Linq;
|
|
using Zennysoft.Game.Abstractions.Entity;
|
|
|
|
namespace Zennysoft.Game.Ma;
|
|
|
|
[Meta(typeof(IAutoNode))]
|
|
public partial class PatrolBehavior : Node3D, IBehavior
|
|
{
|
|
public override void _Notification(int what) => this.Notify(what);
|
|
|
|
[Export] private double _patrolSpeed { get; set; } = 100f;
|
|
|
|
[Export] private double _thinkTime { get; set; } = 0.8f;
|
|
|
|
[Export] private float _patrolRange { get; set; } = 5f;
|
|
|
|
[Export] private float _patrolTime { get; set; } = 10f;
|
|
|
|
private Timer _patrolTimer { get; set; } = default!;
|
|
private NavigationAgent3D _navigationAgent;
|
|
|
|
private Vector3 _homePosition;
|
|
|
|
public Vector3 HomePosition
|
|
{
|
|
get => _homePosition;
|
|
set
|
|
{
|
|
_homePosition = value;
|
|
_navigationAgent.TargetPosition = value;
|
|
}
|
|
}
|
|
|
|
[Signal] public delegate void OnVelocityComputedEventHandler(Vector3 safeVelocity);
|
|
|
|
public void OnReady()
|
|
{
|
|
_patrolTimer = new Timer() { WaitTime = _patrolTime };
|
|
_patrolTimer.Timeout += PatrolTimer_Timeout;
|
|
AddChild(_patrolTimer);
|
|
SetPhysicsProcess(false);
|
|
}
|
|
|
|
public void Init(NavigationAgent3D navigationAgent)
|
|
{
|
|
_navigationAgent = navigationAgent;
|
|
_navigationAgent.WaypointReached += NavigationAgent_WaypointReached;
|
|
}
|
|
|
|
private async void NavigationAgent_WaypointReached(Dictionary details)
|
|
{
|
|
await ToSignal(GetTree().CreateTimer(_thinkTime), "timeout");
|
|
}
|
|
|
|
public void StartPatrol()
|
|
{
|
|
SetPhysicsProcess(true);
|
|
_patrolTimer?.Start();
|
|
}
|
|
|
|
public void StopPatrol()
|
|
{
|
|
SetPhysicsProcess(false);
|
|
_patrolTimer?.Stop();
|
|
}
|
|
|
|
public void OnPhysicsProcess(double delta)
|
|
{
|
|
var nextVelocity = _navigationAgent.GetNextPathPosition();
|
|
var parent = GetParent() as Node3D;
|
|
var velocity = parent.GlobalPosition.DirectionTo(nextVelocity) * (float)_patrolSpeed * (float)delta;
|
|
EmitSignal(SignalName.OnVelocityComputed, velocity);
|
|
}
|
|
|
|
public void SetPatrolTarget()
|
|
{
|
|
var rng = new RandomNumberGenerator();
|
|
rng.Randomize();
|
|
var randomPointX = rng.RandfRange(-_patrolRange, _patrolRange);
|
|
var randomPointZ = rng.RandfRange(-_patrolRange, _patrolRange);
|
|
var patrolPoint = HomePosition + new Vector3(randomPointX, 0, randomPointZ);
|
|
var rid = NavigationServer3D.GetMaps().Single();
|
|
_navigationAgent.TargetPosition = NavigationServer3D.MapGetClosestPoint(rid, patrolPoint);
|
|
}
|
|
|
|
private void PatrolTimer_Timeout() => SetPatrolTarget();
|
|
|
|
public void OnExitTree()
|
|
{
|
|
_patrolTimer.Stop();
|
|
_patrolTimer.Timeout -= PatrolTimer_Timeout;
|
|
_navigationAgent.WaypointReached -= NavigationAgent_WaypointReached;
|
|
}
|
|
}
|