I have something working, might not be the best solution but it seems work a bit better than what I had before. The only issue I’m running into is that if I place a destination that is at a similar height to where the AI agent is it won’t go along the path. You can see it in this video below. Can you think of anything that might cause this? I’ve linked the script for reference. I use the same setting in the inspector that you can see in the video.
using UnityEngine;
using System.Collections.Generic;
namespace Pathfinding {
/// <summary>
/// Movement script for curved worlds.
/// This script inherits from AIPath, but adjusts its movement plane every frame using the ground normal.
/// </summary>
public class AIWallCrawler : AIPath {
public float smoothness = 5f;
public int raysNb = 8;
public float raysEccentricity = 0.2f;
public float outerRaysOffset = 2f;
public float innerRaysOffset = 25f;
private Vector3 vel;
private Vector3 lastVelocity;
private Vector3 lastPosition;
private Vector3 forward;
private Vector3 upward;
private Quaternion lastRot;
private Vector3[] pn;
protected override void Start () {
base.Start();
//movementPlane = new Util.SimpleMovementPlane(rotation);
vel = new Vector3();
forward = transform.forward;
upward = transform.up;
lastRot = transform.rotation;
}
protected override void OnUpdate (float dt) {
base.OnUpdate(dt);
UpdateMovementPlane();
}
static Vector3[] GetClosestPoint(Vector3 point, Vector3 forward, Vector3 up, float halfRange, float eccentricity, float offset1, float offset2, int rayAmount)
{
Vector3[] res = new Vector3[2] { point, up };
Vector3 right = Vector3.Cross(up, forward);
float normalAmount = 1f;
float positionAmount = 1f;
Vector3[] dirs = new Vector3[rayAmount];
float angularStep = 2f * Mathf.PI / (float)rayAmount;
float currentAngle = angularStep / 2f;
for(int i = 0; i < rayAmount; ++i)
{
dirs[i] = -up + (right * Mathf.Cos(currentAngle) + forward * Mathf.Sin(currentAngle)) * eccentricity;
currentAngle += angularStep;
}
foreach (Vector3 dir in dirs)
{
RaycastHit hit;
Vector3 largener = Vector3.ProjectOnPlane(dir, up);
Ray ray = new Ray(point - (dir + largener) * halfRange + largener.normalized * offset1 / 100f, dir);
Debug.DrawRay(ray.origin, ray.direction);
if (Physics.SphereCast(ray, 0.01f, out hit, 2f * halfRange))
{
res[0] += hit.point;
res[1] += hit.normal;
normalAmount += 1;
positionAmount += 1;
}
ray = new Ray(point - (dir + largener) * halfRange + largener.normalized * offset2 / 100f, dir);
Debug.DrawRay(ray.origin, ray.direction, Color.green);
if (Physics.SphereCast(ray, 0.01f, out hit, 2f * halfRange))
{
res[0] += hit.point;
res[1] += hit.normal;
normalAmount += 1;
positionAmount += 1;
}
}
res[0] /= positionAmount;
res[1] /= normalAmount;
return res;
}
/// <summary>Find the world position of the ground below the character</summary>
protected override void UpdateMovementPlane () {
vel = (smoothness * velocity + (transform.position - lastPosition)) / (1f + smoothness);
if (vel.magnitude < 0.00025f)
vel = lastVelocity;
lastPosition = transform.position;
lastVelocity = vel;
if (!reachedDestination)
{
pn = GetClosestPoint(transform.position, transform.forward, transform.up, 0.5f, 0.1f, 30, -30, 4);
upward = pn[1];
Vector3[] pos = GetClosestPoint(transform.position, transform.forward, transform.up, 0.5f, raysEccentricity, innerRaysOffset, outerRaysOffset, raysNb);
transform.position = Vector3.Lerp(lastPosition, pos[0], 1f / (1f + smoothness));
forward = vel.normalized;
Quaternion q = Quaternion.LookRotation(forward, upward);
transform.rotation = Quaternion.Lerp(lastRot, q, 1f / (1f + smoothness));
lastRot = transform.rotation;
movementPlane = new Util.SimpleMovementPlane(lastRot);
if (rvoController != null) rvoController.movementPlane = movementPlane;
}
}
}
}