Unity通过旋转将车辆与道路对齐

时间:2021-07-15 21:47:39

标签: c# unity3d vector quaternions

目标: 创建具有类似于 Mario Kart 8 的反重力模式或 f-0 的属性的车辆;在极端非水平表面上骑行的能力。

期望的行为: 除非按下/移动拇指杆或箭头键,否则车辆不应转弯;它必须保持直线运动,除了相对于相机视野的滚动和垂直曲率。

实际行为: 车辆将缓慢(有时很快)脱离直线并保持弯曲,直到轨道停止弯曲。如果放置在面向内的圆柱体中并沿径向行驶,车辆将开始向全局 +z 或全局 +y 弯曲。

(没有错误信息)

我尝试过的: -将 transform.up 设置为表面法线,然后围绕法线作为轴旋转 - 使用 quaternion.euler(0, [desired angle], 0) 然后 fromToRotation

对齐和旋转代码:

transform.rotation = Quaternion.Euler(0, rotation, 0);
Quaternion tilt = Quaternion.FromToRotation(Vector3.up, localUp);
transform.rotation = tilt * transform.rotation;
transform.position += velocity * 1.1f;

整个脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using PhysicsExtensions;
using UnityEngine.Rendering.PostProcessing;

public class Cart : MonoBehaviour
{
Kartphysics inputActions;
public new Transform camera, camTarget, camTargetDrift, Visual;
public ShipType shipType;
public AudioSource Vroom;
public Vector3 localUp = Vector3.up;
Vector3 velocity, camUp, followPos;
public AnimationCurve SteeringControl;
public float steerAmount;
float rotation, rollTarget, roll, fovDifference, vroomPitch = 0, flameLength = 0;
public float normalFov, speedFov, Velocity, rollAmount, speedFactor, forcedAcceleration;
public GameObject[] ships;
public FlamingTrail[] flames;
public PostProcessProfile ppp;
Vector2 JoystickVal;
ChromaticAberration ca;
LensDistortion ld;
Vector3 LastForward;

private void Start()
{
    switch (shipType)
    {
        case ShipType.Carrier:
            {
                ships[0].SetActive(true);
                break;
            }
        case ShipType.Ram:
            {
                ships[1].SetActive(true);
                break;
            }
    }
    ca = ppp.GetSetting<ChromaticAberration>();
    ld = ppp.GetSetting<LensDistortion>();
}
private void Update()
{
    UpdateVisuals();
    UpdateCamera();
    Velocity = velocity.magnitude;
}
private void FixedUpdate()
{
    UpdateKart();
}
void SetFlames(float length)
{
    for(int i = 0; i < flames.Length; i++)
    {
        flames[i].length = length;
    }
}
void UpdateVisuals()
{
    ca.intensity.value = Mathf.Clamp01(forcedAcceleration) * 2;
    ld.intensity.value = Mathf.Lerp(0, -70f, Mathf.Clamp(forcedAcceleration, 0, 1));
    SetFlames(flameLength);
    Vroom.pitch = Mathf.Lerp(Vroom.pitch, vroomPitch, (speedFactor * 0.01f) * 10);
    Visual.position = Vector3.Lerp(Visual.position, transform.position, (speedFactor * 0.01f) * 30);
    Visual.rotation = Quaternion.Lerp(Visual.rotation, transform.rotation, (speedFactor * 0.01f) * 15);
    
}
void UpdateCamera()
{
    fovDifference = speedFov - normalFov;
    Camera.main.fieldOfView = speedFov - (fovDifference * (1 / Mathf.Clamp(velocity.magnitude + 1, 0, Mathf.Infinity)));
    camUp = Vector3.Lerp(camUp, localUp.normalized, (speedFactor * 0.01f) * (Vector3.Distance(camera.position, Vector3.Lerp(camTarget.position, camTargetDrift.position, transform.InverseTransformDirection(velocity).x)) + 3));
    camera.rotation = Quaternion.Slerp(camera.rotation, Quaternion.LookRotation((transform.position - (transform.right * transform.InverseTransformDirection(velocity).x * 5) + transform.up) - camera.position, camUp), (speedFactor * 0.01f) * 13);
    camera.position = Vector3.Lerp(camera.position, Vector3.Lerp(camTarget.position, camTargetDrift.position, transform.InverseTransformDirection(velocity).x), (speedFactor * 0.01f) * Vector3.Distance(camera.position, camTarget.position) * 20);
}
void UpdateKart()
{
    JoystickVal = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
    if (Input.GetAxis("Submit") > 0.5)
        JoystickVal = new Vector2(JoystickVal.x, 1);
    if (Input.GetAxis("Cancel") > 0.5)
        JoystickVal = new Vector2(JoystickVal.x, -1);
    if (JoystickVal.magnitude > 1)
    {
        JoystickVal.Normalize();
    }
    JoystickVal *= (speedFactor * 0.01f) * 0.2f;
    JoystickVal /= Mathf.Clamp(velocity.magnitude, 0.7f, Mathf.Infinity);
    velocity += ((transform.forward * JoystickVal.y) / Mathf.Clamp(Mathf.Abs(transform.InverseTransformDirection(velocity).x), 0.7f, Mathf.Infinity));
    
    rollTarget = Mathf.Clamp01(SteeringControl.Evaluate(velocity.magnitude)) * JoystickVal.x * rollAmount;
    roll = Mathf.MoveTowards(roll, rollTarget, (speedFactor * 0.01f) * 4);
    velocity -= localUp * (speedFactor * 0.01f) * 0.7f;
    velocity /= 1 + ((speedFactor * 0.01f) / 8);
    RaycastHit hit;
    CircleCastHit circleHit;
    if (Physics.Raycast(transform.position + transform.up, -transform.up + (velocity / 1), out hit))
    {

        if (hit.distance < 4)
        {
            transform.position -= hit.normal.normalized * (speedFactor * 0.01f);
            localUp = Vector3.MoveTowards(localUp, hit.normal, (speedFactor * 0.01f) * 9);
            if (hit.distance < 1.2f)
            {
                flameLength = Velocity * 2;
                if (hit.collider.tag == "SpeedPanel")
                    forcedAcceleration = 3f;
                rotation += SteeringControl.Evaluate(velocity.magnitude * 0.7f) * JoystickVal.x * (speedFactor * 0.01f) * 100 * steerAmount;
                transform.position += hit.normal.normalized * (1 - hit.distance);
                vroomPitch = velocity.magnitude * 1.5f;
                velocity += ((transform.forward * ((JoystickVal.y * 1.3f) + (forcedAcceleration / 100))) / Mathf.Clamp(Mathf.Abs(transform.InverseTransformDirection(velocity).x), 0.7f, Mathf.Infinity));
                rotation += SteeringControl.Evaluate((speedFactor * 0.01f) * velocity.magnitude * 50) * JoystickVal.x * 0.3f;
                velocity /= 1 + ((speedFactor * 0.01f));
                velocity -= transform.right * transform.InverseTransformDirection(velocity).x * 0.2f;
                Vector3 force = (hit.normal * -transform.InverseTransformDirection(velocity).y / Mathf.Clamp(hit.distance - 0.1f, 0.5f, 2)) * 1.1f;
                if (force.magnitude > 1)
                    force = force.normalized * 1;
                force /= 8;
                velocity += force;

            }
            else
            {
                vroomPitch = 0;
                flameLength = Mathf.MoveTowards(flameLength, 0, 0.03f);
            }

        }
        else
        {
            localUp = Vector3.MoveTowards(localUp, Vector3.up, (speedFactor * 0.01f) * 1.2f);
            vroomPitch = 0;
            transform.forward = velocity.normalized;
            flameLength = Mathf.MoveTowards(flameLength, 0, 0.03f);
        }
    }
    else
    {
        localUp = Vector3.MoveTowards(localUp, Vector3.up, (speedFactor * 0.01f) * 2);
        vroomPitch = 0;
        flameLength = Mathf.MoveTowards(flameLength, 0, 0.03f);
    }
    if (PhysicsII.CircleCast(transform.position + (transform.up * 0.5f), localUp, 0.7f, 8, out circleHit))
    {
        Debug.DrawRay(circleHit.nearestHit().point, circleHit.nearestHit().normal, Color.red, 0.1f);
        Debug.Log("HIT");
        velocity += (transform.position + (transform.up * 0.5f) - circleHit.nearestHit().point) / 3;
        if (circleHit.nearestHit().distance < 0.4f)
            velocity += (transform.position + (transform.up * 0.5f) - circleHit.nearestHit().point) / 7;
        if (circleHit.nearestHit().distance < 0.14f)
            velocity += (transform.position + (transform.up * 0.5f) - circleHit.nearestHit().point) / 7;

    }
    if(Physics.Raycast(transform.position + (transform.up * 0.8f) - velocity, velocity , out hit))
    {
        if(hit.distance < Velocity * 2)
            velocity /= 1 + ((speedFactor * 0.01f) * 2f);
        if (hit.distance < Velocity * 1.2f)
            velocity = Vector3.Reflect(velocity, hit.normal);
    }
    forcedAcceleration = Mathf.MoveTowards(forcedAcceleration, 0, 0.1f);

    transform.rotation = Quaternion.Euler(0, rotation, 0);
    Quaternion tilt = Quaternion.FromToRotation(Vector3.up, localUp);
    transform.rotation = tilt * transform.rotation;
    transform.position += velocity * 1.1f;

}
public enum ShipType
{
    Carrier = 0,
    Ram = 1
}
}

