我的控制和物理组件/脚本已挂载到车体上。像路面轮子和齿轮这样的部件已拖入对应的框中。油箱控制基本上是数据,而油箱驱动物理是实际使用的机制。问题在于,我油门打开时油箱不想移动,但转速随转矩和齿轮一起上升,这说不通。当前的力是应用在轮子触地位置的。谢谢你的帮助。我可以分享3D模型,它是我在Blender中制作的,已经准备好用于游戏。

下面是我的油箱驱动物理脚本:

using UnityEngine;

using UnityEngine.SceneManagement;

[RequireComponent(typeof(Rigidbody))]

[RequireComponent(typeof(TankController))]

public class TankDrivePhysics : MonoBehaviour
{
private TankController config;
private Rigidbody rb;
private float currentThrottleInput = 0.0f;
private float currentSteerInput = 0.0f;
private bool isBrakeEngaged = false;
private int currentGearIndex = 0; // Start in Gear 1 (Index 0)
private bool isReversing = false;
private float currentEngineRpm = 1000f;
private float shiftCooldownTimer = 0f;
private float clutchEngagementFactor = 1.0f;

void Start()
{
    config = GetComponent<TankController>();
    rb = GetComponent<Rigidbody>();
    if (rb != null)
    {
        rb.mass = config.tankWeightTons * 1000f;
        rb.linearDamping = config.linearDamping;
        rb.angularDamping = config.airAngularDamping;
        rb.useGravity = true;
        rb.centerOfMass = config.centerOfMassOffset;
    }
    string currentScene = SceneManager.GetActiveScene().name.ToLower();
    if (currentScene.Contains("test"))
    {
        config.isTestDriving = true;
    }
}

void Update()
{
    if (!config.isTestDriving) return;
    if (Input.GetKeyDown(KeyCode.B))
    {
        isBrakeEngaged = !isBrakeEngaged;
    }
}

void FixedUpdate()
{
    if (!config.isTestDriving)
    {
        config.CurrentSpeedKmh = 0f;
        config.CurrentEngineRpm = config.engine.idleRpm;
        config.CurrentTorqueNm = 0f;
        config.engine.calculatedHorsepower = 0f;
        return;
    }

    if (rb != null)
    {
        rb.centerOfMass = config.centerOfMassOffset;
        rb.linearDamping = config.linearDamping;
    }

    if (shiftCooldownTimer > 0f)
    {
        shiftCooldownTimer -= Time.fixedDeltaTime;
        clutchEngagementFactor = 0.8f;
    }
    else
    {
        clutchEngagementFactor = 1.0f;
    }

    Vector3 forwardMoveDir = transform.TransformDirection(new Vector3(-1f, 0f, 0f)).normalized;
    float forwardVel = Vector3.Dot(rb.linearVelocity, forwardMoveDir);
    config.CurrentSpeedKmh = Mathf.Abs(forwardVel) * 3.6f;
    float rawAxis = Input.GetAxis("Vertical");
    float rawTurn = Input.GetAxis("Horizontal");
    GetPlayerInput(rawAxis, rawTurn);

    if (Mathf.Abs(rawTurn) < 0.05f && Mathf.Abs(currentSteerInput) < 0.01f)
    {
        Vector3 angVel = rb.angularVelocity;
        angVel.y = Mathf.Lerp(angVel.y, 0f, 15f * Time.fixedDeltaTime);
        rb.angularVelocity = angVel;
    }

    if (isBrakeEngaged)
    {
        if (Mathf.Abs(rawAxis) < 0.01f)
        {
            Vector3 vel = rb.linearVelocity;
            vel.x = Mathf.Lerp(vel.x, 0f, 10f * Time.fixedDeltaTime);
            vel.z = Mathf.Lerp(vel.z, 0f, 10f * Time.fixedDeltaTime);
            rb.linearVelocity = vel;
            rb.angularVelocity = Vector3.zero;
        }
        else
        {
            Vector3 brakeResistance = -forwardMoveDir * forwardVel * config.brakeDecelerationRate * rb.mass;
            rb.AddForce(brakeResistance, ForceMode.Force);
        }
    }

    UpdateDrivetrainSimulation(forwardVel, rawAxis);
    bool isAtRest = (Mathf.Abs(rawAxis) < 0.01f && Mathf.Abs(rawTurn) < 0.05f && config.CurrentSpeedKmh <= 1.0f && !isBrakeEngaged);
    if (isAtRest)
    {
        Vector3 vel = rb.linearVelocity;
        vel.x = Mathf.Lerp(vel.x, 0f, 6f * Time.fixedDeltaTime);
        vel.z = Mathf.Lerp(vel.z, 0f, 6f * Time.fixedDeltaTime);
        rb.linearVelocity = vel;
        rb.angularVelocity = Mathf.Lerp(rb.angularVelocity.magnitude, 0f, 6f * Time.fixedDeltaTime) * rb.angularVelocity.normalized;
        UpdateHingeJointSprings(forwardVel);
        CalculateWheelRotations(forwardSpeedMps: forwardVel);
        return;
    }

    ApplyDifferentialTrackForces(rawAxis, rawTurn, forwardMoveDir, forwardVel);
    ApplyTrackGrip();
    ApplyEnvironmentalResistance(forwardMoveDir, forwardVel);
    UpdateHingeJointSprings(forwardVel);
    CalculateWheelRotations(forwardSpeedMps: forwardVel);
}

void GetPlayerInput(float rawAxis, float rawTurn)
{
    if (config.invertSteering) rawTurn = -rawTurn;
    if (Mathf.Abs(rawTurn) < 0.05f)
    {
        rawTurn = 0f;
    }
    currentThrottleInput = rawAxis;
    currentSteerInput = rawTurn;
}

float GetEngineTorqueAtRpm(float rpm)
{
    float peakRpm = Mathf.Min(config.engine.peakTorqueRpm, config.engine.redlineLimitRpm - 200f);
    float maxTq = config.engine.maxTorqueNm;
    if (rpm <= config.engine.idleRpm) return maxTq * 0.6f;
    if (rpm >= config.engine.redlineLimitRpm) return maxTq * 0.3f;
    float normalizedDist = Mathf.Abs(rpm - peakRpm) / (config.engine.redlineLimitRpm - config.engine.idleRpm);
    float curveFactor = 1.0f - (normalizedDist * normalizedDist);
    return Mathf.Clamp(maxTq * Mathf.Max(curveFactor, 0.4f), maxTq * 0.3f, maxTq);
}

void UpdateDrivetrainSimulation(float forwardVel, float rawAxis)
{
    bool targetReverse = rawAxis < -0.01f;
    if (Mathf.Abs(rawAxis) < 0.05f)
    {
        currentEngineRpm = Mathf.Lerp(currentEngineRpm, config.engine.idleRpm, 4f * Time.fixedDeltaTime);
        config.CurrentEngineRpm = currentEngineRpm;
        config.CurrentGearDisplay = isReversing ? -(currentGearIndex + 1) : (currentGearIndex + 1);
        config.CurrentTorqueNm = 0f;
        config.engine.calculatedHorsepower = 0f;
        return;
    }

    if (targetReverse != isReversing)
    {
        isReversing = targetReverse;
        currentGearIndex = 0;
    }

    float[] activeGearArray = isReversing ? config.transmission.reverseGearRatios : config.transmission.forwardGearRatios;
    if (activeGearArray == null || activeGearArray.Length == 0) return;
    currentGearIndex = Mathf.Clamp(currentGearIndex, 0, activeGearArray.Length - 1);

    // STABLE AUTOMATED SHIFTING BASED ON RPM
    if (shiftCooldownTimer <= 0f && !isReversing)
    {
        if (currentEngineRpm >= config.engine.upshiftRpm && currentGearIndex < activeGearArray.Length - 1)
        {
            currentGearIndex++;
            currentEngineRpm *= 0.75f;
            shiftCooldownTimer = 0.8f;
        }
        else if (currentEngineRpm <= config.engine.idleRpm + 300f && currentGearIndex > 0)
        {
            currentGearIndex--;
            currentEngineRpm *= 1.25f;
            shiftCooldownTimer = 0.6f;
        }
    }

    // FIXED: RPM now accounts for throttle demand and vehicle speed load
    float throttleDemand = Mathf.Abs(rawAxis);
    float speedFactor = Mathf.Clamp01(Mathf.Abs(forwardVel) / (config.maxSpeedKmh / 3.6f));
    float targetEngineRpm = Mathf.Lerp(config.engine.idleRpm, config.engine.redlineLimitRpm, throttleDemand * (1f - (speedFactor * 0.15f)));
    currentEngineRpm = Mathf.Lerp(currentEngineRpm, targetEngineRpm, 5f * Time.fixedDeltaTime);
    currentEngineRpm = Mathf.Clamp(currentEngineRpm, config.engine.idleRpm, config.engine.redlineLimitRpm + 100f);
    config.CurrentEngineRpm = currentEngineRpm;
    config.CurrentGearDisplay = isReversing ? -(currentGearIndex + 1) : (currentGearIndex + 1);
    float baseEngineTorque = GetEngineTorqueAtRpm(currentEngineRpm);
    config.CurrentTorqueNm = baseEngineTorque * throttleDemand * clutchEngagementFactor;
    config.engine.calculatedHorsepower = (baseEngineTorque * currentEngineRpm) / 9548.8f;
}

void ApplyDifferentialTrackForces(float rawAxis, float rawTurn, Vector3 forwardMoveDir, float forwardVel)
{
    if (Mathf.Abs(rawAxis) < 0.01f && Mathf.Abs(rawTurn) < 0.01f)
    {
        config.CurrentTorqueNm = 0f;
        return;
    }

    float[] activeGearArray = isReversing ? config.transmission.reverseGearRatios : config.transmission.forwardGearRatios;
    float currentGearRatio = (currentGearIndex >= 0 && currentGearIndex < activeGearArray.Length) ? activeGearArray[currentGearIndex] : 4.0f;
    float baseEngineTorque = GetEngineTorqueAtRpm(currentEngineRpm);
    float engineTorque = baseEngineTorque * Mathf.Max(Mathf.Abs(rawAxis), Mathf.Abs(rawTurn)) * clutchEngagementFactor;
    config.CurrentTorqueNm = engineTorque;

    float totalWheelTorque = engineTorque * currentGearRatio * Mathf.Max(config.transmission.finalDriveRatio, 1f);
    float wheelRadiusM = (config.roadWheelDiameterMm / 1000f) * 0.5f;
    if (wheelRadiusM <= 0.1f) wheelRadiusM = 0.45f;
    float baseDriveForce = totalWheelTorque / wheelRadiusM;
    float leftForceMultiplier = 1.0f;
    float rightForceMultiplier = 1.0f;
    Vector3 leftForceDir = forwardMoveDir;
    Vector3 rightForceDir = forwardMoveDir;
    bool isMovingReverse = (currentGearIndex >= 0 && rawAxis < 0f);
    if (isMovingReverse)
    {
        leftForceDir = -forwardMoveDir;
        rightForceDir = -forwardMoveDir;
    }

    if (Mathf.Abs(rawAxis) > 0.01f && Mathf.Abs(rawTurn) > 0.01f)
    {
        float steerReduction = Mathf.Abs(rawTurn) * config.steeringSensitivity;
        if (rawTurn > 0f) rightForceMultiplier = Mathf.Clamp01(1.0f - steerReduction);
        else leftForceMultiplier = Mathf.Clamp01(1.0f - steerReduction);
    }
    else if (Mathf.Abs(rawAxis) < 0.01f && Mathf.Abs(rawTurn) > 0.01f)
    {
        if (rawTurn > 0f)
        {
            leftForceDir = forwardMoveDir;
            rightForceDir = -forwardMoveDir;
        }
        else
        {
            leftForceDir = -forwardMoveDir;
            rightForceDir = forwardMoveDir;
        }
        leftForceMultiplier = Mathf.Abs(rawTurn);
        rightForceMultiplier = Mathf.Abs(rawTurn);
    }

    float leftFinalForce = baseDriveForce * leftForceMultiplier;
    float rightFinalForce = baseDriveForce * rightForceMultiplier;

    ApplyForceToWheels(config.leftRoadWheels, leftForceDir * leftFinalForce);
    ApplyForceToWheels(config.rightRoadWheels, rightForceDir * rightFinalForce);

    if ((config.leftRoadWheels == null || config.leftRoadWheels.Length == 0) && (config.rightRoadWheels == null || config.rightRoadWheels.Length == 0))
    {
        rb.AddForce((leftForceDir * leftFinalForce + rightForceDir * rightForceMultiplier) * 0.5f, ForceMode.Force);
    }
}

void ApplyForceToWheels(Transform[] wheels, Vector3 forceVector)
{
    if (wheels == null || wheels.Length == 0) return;
    float forcePerWheel = forceVector.magnitude / wheels.Length;
    Vector3 direction = forceVector.normalized;
    foreach (Transform wheel in wheels)
    {
        if (wheel != null)
        {
            rb.AddForceAtPosition(direction * forcePerWheel, wheel.position, ForceMode.Force);
        }
    }
}

void ApplyTrackGrip()
{
    // FIXED: Use Vector3.forward (local side axis) instead of Vector3.up for lateral grip
    Vector3 gripDir = transform.TransformDirection(Vector3.forward).normalized;
    float lateralVelocityMagnitude = Vector3.Dot(rb.linearVelocity, gripDir);
    float speedMagnitude = rb.linearVelocity.magnitude;
    float dynamicGripScale = Mathf.Lerp(1.2f, 0.4f, Mathf.Clamp01(speedMagnitude / 10f));
    float activeGripMultiplier = (Mathf.Abs(currentThrottleInput) > 0.05f && Mathf.Abs(currentSteerInput) > 0.5f) ? dynamicGripScale : 1.0f;
    Vector3 lateralFrictionForce = -gripDir * lateralVelocityMagnitude * (config.lateralGrip * 30000f * activeGripMultiplier);
    rb.AddForce(lateralFrictionForce, ForceMode.Force);
}

void ApplyEnvironmentalResistance(Vector3 forwardMoveDir, float forwardVel)
{
    float massKg = config.tankWeightTons * 1000f;
    float normalForce = massKg * 9.81f;
    float rollingResistanceForceMagnitude = normalForce * config.rollingResistanceCoefficient;
    Vector3 rollingResistance = -forwardMoveDir * Mathf.Sign(forwardVel) * rollingResistanceForceMagnitude;
    float airDensity = 1.225f;
    float speedSq = forwardVel * forwardVel;
    float dragForceMagnitude = 0.5f * airDensity * config.aerodynamicDragCoefficient * config.frontalAreaMeters2 * speedSq;
    Vector3 aerodynamicDrag = -forwardMoveDir * Mathf.Sign(forwardVel) * dragForceMagnitude;
    rb.AddForce(rollingResistance + aerodynamicDrag, ForceMode.Force);

    bool isReversingSpeed = (forwardVel < 0f);
    float activeMaxSpeedKmh = isReversingSpeed ? config.maxReverseSpeedKmh : config.maxSpeedKmh;
    float maxSpeedMs = activeMaxSpeedKmh / 3.6f;
    if (Mathf.Abs(forwardVel) > maxSpeedMs)
    {
        float excess = forwardVel - (Mathf.Sign(forwardVel) * maxSpeedMs);
        rb.linearVelocity -= forwardMoveDir * (excess * 0.4f);
    }
}

void UpdateHingeJointSprings(float forwardVel)
{
    if (!config.enableReverseJumpFix) return;
    float targetPos = (forwardVel < -0.2f) ? config.reverseSpringTargetOffset : 0f;
    UpdateHingeGroupSpringTarget(config.leftSuspensionArms, targetPos);
    UpdateHingeGroupSpringTarget(config.rightSuspensionArms, targetPos);
}

void UpdateHingeGroupSpringTarget(Transform[] arms, float targetPos)
{
    if (arms == null) return;
    foreach (Transform arm in arms)
    {
        if (arm != null)
        {
            HingeJoint hinge = arm.GetComponent<HingeJoint>();
            if (hinge != null && hinge.useSpring)
            {
                JointSpring spring = hinge.spring;
                spring.targetPosition = Mathf.Lerp(spring.targetPosition, targetPos, 10f * Time.fixedDeltaTime);
                hinge.spring = spring;
            }
        }
    }
}

void CalculateWheelRotations(float forwardSpeedMps)
{
    float tankYawRateDegPerSec = rb.angularVelocity.y * Mathf.Rad2Deg;
    float tankYawRateRadPerSec = tankYawRateDegPerSec * Mathf.Deg2Rad;
    float halfTrackSpan = config.trackSpanMeters * 0.5f;
    float scaledTurnSpeed = tankYawRateRadPerSec * config.pivotVisualMultiplier;
    float leftTrackSpeedMps = forwardSpeedMps + (scaledTurnSpeed * halfTrackSpan);
    float rightTrackSpeedMps = forwardSpeedMps - (scaledTurnSpeed * halfTrackSpan);
    if (config.invertSprocketTurnRotation)
    {
        leftTrackSpeedMps = forwardSpeedMps - (scaledTurnSpeed * halfTrackSpan);
        rightTrackSpeedMps = forwardSpeedMps + (scaledTurnSpeed * halfTrackSpan);
    }

    if (config.sprocketDiameterMm > 0f)
    {
        float sprocketRadiusM = (config.sprocketDiameterMm / 1000f) * 0.5f;
        float sprocketCircumference = 2f * Mathf.PI * sprocketRadiusM;
        float leftSprocketDegPerSec = (leftTrackSpeedMps / sprocketCircumference) * 360f;
        float rightSprocketDegPerSec = (rightTrackSpeedMps / sprocketCircumference) * 360f;
        float leftSprocketDeg = leftSprocketDegPerSec * Time.fixedDeltaTime;
        float rightSprocketDeg = rightSprocketDegPerSec * Time.fixedDeltaTime;
        if (config.invertSprocketDriveRotation)
        {
            leftSprocketDeg = -leftSprocketDeg;
            rightSprocketDeg = -rightSprocketDeg;
        }
        RotateWheelGroup(config.leftSprockets, config.sprocketRotationAxis, leftSprocketDeg);
        RotateWheelGroup(config.rightSprockets, config.sprocketRotationAxis, rightSprocketDeg);
    }

    if (config.roadWheelDiameterMm > 0f)
    {
        float roadWheelRadiusM = (config.roadWheelDiameterMm / 1000f) * 0.5f;
        float roadWheelCircumference = 2f * Mathf.PI * roadWheelRadiusM;
        float leftRoadWheelDeg = (leftTrackSpeedMps / roadWheelCircumference) * 360f * Time.fixedDeltaTime;
        float rightRoadWheelDeg = (rightTrackSpeedMps / roadWheelCircumference) * 360f * Time.fixedDeltaTime;
        if (config.invertRoadWheelRotation)
        {
            leftRoadWheelDeg = -leftRoadWheelDeg;
            rightRoadWheelDeg = -rightRoadWheelDeg;
        }
        RotateWheelGroup(config.leftRoadWheels, Vector3.up, leftRoadWheelDeg);
        RotateWheelGroup(config.rightRoadWheels, Vector3.up, rightRoadWheelDeg);
    }
}

void RotateWheelGroup(Transform[] wheels, Vector3 axis, float amount)
{
    if (wheels == null) return;
    foreach (Transform wheel in wheels)
    {
        if (wheel != null) wheel.Rotate(axis, amount, Space.Self);
    }
}

}