Files
sbox-public/engine/Sandbox.Engine/Scene/Components/Navigation/NavMeshArea.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

167 lines
4.2 KiB
C#

using ProtoBuf;
using Sandbox.Engine.Resources;
using Sandbox.Navigation;
using Sandbox.Navigation.Generation;
using Sandbox.Volumes;
namespace Sandbox;
/// <summary>
/// An area that influences the NavMesh generation.
/// Areas can be used to block off parts of the NavMesh.
/// Static areas have almost no performance overhead.
/// Moving areas at runtime will have an impact on performance if done excessively.
/// </summary>
[Expose]
[Title( "NavMesh - Area" )]
[Category( "Navigation" )]
[Icon( "crop" )]
[EditorHandle( "materials/gizmo/navmeshagent.png" )]
[Alias( "NavAgent" )]
public class NavMeshArea : VolumeComponent, Component.ExecuteInEditor
{
/// <summary>
/// Whether navmesh generation in this area will be completely disabled.
/// </summary>
[Property, MakeDirty]
public bool IsBlocker { get; set; } = true;
/// <summary>
/// The NavMesh area definition to apply to this area.
/// </summary>
[Property, MakeDirty]
public NavMeshAreaDefinition Area { get; set; }
/// <summary>
/// The collider this area's shape is based on.
/// In almost every case, you will want to use a trigger collider for this.
/// </summary>
[Property, Hide]
[Obsolete( "Use the SceneVolume property inherited from VolumeComponent instead" )]
public Collider LinkedCollider
{
get => _linkedCollider;
set
{
_linkedCollider = value;
}
}
protected override async Task OnLoad()
{
if ( _linkedCollider != null )
{
await ConvertColliderToSceneVolumeLoadTask();
}
}
private Collider _linkedCollider;
private NavMeshAreaData _navMeshArea;
protected override void OnDirty()
{
UpdateNavMeshArea();
}
internal override void OnEnabledInternal()
{
UpdateNavMeshArea();
Transform.OnTransformChanged += OnTransformChanged;
base.OnEnabledInternal();
}
internal override void OnDisabledInternal()
{
Transform.OnTransformChanged -= OnTransformChanged;
RemoveNavMeshArea();
base.OnDisabledInternal();
}
/// <summary>
/// Legacy support:
/// Convert the linked collider to a SceneVolume and clear the reference
/// </summary>
private async Task ConvertColliderToSceneVolumeLoadTask()
{
if ( _linkedCollider == null )
return;
SceneVolume volume = new SceneVolume();
// Check for specific collider types
if ( _linkedCollider is SphereCollider sphereCollider )
{
volume.Type = SceneVolume.VolumeTypes.Sphere;
volume.Sphere = new Sphere( sphereCollider.Center, sphereCollider.Radius );
}
else if ( _linkedCollider is BoxCollider boxCollider )
{
volume.Type = SceneVolume.VolumeTypes.Box;
volume.Box = BBox.FromPositionAndSize( boxCollider.Center, boxCollider.Scale );
}
else if ( _linkedCollider is CapsuleCollider capsuleCollider )
{
volume.Type = SceneVolume.VolumeTypes.Capsule;
volume.Capsule = new Capsule( capsuleCollider.Start, capsuleCollider.End, capsuleCollider.Radius );
}
else if ( _linkedCollider is PlaneCollider planeCollider )
{
volume.Type = SceneVolume.VolumeTypes.Box;
volume.Box = BBox.FromPositionAndSize( planeCollider.Center, new Vector3( planeCollider.Scale, 8f ) );
}
else
{
// Wait until physics are ready.
await GameTask.DelayRealtimeSeconds( 0.5f );
volume.Type = SceneVolume.VolumeTypes.Box;
volume.Box = _linkedCollider.LocalBounds;
}
SceneVolume = volume;
_linkedCollider = null;
}
private void UpdateNavMeshArea()
{
// Create area if it doesn't exist
if ( _navMeshArea == null )
{
_navMeshArea = new NavMeshAreaData();
_navMeshArea.IsBlocked = IsBlocker;
_navMeshArea.AreaDefinition = Area;
Scene.NavMesh.AddSpatiaData( _navMeshArea );
}
// Update the properties
_navMeshArea.Volume = SceneVolume;
_navMeshArea.IsBlocked = IsBlocker;
_navMeshArea.AreaDefinition = Area;
_navMeshArea.LocalBounds = SceneVolume.GetBounds();
_navMeshArea.WorldBounds = _navMeshArea.LocalBounds.Transform( WorldTransform );
_navMeshArea.Transform = WorldTransform;
_navMeshArea.HasChanged = true;
}
/// <summary>
/// Removes the current NavMeshArea
/// </summary>
private void RemoveNavMeshArea()
{
if ( _navMeshArea != null )
{
_navMeshArea.IsPendingRemoval = true;
_navMeshArea = null;
}
}
/// <summary>
/// Called when the transform changes
/// </summary>
private void OnTransformChanged()
{
UpdateNavMeshArea();
}
}