SanAndreasUnity/Assets/Scripts/Behaviours/Vehicles/Vehicle_Physics.cs

193 lines
No EOL
6.7 KiB
C#

using SanAndreasUnity.Importing.Vehicles;
using SanAndreasUnity.Utilities;
using System.Linq;
using UnityEngine;
using VConsts = SanAndreasUnity.Behaviours.Vehicles.VehiclePhysicsConstants;
namespace SanAndreasUnity.Behaviours.Vehicles
{
[RequireComponent(typeof(Rigidbody))]
public partial class Vehicle
{
private Rigidbody _rigidBody;
public Rigidbody RigidBody => _rigidBody;
[Range(-1, 1)]
public float Accelerator;
[Range(-1, 1)]
public float Steering;
[Range(0, 1)]
public float Braking = 1f;
public Vector3 Velocity { get { return _rigidBody.velocity; } }
public float AverageWheelHeight { get { return _wheels.Count == 0 ? transform.position.y : _wheels.Average(x => x.Child.position.y); } }
public Handling.Car HandlingData { get; private set; }
private void InitializePhysics()
{
_geometryParts.AttachCollisionModel(transform, true);
_rigidBody = gameObject.GetComponent<Rigidbody>();
_rigidBody.interpolation = Net.NetStatus.IsServer ? VehicleManager.Instance.rigidbodyInterpolationOnServer : VehicleManager.Instance.rigidbodyInterpolationOnClient;
_rigidBody.collisionDetectionMode = VehicleManager.Instance.rigidBodyCollisionDetectionMode;
if (Net.NetStatus.IsClientOnly)
{
_rigidBody.useGravity = false;
_rigidBody.detectCollisions = false;
}
HandlingData = Handling.Get<Handling.Car>(Definition.HandlingName);
VConsts.Changed += UpdateValues;
var vals = VConsts.Instance;
foreach (var wheel in _wheels)
{
var front = (wheel.Alignment & WheelAlignment.Front) == WheelAlignment.Front;
wheel.Parent.position -= Vector3.up * HandlingData.SuspensionLowerLimit;
var scale = front ? Definition.WheelScaleFront : Definition.WheelScaleRear;
var mf = wheel.Child.GetComponent<MeshFilter>();
if (mf != null)
{
var size = mf.sharedMesh.bounds.size.y;
wheel.Child.localScale = Vector3.one * scale / size;
}
wheel.Collider = wheel.Parent.gameObject.AddComponent<WheelCollider>();
wheel.Collider.radius = scale * .5f;
wheel.Collider.suspensionDistance = HandlingData.SuspensionUpperLimit - HandlingData.SuspensionLowerLimit;
}
UpdateValues(vals);
}
private void UpdateValues(VConsts vals)
{
_rigidBody.drag = HandlingData.Drag * vals.DragScale;
_rigidBody.mass = HandlingData.Mass * vals.MassScale;
_rigidBody.centerOfMass = HandlingData.CentreOfMass;
foreach (var wheel in _wheels)
{
var spring = wheel.Collider.suspensionSpring;
spring.damper = HandlingData.SuspensionDampingLevel * vals.SuspensionDampingScale;
spring.spring = HandlingData.SuspensionForceLevel * vals.SuspensionForceScale;
spring.targetPosition = 0.5f;
wheel.Collider.suspensionSpring = spring;
var friction = wheel.Collider.sidewaysFriction;
friction.extremumSlip = vals.SideFrictionExtremumSlip;
friction.extremumValue = vals.SideFrictionExtremumValue;
friction.asymptoteSlip = vals.SideFrictionAsymptoteSlip;
friction.asymptoteValue = vals.SideFrictionAsymptoteValue;
friction.stiffness = 1f;
wheel.Collider.sidewaysFriction = friction;
friction = wheel.Collider.forwardFriction;
friction.extremumSlip = vals.ForwardFrictionExtremumSlip;
friction.extremumValue = vals.ForwardFrictionExtremumValue;
friction.asymptoteSlip = vals.ForwardFrictionAsymptoteSlip;
friction.asymptoteValue = vals.ForwardFrictionAsymptoteValue;
friction.stiffness = 1f;
wheel.Collider.forwardFriction = friction;
}
}
private float DriveBias(Wheel wheel)
{
switch (HandlingData.TransmissionDriveType)
{
case DriveType.Forward:
return wheel.IsFront ? 1f : 0f;
case DriveType.Rear:
return wheel.IsRear ? 1f : 0f;
default:
return 1f;
}
}
private bool ShouldSteer(Wheel wheel)
{
// TODO: look at flags
return wheel.IsFront;
}
private float BrakeBias(Wheel wheel)
{
return wheel.IsFront
? 1f - HandlingData.BrakeBias : wheel.IsRear
? HandlingData.BrakeBias : .5f;
}
private void PhysicsFixedUpdate()
{
if (m_isServer || (this.IsControlledByLocalPlayer && VehicleManager.Instance.controlWheelsOnLocalPlayer))
{
this.UpdateWheelsPhysics();
}
if (m_isServer)
{
this.AddAntiRollForceToRigidBody();
}
}
void UpdateWheelsPhysics()
{
var vals = VConsts.Instance;
foreach (var wheel in _wheels)
{
// apply steering
if (ShouldSteer(wheel))
{
wheel.Collider.steerAngle = HandlingData.SteeringLock * Steering;
}
// apply motor torque
wheel.Collider.motorTorque =
Accelerator * HandlingData.TransmissionEngineAccel
* vals.AccelerationScale * DriveBias(wheel);
// apply brake torque
wheel.Collider.brakeTorque =
Braking * HandlingData.BrakeDecel
* vals.BreakingScale * BrakeBias(wheel);
// update travel
if (wheel.Complement != null) wheel.UpdateTravel();
}
}
/// <summary>
/// Adds upward force to rigid body based on difference of travel between complemented wheels.
/// </summary>
void AddAntiRollForceToRigidBody()
{
foreach (var wheel in _wheels.Where(x => x.Complement != null))
{
if (wheel.Travel == wheel.Complement.Travel) continue;
if (!wheel.Collider.isGrounded) continue;
var force = (wheel.Complement.Travel - wheel.Travel) * VConsts.Instance.AntiRollScale;
_rigidBody.AddForceAtPosition(wheel.Parent.transform.up * force, wheel.Parent.position);
}
}
}
}