168 lines
6.4 KiB
C#
168 lines
6.4 KiB
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Sirenix.OdinInspector;
|
|
using Unity.AI.Navigation;
|
|
using UnityEngine;
|
|
using UnityEngine.AI;
|
|
using VOID.Generic.Objects.Traversable;
|
|
|
|
namespace VOID.Generic.Navigation
|
|
{
|
|
[RequireComponent(typeof(NavMeshSurface))]
|
|
[DefaultExecutionOrder(-1)]
|
|
public class NavigationManager : MonoBehaviour
|
|
{
|
|
public static NavigationManager current { get; private set; }
|
|
|
|
private const float NavMeshLinkThreshold = 0.01f;
|
|
|
|
// NavMesh data
|
|
private NavMeshSurface _navMeshSurface;
|
|
private NavMeshBuildSettings _navMeshBuildSettings;
|
|
|
|
// Cached links
|
|
private Dictionary<Vector2Int, Dictionary<Vector3, TraversableObject>> _linksPerTile;
|
|
private Dictionary<Vector3, bool> _isPositionStart;
|
|
|
|
private void Awake()
|
|
{
|
|
current = this;
|
|
_navMeshSurface = gameObject.GetComponent<NavMeshSurface>();
|
|
_navMeshBuildSettings = _navMeshSurface.GetBuildSettings();
|
|
_navMeshBuildSettings.preserveTilesOutsideBounds = true;
|
|
_linksPerTile = new Dictionary<Vector2Int, Dictionary<Vector3, TraversableObject>>();
|
|
_isPositionStart = new Dictionary<Vector3, bool>();
|
|
}
|
|
|
|
public void BuildNavMesh()
|
|
{
|
|
_navMeshSurface.BuildNavMesh();
|
|
}
|
|
|
|
[Button]
|
|
public void UpdateNavMesh(Bounds bounds)
|
|
{
|
|
bounds.extents += Vector3.one * GetTileSize();
|
|
NavMeshBuilder.UpdateNavMeshData(
|
|
_navMeshSurface.navMeshData,
|
|
_navMeshBuildSettings,
|
|
GetSources(bounds),
|
|
bounds
|
|
);
|
|
}
|
|
|
|
private static List<NavMeshBuildSource> GetSources(Bounds bounds)
|
|
{
|
|
var sources = new List<NavMeshBuildSource>();
|
|
NavMeshBuilder.CollectSources(
|
|
bounds,
|
|
LayerManager.Static | LayerManager.NavModifier,
|
|
NavMeshCollectGeometry.RenderMeshes,
|
|
0,
|
|
GetMarkups(),
|
|
sources
|
|
);
|
|
AppendSourcesWithModifierVolume(ref sources);
|
|
return sources;
|
|
}
|
|
|
|
private static List<NavMeshBuildMarkup> GetMarkups()
|
|
{
|
|
var markups = new List<NavMeshBuildMarkup>();
|
|
foreach (var modifier in NavMeshModifier.activeModifiers)
|
|
{
|
|
var markup = new NavMeshBuildMarkup();
|
|
markup.root = modifier.transform;
|
|
markup.overrideArea = modifier.overrideArea;
|
|
markup.area = modifier.area;
|
|
markup.ignoreFromBuild = modifier.ignoreFromBuild;
|
|
markups.Add(markup);
|
|
}
|
|
return markups;
|
|
}
|
|
|
|
private static void AppendSourcesWithModifierVolume(ref List<NavMeshBuildSource> sources)
|
|
{
|
|
foreach (var modifier in NavMeshModifierVolume.activeModifiers)
|
|
{
|
|
var mcenter = modifier.transform.TransformPoint(modifier.center);
|
|
var scale = modifier.transform.lossyScale;
|
|
var msize = new Vector3(modifier.size.x * Mathf.Abs(scale.x), modifier.size.y * Mathf.Abs(scale.y), modifier.size.z * Mathf.Abs(scale.z));
|
|
|
|
var src = new NavMeshBuildSource();
|
|
src.shape = NavMeshBuildSourceShape.ModifierBox;
|
|
src.transform = Matrix4x4.TRS(mcenter, modifier.transform.rotation, Vector3.one);
|
|
src.size = msize;
|
|
src.area = modifier.area;
|
|
sources.Add(src);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets cached TraversableObject at given point.
|
|
/// </summary>
|
|
/// <param name="pos">Check point for TraversableObject</param>
|
|
/// <param name="traversable">returns found traversable</param>
|
|
/// <param name="isReverse">returns bool if this is point is from link's end</param>
|
|
/// <returns>Boolean if traversable is found.</returns>
|
|
public bool GetCachedTraversable(Vector3 pos, out TraversableObject traversable, out bool isReverse)
|
|
{
|
|
var index = GetTileIndex(pos);
|
|
if (!_linksPerTile.ContainsKey(index))
|
|
{
|
|
traversable = null;
|
|
isReverse = false;
|
|
return false;
|
|
}
|
|
var foundPair = _linksPerTile[index].FirstOrDefault(pair =>
|
|
{
|
|
var position = pair.Key;
|
|
return Vector3.Distance(position, pos) < NavMeshLinkThreshold;
|
|
});
|
|
traversable = foundPair.Value;
|
|
isReverse = !_isPositionStart.GetValueOrDefault(foundPair.Key);
|
|
return traversable;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Cache both points of TraversableObject. Alias for:
|
|
/// <see cref="RegisterTraversable(Vector3, TraversableObject, bool)" />
|
|
/// </summary>
|
|
public void RegisterTraversable(TraversableObject traversable)
|
|
{
|
|
var startPosition = traversable.traversableData.linkStartPosition;
|
|
var endPosition = traversable.traversableData.linkEndPosition;
|
|
RegisterTraversable(startPosition, traversable, true);
|
|
RegisterTraversable(endPosition, traversable, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Cache given TraversableObject at given point.
|
|
/// </summary>
|
|
/// <param name="position">At which position, also it calculates into position index.</param>
|
|
/// <param name="traversable">Which traversable will be cached.</param>
|
|
/// <param name="isStart">Is given point start link? Otherwise it is end.</param>
|
|
public void RegisterTraversable(Vector3 position, TraversableObject traversable, bool isStart)
|
|
{
|
|
var index = GetTileIndex(position);
|
|
if (!_linksPerTile.ContainsKey(index))
|
|
_linksPerTile[index] = new Dictionary<Vector3, TraversableObject>();
|
|
_linksPerTile[index].Add(position, traversable);
|
|
_isPositionStart.Add(position, isStart);
|
|
}
|
|
|
|
private float GetTileSize()
|
|
{
|
|
return _navMeshBuildSettings.tileSize * _navMeshBuildSettings.voxelSize;
|
|
}
|
|
|
|
private Vector2Int GetTileIndex(Vector3 pos)
|
|
{
|
|
return new Vector2Int()
|
|
{
|
|
x = Mathf.FloorToInt(pos.x / GetTileSize()),
|
|
y = Mathf.FloorToInt(pos.z / GetTileSize()),
|
|
};
|
|
}
|
|
}
|
|
} |