我的控制和物理组件/脚本已挂载到车体上。像路面轮子和齿轮这样的部件已拖入对应的框中。油箱控制基本上是数据,而油箱驱动物理是实际使用的机制。问题在于,我油门打开时油箱不想移动,但转速随转矩和齿轮一起上升,这说不通。当前的力是应用在轮子触地位置的。谢谢你的帮助。我可以分享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);
}
}
}
评论 (0)