我目前正在开展一个项目,需要在 Unity 中为自行车运动系统实现路点导航。目标是在按下按钮时加速自行车并使其平稳地遵循预定义的路径点路径。
以下是我迄今为止所做工作的总结:
但是,我不确定实现航路点导航的最佳方法。我听说 NavMesh 对于 Unity 中的寻路很有用,但我不确定它是否是自行车运动系统的正确选择。
这是我编写的代码片段以供参考:
关注.AI
public class FollowAI : MonoBehaviour
{
MotoController motoController;
NavMeshAgent agent;
Transform tr;
public Transform target;
Transform targetPrev;
[SerializeField] Vector3 targetPoint;
bool targetVisible;
bool targetIsWaypoint;
VehicleWaypoint targetWaypoint;
public float followDistance;
bool close;
[Tooltip("Percentage of maximum speed to drive at")]
[Range(0, 1)]
public float speed = 1;
float initialSpeed;
float prevSpeed;
public float targetVelocity = -1;
[Tooltip("Mask for which objects can block the view of the target")]
public LayerMask viewBlockMask;
Vector3 dirToTarget; // Normalized direction to target
float lookDot; // Dot product of forward direction and dirToTarget
float steerDot; // Dot product of right direction and dirToTarget
public int MaxSteeringAngle = 45;
private void Start()
{
tr = transform;
motoController =GetComponent<MotoController>();
agent = GetComponent<NavMeshAgent>();
initialSpeed = speed;
InitializeTarget();
}
public void Follow()
{
if (target)
{
if (target != targetPrev)
{
InitializeTarget();
}
targetPrev = target;
// Is the target a waypoint?
targetIsWaypoint = target.GetComponent<VehicleWaypoint>();
// Can I see the target?
targetVisible = !Physics.Linecast(tr.position, target.position, viewBlockMask);
if (targetVisible || targetIsWaypoint)
{
targetPoint = target.position;
}
if (targetIsWaypoint)
{
// if vehicle is close enough to target waypoint, switch to the next one
if ((tr.position - target.position).sqrMagnitude <= targetWaypoint.radius * targetWaypoint.radius)
{
target = targetWaypoint.nextPoint.transform;
agent.SetDestination(target.position);
targetWaypoint = targetWaypoint.nextPoint;
}
}
close = (tr.position - target.position).sqrMagnitude <= Mathf.Pow(followDistance, 2) && !targetIsWaypoint;
dirToTarget = (targetPoint - tr.position).normalized;
Vector3 relativeVector = transform.InverseTransformPoint(targetPoint);
float SteeringAngle = (relativeVector.x / relativeVector.magnitude) * MaxSteeringAngle;
float angleToWaypoint = Vector3.Angle(tr.forward, dirToTarget);
float deadzoneAngle = 15f; // Adjust as needed
float leanInput = 0f;
if (angleToWaypoint > deadzoneAngle)
{
steerDot = -Mathf.Sign(steerDot) * (close ? 0 : 1);
motoController.turnLeanAmount = SteeringAngle;
}
else
{
steerDot = 0f; // No steering needed if within deadzone
}
// Set accel input
if ((tr.position - target.position).sqrMagnitude > Mathf.Pow(followDistance, 2))
{
motoController.customAccelerationAxis = 1f; // Start acceleration on touch down
motoController.rawCustomAccelerationAxis = 1f;
}
else
{
motoController.customAccelerationAxis = 0f; // Start acceleration on touch down
motoController.rawCustomAccelerationAxis = 0f;
}
motoController.customSteerAxis = steerDot;
motoController.customLeanAxis = leanInput;
}
}
public void InitializeTarget()
{
if(target)
{
targetWaypoint = target.GetComponent<VehicleWaypoint>();
if (targetWaypoint)
{
prevSpeed = targetWaypoint.speed;
}
}
}
}
移动输入控制器
public class MobileController : MonoBehaviour
{
MotoController motoController;
public MobileButtonHandler forward, backward, left, right, wheelie;
private bool isTouched;
private float touchStartTime;
private FollowAI followAI;
void Start()
{
motoController = GetComponent<MotoController>();
followAI = GetComponent<FollowAI>();
}
// Update is called once per frame
void FixedUpdate()
{
if(followAI != null)
{
if (forward.buttonPressed + backward.buttonPressed > 0)
{
followAI.Follow();
}
}
MobileInput(left.buttonPressed+right.buttonPressed, ref motoController.customSteerAxis, motoController.steerControls.x, motoController.steerControls.y, false);
MobileInput(forward.buttonPressed + backward.buttonPressed, ref motoController.customAccelerationAxis, 1, 1, false);
MobileInput(left.buttonPressed+right.buttonPressed, ref motoController.customLeanAxis, motoController.steerControls.x, motoController.steerControls.y, false);
MobileInput(forward.buttonPressed + backward.buttonPressed, ref motoController.rawCustomAccelerationAxis, 1, 1, true);
motoController.wheelieInput = System.Convert.ToBoolean(wheelie.buttonPressed);
}
private void SetBreaks()
{
MobileInput(-1, ref motoController.rawCustomAccelerationAxis, 1f, 0.1f, false);
MobileInput(-1, ref motoController.rawCustomAccelerationAxis, 1f, 0.1f, false);
}
float MobileInput(int instruction, ref float axis, float sensitivity, float gravity, bool isRaw)
{
var r = instruction*2;
var s = sensitivity;
var g = gravity;
var t = Time.unscaledDeltaTime;
if (isRaw)
axis = r;
else
{
if (r != 0)
axis = Mathf.Clamp(axis + r * s * t, -1f, 1f);
else
axis = Mathf.Clamp01(Mathf.Abs(axis) - g * t) * Mathf.Sign(axis);
}
return axis;
}
}
如果您对以下几点提出建议或见解,我将不胜感激:
任何帮助或指导将不胜感激。谢谢!
是否有任何替代方法或 Unity 包可能更适合此任务?
从您的要求来看,听起来 Splines 是 NavMesh 的更好替代方案(可从 Unity 2022 获得)
此视频是关于设置样条线的精彩介绍:https://www.youtube.com/watch?v=n-o2e4KxbW4
这里是一个帖子的链接,其中包含一些代码,用于沿着样条线自定义对象移动:https://stackoverflow.com/a/78316304/4640357