namespace Pathfinding.Graphs.Grid.Rules { using Pathfinding.Jobs; using Unity.Jobs; using Unity.Collections; using Unity.Burst; using UnityEngine; using Unity.Mathematics; /// /// Applies penalty based on the slope of the surface below the node. /// /// This is useful if you for example want to discourage agents from walking on steep slopes. /// /// The penalty applied is equivalent to: /// /// /// penalty = curve.evaluate(slope angle in degrees) * penaltyScale /// /// /// [Open online documentation to see images] /// /// See: grid-rules (view in online documentation for working links) /// [Pathfinding.Util.Preserve] public class RuleAnglePenalty : GridGraphRule { public float penaltyScale = 10000; public AnimationCurve curve = AnimationCurve.Linear(0, 0, 90, 1); NativeArray angleToPenalty; public override void Register (GridGraphRules rules) { if (!angleToPenalty.IsCreated) angleToPenalty = new NativeArray(32, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); for (int i = 0; i < angleToPenalty.Length; i++) { angleToPenalty[i] = Mathf.Max(0, curve.Evaluate(90.0f * i / (angleToPenalty.Length - 1)) * penaltyScale); } rules.AddJobSystemPass(Pass.BeforeConnections, context => { new JobPenaltyAngle { angleToPenalty = angleToPenalty, up = context.data.up, nodeNormals = context.data.nodes.normals, penalty = context.data.nodes.penalties, }.Schedule(context.tracker); }); } public override void DisposeUnmanagedData () { if (angleToPenalty.IsCreated) angleToPenalty.Dispose(); } [BurstCompile(FloatMode = FloatMode.Fast)] public struct JobPenaltyAngle : IJob { public Vector3 up; [ReadOnly] public NativeArray angleToPenalty; [ReadOnly] public NativeArray nodeNormals; public NativeArray penalty; public void Execute () { float4 up = new float4(this.up.x, this.up.y, this.up.z, 0); for (int i = 0; i < penalty.Length; i++) { float4 normal = nodeNormals[i]; if (math.any(normal)) { float angle = math.acos(math.dot(normal, up)); // Take the dot product to find out the cosinus of the angle it has // Add penalty based on the angle from a precalculated array float x = angle*(angleToPenalty.Length - 1)/math.PI; int ix = (int)x; float p1 = angleToPenalty[math.max(ix, 0)]; float p2 = angleToPenalty[math.min(ix + 1, angleToPenalty.Length - 1)]; penalty[i] += (uint)math.lerp(p1, p2, x - ix); } } } } } }