1 个答案:

答案 0 :(得分:0)

这是部分答案,因为我目前无法在我这边测试它是否有效。看起来“滚动”还没有用于任何事情(它是否意味着以某种方式改变变换的局部?)所以我不确定。

首先,您可以使用 transform.forwardtransform.right 来实现这些目的,而不是保持浮动旋转来跟踪车辆的转向方式,并根据每个-框架基础:

void UpdateKart()
{

    Vector3 newForward = transform.forward;  
    float turnAmount = 0f;

    // ...
            if (hit.distance < 1.2f)
            {
                flameLength = Velocity * 2;
                if (hit.collider.tag == "SpeedPanel")
                    forcedAcceleration = 3f;
                turnAmount += SteeringControl.Evaluate(velocity.magnitude * 0.7f) 
                        * JoystickVal.x * (speedFactor * 0.01f) * 100 * steerAmount;
                transform.position += hit.normal.normalized * (1 - hit.distance);
                vroomPitch = velocity.magnitude * 1.5f;
                velocity += /* too long to bother formatting */
                turnAmount += SteeringControl.Evaluate((speedFactor * 0.01f) 
                        * velocity.magnitude * 50) * JoystickVal.x * 0.3f;

    // ...

然后当您实际调整旋转时,将绕局部向上轴的转动量应用到当前局部向前方向。最后,设置变换的旋转,使其新的局部向上为 localUp,并使其局部向前保持尽可能恒定的方向(叉积后跟 Quaternion.LookRotation 可用于此目的):

    forcedAcceleration = Mathf.MoveTowards(forcedAcceleration, 0, 0.1f);

    Vector3 turnedForward = Quaternion.AngleAxis(turnAmount - 180, localUp) * 
            transform.forward; 

    Vector3 newRight = Vector3.Cross(turnedForward, localUp);
    if (newRight == Vector3.zero)
    {
       /* Ambiguous situation - maybe kart landed with its nose directly in the 
          direction of localUp or opposite direction. Possible solution: use 
          velocity as previous forward direction and recalculate, using a random 
          direction if that doesn't work 
       */

       newRight = Vector3.Cross(velocity, localUp); 

       if (newRight == Vector3.zero) 
       {    
           newRight = Vector3.ProjectOnPlane(Random.insideUnitSphere, 
                   localUp).normalized;
       }
    }

    Vector3 newForward = Vector3.Cross(newRight, localUp);

    transform.rotation = Quaternion.LookRotation(newForward, localUp);
    transform.position += velocity * 1.1f;

您看到结果的原因是 FromToRotation 会为您提供“最小”旋转,将一个向量移动到另一个向量。但是你更关心的是一个轮换,这将使本地前锋接近调整前的状态(很难解释为什么这不是一回事)。因此,Cross 的东西。

正如我所说,这只是为了让您更亲近的部分解决方案。但是,这可能就是您所需要的。请在评论中告诉我您的想法。

相关问题