*
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
public class Brake : MonoBehaviour {
|
||||
public float _friction = 100;
|
||||
public float _radius = 0.2f;
|
||||
public float _area = 0.2f;
|
||||
public float _max_pressure = 10;
|
||||
public float _bias = 1;
|
||||
private float _threshold = 4.0e-4f;
|
||||
private bool _is_locked;
|
||||
|
||||
public Brake() {
|
||||
|
||||
}
|
||||
|
||||
public Brake (float sliding, float radius, float area, float max_pressure, float bias) {
|
||||
_friction = sliding;
|
||||
_radius = radius;
|
||||
_area = area;
|
||||
_max_pressure = max_pressure * bias;
|
||||
_bias = bias;
|
||||
}
|
||||
|
||||
public float GetTorque(float factor, float rotational_speed) {
|
||||
// `factor' is the fraction of maximum pressure applied.
|
||||
float pressure = factor * _bias * _max_pressure;
|
||||
float normal = pressure * _area;
|
||||
float torque = _friction * normal * _radius;
|
||||
float velocity = _radius * rotational_speed;
|
||||
if(velocity < 0.0f)
|
||||
torque *= -1;
|
||||
|
||||
// See if the brake is locked.
|
||||
if(Mathf.Abs(velocity) < (_threshold * normal)) {
|
||||
_is_locked = true;
|
||||
torque = 0.0f;
|
||||
} else {
|
||||
_is_locked = false;
|
||||
}
|
||||
|
||||
return torque;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2696fc12f129fb43910ceeaa8bcea37
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
@@ -0,0 +1,110 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[Serializable]
|
||||
public class IdleThrolleController
|
||||
{
|
||||
public double IdleSpeed;
|
||||
public double SpeedThreshold = 1.0;
|
||||
public double LagConstant = 1.0;
|
||||
public double IdleThrolle = 0.0;
|
||||
|
||||
public void Integrate(double dt, double speed)
|
||||
{
|
||||
var throlleDerivative = ((0.5f*(1.0 - Math.Tanh(4.0*(speed - IdleSpeed)/SpeedThreshold))) - IdleThrolle)/LagConstant;
|
||||
IdleThrolle += throlleDerivative * dt;
|
||||
}
|
||||
}
|
||||
|
||||
public class CarEngine : MonoBehaviour {
|
||||
public Flywheel flywheel;
|
||||
public int cylinderCount = 4;
|
||||
public float startRpm = 50;
|
||||
|
||||
public double inertia = 0.3f;
|
||||
public double inputInertia = 0.0;
|
||||
public double inputTorque = 0.0;
|
||||
|
||||
/*public abstract Curve TorqueCurve { get;}
|
||||
public abstract Curve FrictionCurve { get;}*/
|
||||
public float Weight = 100;
|
||||
|
||||
public Starter starter;
|
||||
|
||||
public double inputThrolle = 0.0f;
|
||||
public double engineThrolle = 0.0f;
|
||||
public IdleThrolleController idleThrolleController;
|
||||
public double crankshaftAngle = 0.0; //radians
|
||||
public double angularSpeed;
|
||||
public double angularSpeedRPM;
|
||||
public double k_Friction = 0.000003;
|
||||
|
||||
public Curve powerCurve;
|
||||
|
||||
public double k_FrictionTorque = 0.75f;
|
||||
public double kinematicFrictionTorque = 0;
|
||||
|
||||
public double Rpm
|
||||
{
|
||||
get { return angularSpeed * MathEx.RadSToRpm; }
|
||||
}
|
||||
|
||||
|
||||
/* private static KeyValuePair<int, float>[] _starterTable = new KeyValuePair<int, float>[]{
|
||||
new KeyValuePair<int, float>(2, 12.5f),
|
||||
new KeyValuePair<int, float>(4, 8.0f),
|
||||
new KeyValuePair<int, float>(6, 6.5f),
|
||||
new KeyValuePair<int, float>(8, 6.0f),
|
||||
new KeyValuePair<int, float>(12, 5.5f)
|
||||
};*/
|
||||
|
||||
|
||||
void FixedUpdate()
|
||||
{
|
||||
Integrate(Time.fixedDeltaTime);
|
||||
}
|
||||
|
||||
public void Integrate(double dt) {
|
||||
//do idle throlle
|
||||
idleThrolleController.Integrate(dt, Rpm);
|
||||
engineThrolle = Math.Max(inputThrolle, idleThrolleController.IdleThrolle);
|
||||
|
||||
//integrate angular acceleration and speed
|
||||
var angularDerivative = (GetTorque(engineThrolle, Rpm) - inputTorque) / (inertia + inputInertia);
|
||||
angularSpeed += angularDerivative * dt;
|
||||
|
||||
angularSpeedRPM = MathEx.RadSToRpm * angularSpeed;
|
||||
|
||||
//integrate angle
|
||||
crankshaftAngle += angularSpeed * dt;
|
||||
|
||||
kinematicFrictionTorque = (angularSpeedRPM/60.0)*k_FrictionTorque;
|
||||
}
|
||||
|
||||
public double GetTorque(double throlle, double rpm)
|
||||
{
|
||||
var t = GetTorque(rpm);
|
||||
t = t * throlle - k_Friction * rpm * rpm * (1.0 - throlle);
|
||||
return t;
|
||||
}
|
||||
|
||||
public double GetTorque(double rpm){
|
||||
if (powerCurve == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return powerCurve.Sample((float)rpm);
|
||||
}
|
||||
|
||||
public static float GetWatts(float torque, float rpm){
|
||||
return torque * rpm / 9549.0f;
|
||||
}
|
||||
|
||||
public static float GetHp(float torque, float rpm){
|
||||
return GetWatts(torque, rpm) * 1.36f;
|
||||
}
|
||||
|
||||
public bool Started = false;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84c8f10b9cc105c4d8c0f3565df12479
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,24 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
public class CarEngineDebug : MonoBehaviour{
|
||||
public CarEngine engine;
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
if (engine != null)
|
||||
{
|
||||
GUI.Label(new Rect(0, 10, 200, 30), "Engine RPM: " + engine.Rpm);
|
||||
}
|
||||
|
||||
if (GUI.Button(new Rect(0, 70, 100, 30), "Reset engine"))
|
||||
{
|
||||
engine.engineThrolle = 0.0548135214770327;
|
||||
engine.inputThrolle = 0;
|
||||
engine.angularSpeed = MathEx.RpmToRadS * 500;
|
||||
engine.angularSpeedRPM = MathEx.RadSToRpm * engine.angularSpeed;
|
||||
engine.crankshaftAngle = 0.0;
|
||||
engine.idleThrolleController.IdleThrolle = 0.0548135214770327;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8a5446a43ae59804f8686e318dd3d03a
|
||||
timeCreated: 1453922163
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,19 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System;
|
||||
|
||||
[Serializable]
|
||||
public class CarWheelAxis {
|
||||
public Suspension leftWheel;
|
||||
public Suspension rightWheel;
|
||||
|
||||
// Use this for initialization
|
||||
void Start () {
|
||||
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update () {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2cdc318b1a3403e40b45e62d9bd84067
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
@@ -0,0 +1,16 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
public class Chassis : MonoBehaviour {
|
||||
public Suspension[] suspension;
|
||||
public CarWheelAxis[] axisCollection;
|
||||
|
||||
|
||||
public void Integrate(float dt, Car car){
|
||||
foreach(var s in suspension){
|
||||
s.Integrate(dt, car);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0f74529597105e4b93451888357b273
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
@@ -0,0 +1,61 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
public class Differential : MonoBehaviour {
|
||||
|
||||
public float gearRatio = 3.5f;
|
||||
|
||||
public DriveShaft left;
|
||||
public DriveShaft right;
|
||||
public DriveShaft center;
|
||||
|
||||
// Use this for initialization
|
||||
void Start () {
|
||||
/*if (left != null) {
|
||||
left.target = this;
|
||||
}
|
||||
if (right != null) {
|
||||
right.target = this;
|
||||
}
|
||||
if (center != null) {
|
||||
center.target = this;
|
||||
}*/
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update () {
|
||||
|
||||
|
||||
}
|
||||
/*
|
||||
void OnDrawGizmos(){
|
||||
AdvancedGizmo.DrawCircle (transform.position, Vector3.up, 0.2f, Color.green, 12, false, 1);
|
||||
|
||||
Gizmos.color = Color.red;
|
||||
if (left != null) {
|
||||
Gizmos.DrawLine(left.transform.position, gameObject.transform.position);
|
||||
}
|
||||
|
||||
if (right != null) {
|
||||
Gizmos.DrawLine(right.transform.position, gameObject.transform.position);
|
||||
}
|
||||
|
||||
if (center != null) {
|
||||
Gizmos.color = Color.blue;
|
||||
|
||||
Gizmos.DrawLine(center.transform.position, gameObject.transform.position);
|
||||
}
|
||||
|
||||
var pt = Tools.WorldToGuiPoint (transform.position);
|
||||
if (pt.z > 0) {
|
||||
Tools.BeginGui ();
|
||||
var textRect = new Rect (pt.x, pt.y, 100, 20);
|
||||
Tools.DrawRectangle(textRect, new Color(0,0,0,0.7f));
|
||||
GUI.Label (textRect, "Gear: " + gearRatio);
|
||||
|
||||
Tools.EndGui ();
|
||||
}
|
||||
|
||||
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7f2e113d7104154882152882f45afdd
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
@@ -0,0 +1,57 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
public class DriveShaft : MonoBehaviour
|
||||
{
|
||||
public Transform endpoint0;
|
||||
public Transform endpoint1;
|
||||
|
||||
float inv_inertia;
|
||||
|
||||
public float Inv_inertia {
|
||||
get {
|
||||
return inv_inertia;
|
||||
}
|
||||
set {
|
||||
inv_inertia = value;
|
||||
}
|
||||
}
|
||||
|
||||
float ang_velocity;
|
||||
float angle;
|
||||
|
||||
public float Angle{
|
||||
get{return angle;}
|
||||
}
|
||||
|
||||
public float AngularVelocity{
|
||||
get{return ang_velocity;}
|
||||
set {
|
||||
ang_velocity = value;
|
||||
}
|
||||
}
|
||||
|
||||
public DriveShaft() {
|
||||
inv_inertia = 1;
|
||||
ang_velocity = 0;
|
||||
angle = 0;
|
||||
}
|
||||
|
||||
// amount of momentum to reach angular velocity
|
||||
public float GetMomentum(float angvel)
|
||||
{
|
||||
|
||||
return (angvel - ang_velocity) / inv_inertia;
|
||||
}
|
||||
|
||||
// update angular velocity
|
||||
public void ApplyMomentum(float momentum)
|
||||
{
|
||||
ang_velocity += inv_inertia * momentum;
|
||||
}
|
||||
|
||||
// update angle
|
||||
public void Integrate(float dt){
|
||||
angle += ang_velocity * dt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb743870935c8804bb18ddd60eeca428
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3a5c241b3709404b8e82672a96f1b12
|
||||
folderAsset: yes
|
||||
timeCreated: 1453140670
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
public class EnginePowerCurve : Curve
|
||||
{
|
||||
public float maxPower = 150000; //in Watts
|
||||
public float rpmAtMaxPower = 8000;
|
||||
|
||||
public float k_p1 = 1;
|
||||
public float k_p2 = 1;
|
||||
public float k_p3 = 1;
|
||||
|
||||
public override float Sample(float arg) {
|
||||
var o_max = rpmAtMaxPower;
|
||||
double o_e = arg;
|
||||
|
||||
var p = new double[4];
|
||||
p[0] = 0;
|
||||
p[1] = maxPower / o_max * k_p1;
|
||||
p[2] = maxPower / (o_max * o_max) * k_p2;
|
||||
p[3] = -maxPower / (o_max * o_max * o_max) * k_p3;
|
||||
|
||||
double Pe = 0.0;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
Pe += p[i] * Math.Pow(o_e, i);
|
||||
}
|
||||
|
||||
var t = MathEx.Torque(Pe, MathEx.RpmToRadS * (arg + 0.000001));
|
||||
return (float)t;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 242e7bc229da7d840be56306cd243fc1
|
||||
timeCreated: 1454257928
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
public class Flywheel : MonoBehaviour
|
||||
{
|
||||
public uint teethCount = 129;
|
||||
public float mass = 6.6f;
|
||||
public float radius = 0.14f;
|
||||
public float inertia = 0.06468f;
|
||||
|
||||
[ContextMenu("Calculate inertia")]
|
||||
void CalcInertia()
|
||||
{
|
||||
inertia = (mass*radius*radius)*0.5f;
|
||||
}
|
||||
|
||||
// Use this for initialization
|
||||
void Start () {
|
||||
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update () {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d20f72f97cc2b864389b190cb34b1d47
|
||||
timeCreated: 1454252724
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
public enum GearboxControlType{
|
||||
Manual,
|
||||
Automatic
|
||||
}
|
||||
|
||||
public abstract class Gearbox : MonoBehaviour {
|
||||
public abstract GearboxControlType ControlType{ get;}
|
||||
public abstract float MaxOutputTorque{ get;}
|
||||
public abstract int GearsCount{ get;}
|
||||
public abstract float GetGearRatio(int gear);
|
||||
public abstract int Gear{ get; set;}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 47ea6b7730855674fba79ad64b9691a1
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,34 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
public class PacejkaParameters : MonoBehaviour {
|
||||
|
||||
|
||||
[ContextMenuItem("Reset coeffs", "resetPacejkaCoeffs")]
|
||||
public PacejkaCoeffs _coeffs = new PacejkaCoeffs();
|
||||
|
||||
[ContextMenuItem("Reset coeffs2", "resetPacejkaCoeffs2")]
|
||||
public PacejkaCoeffs2 _coeffs2;
|
||||
|
||||
public double minLoad = 0;
|
||||
public double maxLoad = 8900;
|
||||
|
||||
|
||||
private void resetPacejkaCoeffs() {
|
||||
_coeffs = new PacejkaCoeffs();
|
||||
}
|
||||
|
||||
private void resetPacejkaCoeffs2() {
|
||||
_coeffs2 = new PacejkaCoeffs2();
|
||||
}
|
||||
|
||||
// Use this for initialization
|
||||
void Start () {
|
||||
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update () {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb98de270c82d434c8c66084fdbf89af
|
||||
timeCreated: 1453140700
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,109 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
public class SimpleSteerController : SteerController {
|
||||
private float _steer;
|
||||
public float maxSteerAngle = 30.0f;
|
||||
|
||||
public WheelPhysics frontLeftWheel;
|
||||
public WheelPhysics frontRightWheel;
|
||||
public WheelPhysics rearLeftWheel;
|
||||
public WheelPhysics rearRightWheel;
|
||||
|
||||
private Vector3 frontLeftStartAngle;
|
||||
private Vector3 frontRightStartAngle;
|
||||
|
||||
public SteeringWheel steeringWheel;
|
||||
|
||||
public override float Steer {
|
||||
get {
|
||||
return _steer;
|
||||
}
|
||||
set {
|
||||
_steer = Mathf.Clamp(value, -1.0f, 1.0f);
|
||||
UpdateWheelAngles();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateWheelAngles(){
|
||||
float d = (frontRightWheel.transform.position - frontLeftWheel.transform.position).magnitude;
|
||||
float l = (frontRightWheel.transform.position - rearRightWheel.transform.position).magnitude;
|
||||
float innerAngle = maxSteerAngle * _steer;
|
||||
float outerAngle = OuterWheelAngle (innerAngle, d, l);
|
||||
|
||||
if (_steer < 0.0f) {
|
||||
UpdateWheelSteering (frontLeftWheel, frontLeftStartAngle, innerAngle);
|
||||
UpdateWheelSteering (frontRightWheel, frontRightStartAngle, outerAngle);
|
||||
} else {
|
||||
UpdateWheelSteering (frontRightWheel, frontRightStartAngle, innerAngle);
|
||||
UpdateWheelSteering (frontLeftWheel, frontLeftStartAngle, outerAngle);
|
||||
}
|
||||
|
||||
steeringWheel.SetAngle(steeringWheel.degrees*0.5f*_steer);
|
||||
}
|
||||
|
||||
void UpdateWheelSteering(WheelPhysics wheel, Vector3 initialAngles, float steerAngle){
|
||||
initialAngles.y += steerAngle;
|
||||
wheel.transform.localEulerAngles = initialAngles;
|
||||
}
|
||||
|
||||
public static float OuterWheelAngle(float innerDegree, float d, float l){
|
||||
float sign = Mathf.Sign (innerDegree);
|
||||
float rad = Mathf.Deg2Rad * innerDegree * sign;
|
||||
float ctgB = Mathf.Cos (rad) / Mathf.Sin (rad);
|
||||
return Arcctg (ctgB + d / l) * Mathf.Rad2Deg * sign;
|
||||
}
|
||||
|
||||
static float Arcctg(float x){
|
||||
return Mathf.PI / 2.0f - Mathf.Atan (x);
|
||||
}
|
||||
|
||||
|
||||
// Use this for initialization
|
||||
void Start () {
|
||||
frontLeftStartAngle = frontLeftWheel.transform.localEulerAngles;
|
||||
frontRightStartAngle = frontRightWheel.transform.localEulerAngles;
|
||||
}
|
||||
|
||||
void OnGUI(){
|
||||
Steer = GUI.HorizontalSlider (new Rect (10, 10, 300, 20), Steer, -1, 1);
|
||||
}
|
||||
|
||||
public float testSteerSpeed = 20.0f;
|
||||
|
||||
public float torque = 1000;
|
||||
|
||||
void FixedUpdate()
|
||||
{
|
||||
float finalTorque = Input.GetKey(KeyCode.W) ? torque : (Input.GetKey(KeyCode.S) ? -torque : 0);
|
||||
frontLeftWheel.driveTorque = finalTorque;
|
||||
frontRightWheel.driveTorque = finalTorque;
|
||||
|
||||
|
||||
double breakTorque = 0;
|
||||
if (Input.GetKey(KeyCode.Space))
|
||||
{
|
||||
breakTorque = 1600;
|
||||
}
|
||||
|
||||
frontLeftWheel.brakeTorque = breakTorque;
|
||||
frontRightWheel.brakeTorque = breakTorque;
|
||||
rearLeftWheel.brakeTorque = breakTorque;
|
||||
rearRightWheel.brakeTorque = breakTorque;
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update () {
|
||||
|
||||
float steerDelta = testSteerSpeed * Time.fixedDeltaTime;
|
||||
if (Input.GetKey (KeyCode.D)) {
|
||||
Steer += steerDelta;
|
||||
}
|
||||
if (Input.GetKey (KeyCode.A)) {
|
||||
Steer -= steerDelta;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ff6449b097dd764d99cf320db63ebce
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
@@ -0,0 +1,33 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
public class SimpleSuspension : Suspension {
|
||||
private Quaternion _startupRotation;
|
||||
// Use this for initialization
|
||||
void Start () {
|
||||
_startupRotation = transform.localRotation;
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update () {
|
||||
|
||||
}
|
||||
|
||||
public override void OnSteerChanged()
|
||||
{
|
||||
transform.localRotation = _startupRotation * Quaternion.AngleAxis((float)Steer, Vector3.up);
|
||||
}
|
||||
|
||||
void OnDrawGizmos(){
|
||||
var up = transform.up;
|
||||
var center = transform.position;
|
||||
Gizmos.DrawLine(center + up * (float)((1.0 - suspensionTarget) * travelDistance), center - up * (float)(suspensionTarget* travelDistance));
|
||||
|
||||
bool front = wheel.name[wheel.name.Length - 2] == 'f';
|
||||
Vector3 vOffset = front ? Vector3.up : Vector3.up * 0.5f;
|
||||
|
||||
Gizmos.DrawLine(dbgWheelForcePos + vOffset, dbgWheelForcePos + dbgWheelForce + vOffset);
|
||||
|
||||
Gizmos.DrawLine(dbgWheelForcePos + vOffset, dbgWheelForcePos);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d16ee8693777b14b952662a59be4ad7
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
public abstract class SteerController : MonoBehaviour {
|
||||
|
||||
public abstract float Steer{ get; set; }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd8405aff4937ff4e948598fa5c04f53
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
@@ -0,0 +1,142 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
public abstract class Suspension : MonoBehaviour {
|
||||
|
||||
private double _steer;
|
||||
protected double _normalForce;
|
||||
public WheelPhysics wheel;
|
||||
public double travelDistance = 0.2f;
|
||||
public double suspensionTarget = 0.2f;
|
||||
private double _displacement;
|
||||
protected double _previousFraction;
|
||||
private double _overtravel;
|
||||
private double _lastDisplacement;
|
||||
public double springForce = 9000;
|
||||
public double springDamping = 400;
|
||||
|
||||
public double Steer{
|
||||
get{return _steer;}
|
||||
set
|
||||
{
|
||||
_steer = value;
|
||||
OnSteerChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnSteerChanged() {
|
||||
|
||||
}
|
||||
|
||||
// Use this for initialization
|
||||
void Start () {
|
||||
|
||||
}
|
||||
|
||||
public double Displacement {
|
||||
get{return _displacement;}
|
||||
set{
|
||||
_lastDisplacement = _displacement;
|
||||
_displacement = MathEx.Clamp(value, 0, travelDistance);
|
||||
_overtravel = value - travelDistance;
|
||||
if(_overtravel < 0){
|
||||
_overtravel = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CalculateForce(double dt){
|
||||
double velocity = ((_displacement - _lastDisplacement) / travelDistance) / dt;
|
||||
double spring = (_displacement / travelDistance) * springForce;
|
||||
double damping = velocity * springDamping;
|
||||
_normalForce = damping + spring;
|
||||
}
|
||||
|
||||
|
||||
double computeImpulseDenominator(Vector3 pos, Vector3 normal, Rigidbody rb){
|
||||
Vector3 r0 = pos - rb.worldCenterOfMass;
|
||||
Vector3 c0 = Vector3.Cross(r0, normal);
|
||||
|
||||
|
||||
var tensorLocal = Matrix4x4.TRS(rb.worldCenterOfMass, rb.inertiaTensorRotation, rb.inertiaTensor);
|
||||
var tensorWorld = transform.localToWorldMatrix * tensorLocal;
|
||||
tensorWorld = Matrix4x4.Inverse(tensorWorld);
|
||||
Vector3 vec = Vector3.Cross(tensorWorld.MultiplyPoint(c0), r0);
|
||||
|
||||
return 1.0f / rb.mass + Vector3.Dot(normal, vec);
|
||||
}
|
||||
|
||||
protected Vector3 dbgWheelForce;
|
||||
protected Vector3 dbgWheelForcePos;
|
||||
|
||||
public void Integrate(double dt, Car car){
|
||||
if(wheel != null){
|
||||
//var rb = car.GetRigidBody();
|
||||
|
||||
//raycast wheel
|
||||
wheel.UpdateContactPoint(car.transform.up);
|
||||
double fraction;
|
||||
double relativeDisplacement = wheel.TotalWheelRadius - wheel.Contact.hit.distance;
|
||||
Displacement += relativeDisplacement;
|
||||
|
||||
fraction = _displacement / travelDistance;
|
||||
CalculateForce(dt);
|
||||
|
||||
var wheelWorldVelocity = car.GetRigidBody().GetPointVelocity(wheel.transform.position);
|
||||
|
||||
if (_overtravel > 0)
|
||||
{
|
||||
double correction_factor = 0.0f;
|
||||
double dv = Vector3.Dot(wheelWorldVelocity, transform.up);// body->getVelocityInLocalPoint(suspension_force_application_point).dot(forcedirection);
|
||||
dv -= correction_factor * _overtravel / dt;
|
||||
// double effectiveMass = 1.0 / computeImpulseDenominator(wheel.transform.position, transform.up, car.GetRigidBody());//1.0 / body->computeImpulseDenominator(wheel_position[i], forcedirection);
|
||||
// double correction = -effectiveMass * dv / dt;
|
||||
/*if (correction > 0)
|
||||
{
|
||||
_normalForce = correction;
|
||||
}*/
|
||||
}
|
||||
|
||||
if(!wheel.Contact.hasHit) {
|
||||
_normalForce = 0;
|
||||
}
|
||||
|
||||
if(_normalForce > 0.0f){
|
||||
car.GetRigidBody().AddForceAtPosition(wheel.Contact.hit.normal * (float)_normalForce, transform.position);
|
||||
}
|
||||
|
||||
wheel.transform.position = GetWheelPosition(fraction);
|
||||
|
||||
Vector3 wheelForce;
|
||||
wheel.Integrate(dt, wheelWorldVelocity, _normalForce, out wheelForce);
|
||||
car.GetRigidBody().AddForceAtPosition(wheelForce, wheel.transform.position);
|
||||
|
||||
dbgWheelForce = wheelForce;
|
||||
dbgWheelForcePos = wheel.transform.position;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void CalculateSuspensionForce(double dt, double fraction) {
|
||||
_normalForce = springForce * (suspensionTarget - fraction) + springDamping * (_previousFraction - fraction) / dt;
|
||||
_previousFraction = fraction;
|
||||
}
|
||||
|
||||
public Vector3 GetWheelPosition(double fraction) {
|
||||
var up = transform.up;
|
||||
var center = transform.position;
|
||||
var top = center + up * (float)((1.0 - suspensionTarget) * travelDistance);
|
||||
var bottom = center - up * (float)(suspensionTarget * travelDistance);
|
||||
|
||||
return bottom + (top - bottom) * Mathf.Clamp01((float)fraction);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Update is called once per frame
|
||||
void Update () {
|
||||
/* */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd4c241e105bc7144a70f4d5e141b4bb
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
@@ -0,0 +1,150 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
using System;
|
||||
|
||||
[ExecuteInEditMode]
|
||||
public class Tyre : MonoBehaviour {
|
||||
public double height = 0.15f;
|
||||
public double k_RollLinear = 0.0038;
|
||||
public double k_RollQuad = 0.000026;
|
||||
|
||||
public double GetRollingResistance(double Fz, double wheelEffectiveRadius, double velocity, double resistanceFactor) {
|
||||
return Fz * wheelEffectiveRadius * resistanceFactor * (k_RollLinear + k_RollQuad * (velocity )) * Math.Sign(velocity);
|
||||
}
|
||||
|
||||
private double oldLatSlip;
|
||||
public double latFilter = 0.85f;
|
||||
|
||||
|
||||
public PacejkaParameters pacejkaParams;
|
||||
|
||||
|
||||
|
||||
/* public void Slip(double patch_speed, Vector3 hub_velocity, out double slipLon, out double slipLat) {
|
||||
var absLongVelocity = (double)Mathf.Abs(hub_velocity.z);
|
||||
absLongVelocity = Math.Max(absLongVelocity, dampAbsRoadVelo);
|
||||
slipLon = CalcSlipRatio(patch_speed, hub_velocity) * 100.0f;
|
||||
slipLat = (Mathf.Atan2(hub_velocity.x, (float)absLongVelocity)) * Mathf.Rad2Deg;
|
||||
}
|
||||
|
||||
public Double3 FrictionForce(double normalForce, double slipLon, double slipLat, double camber, PacejkaCoeffs coeffs) {
|
||||
if(normalForce < 1e-4f) { return Double3.zero; }
|
||||
|
||||
normalForce = MathEx.Clamp(normalForce, 0, 100000);
|
||||
|
||||
//compute Pacejka params
|
||||
var lateralParams = Pacejka.LateralParams(normalForce, camber, coeffs);
|
||||
var longitudinalParams = Pacejka.LongitudinalParams(normalForce, coeffs);
|
||||
var aligningParams = Pacejka.AligningParams(normalForce, camber, coeffs);
|
||||
|
||||
//compute friction forces
|
||||
Double3 result;
|
||||
result.x = Pacejka.MagicFormula(slipLon, longitudinalParams);
|
||||
result.y = Pacejka.MagicFormula(slipLat, lateralParams);
|
||||
result.z = Pacejka.MagicFormula(slipLat, aligningParams);
|
||||
|
||||
/* //do combined friction
|
||||
double d_lon = slipLon / (1.0 + slipLon);
|
||||
double d_lat = Math.Tan(slipLat) / (1.0 + slipLon);
|
||||
double d = Math.Sqrt(d_lat * d_lat + d_lon * d_lon);
|
||||
|
||||
if(d > 0.001) {
|
||||
result.x *= (float)Math.Abs(d_lon / d);
|
||||
result.y *= (float)Math.Abs(d_lat / d);
|
||||
}*/
|
||||
// return result;
|
||||
// }
|
||||
|
||||
|
||||
void OnValidate() {
|
||||
if(enabled) {
|
||||
if(transform.parent != null && gameObject.activeInHierarchy)
|
||||
{
|
||||
SceneView.RepaintAll();
|
||||
// transform.parent.gameObject.SendMessage("OnValidate");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// SAE950311 implementation
|
||||
// private double lastU;
|
||||
// private double signU = 1;
|
||||
//private double oldSlip = 0;
|
||||
// public double tau;
|
||||
// public double B = 0.091f;//relaxationLengthLong
|
||||
public double relaxLong = 0.00172688486f;
|
||||
// private double _integratedSlip;
|
||||
public double speedThreshold = 3.0f;
|
||||
public double dampAbsRoadVelo = 1;
|
||||
public double kBLong = 0.35f;
|
||||
|
||||
public double fixedTimeBase = 0.02f;
|
||||
|
||||
|
||||
public double CalcSlipRatio(double patch_speed, Vector3 hub_velocity) {
|
||||
bool IsOnSurface = true;
|
||||
if(!IsOnSurface) {
|
||||
// _integratedSlip = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* double deltaTime = Time.fixedDeltaTime;
|
||||
double dampSRreversal = 0.5f;
|
||||
double B = relaxLong*100;
|
||||
double fixedTimeStepScalar = fixedTimeBase / Time.fixedDeltaTime;
|
||||
double invFixedTimeStepScalar = 1.0f / fixedTimeStepScalar;
|
||||
|
||||
if(B < kBLong * invFixedTimeStepScalar)
|
||||
B = kBLong * invFixedTimeStepScalar;
|
||||
|
||||
// damp sliAngle and slipRatio oscilation
|
||||
|
||||
|
||||
double factor = hub_velocity.z * 3.6f * 0.02f; // divided by 50
|
||||
if(factor < 1)
|
||||
factor = 1;
|
||||
B *= factor;
|
||||
*/
|
||||
|
||||
// SAE950311 algorithm
|
||||
|
||||
var longVelocity = (double)hub_velocity.z;
|
||||
// var absLongVelocity = Math.Abs(longVelocity);
|
||||
|
||||
|
||||
|
||||
if (Math.Abs(longVelocity - patch_speed) > 1.0e-4)
|
||||
{
|
||||
double denom = Math.Max(Math.Abs(longVelocity), dampAbsRoadVelo);
|
||||
return MathEx.Clamp((patch_speed - longVelocity)/denom, -1.0, 1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
absLongVelocity = Math.Max(absLongVelocity, dampAbsRoadVelo);
|
||||
|
||||
|
||||
|
||||
|
||||
if((lastU < 0 && longVelocity >= 0) || (lastU >= 0 && longVelocity < 0)) {
|
||||
_integratedSlip = -dampSRreversal * _integratedSlip;
|
||||
}
|
||||
|
||||
lastU = Math.Sign(longVelocity);
|
||||
|
||||
double derivative = (patch_speed - longVelocity) - absLongVelocity * _integratedSlip;
|
||||
derivative /= B;
|
||||
|
||||
// Integrate
|
||||
_integratedSlip += derivative * deltaTime;
|
||||
|
||||
return MathEx.Clamp(_integratedSlip + tau * derivative, -1.5, 1.5);*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70404261169dc6f498752a2108433b55
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
[ExecuteInEditMode]
|
||||
public class Wheel : MonoBehaviour {
|
||||
|
||||
public Tyre tyre;
|
||||
public Brake brake;
|
||||
|
||||
public DriveShaft _shaft;
|
||||
|
||||
public float discRadius = 0.385f; //15 inch by default
|
||||
private float _totalWheelRadius;
|
||||
|
||||
public float TotalWheelRadius {
|
||||
get {
|
||||
return _totalWheelRadius;
|
||||
}
|
||||
}
|
||||
|
||||
public float _slipLon;
|
||||
public float _slipLat;
|
||||
private Vector3 _localVelocity;
|
||||
private Vector3 _friction;
|
||||
private Vector3 _peakSlip;
|
||||
public float camber;
|
||||
public bool DrawDebug = true;
|
||||
public float weight = 50;
|
||||
|
||||
public float driveTorque = 0.0f;
|
||||
public float brakeTorque = 0.0f;
|
||||
|
||||
|
||||
public float brakingState = 0.0f;
|
||||
private WheelContact _contact;
|
||||
public Oscilloscope oscilloscope;
|
||||
private int t;
|
||||
|
||||
public bool Grounded {
|
||||
get {
|
||||
return _contact.grounded;
|
||||
}
|
||||
}
|
||||
|
||||
public WheelContact Contact {
|
||||
get {
|
||||
return _contact;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[ContextMenuItem("Reset coeffs", "resetPacejkaCoeffs")]
|
||||
public PacejkaCoeffs _coeffs = new PacejkaCoeffs();
|
||||
|
||||
public float dtMul = 1;
|
||||
|
||||
private void resetPacejkaCoeffs() {
|
||||
_coeffs = new PacejkaCoeffs();
|
||||
}
|
||||
|
||||
public float AngularVelocity {
|
||||
get { return _shaft.AngularVelocity; }
|
||||
set {
|
||||
_shaft.AngularVelocity = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Use this for initialization
|
||||
void Start () {
|
||||
InitWheel();
|
||||
float normalForce = 3.210758f * 1000.0f;
|
||||
var lateralParams = Pacejka.LateralParams(normalForce, 0, _coeffs);
|
||||
var longitudinalParams = Pacejka.LongitudinalParams(normalForce, _coeffs);
|
||||
var aligningParams = Pacejka.AligningParams(normalForce, 0, _coeffs);
|
||||
|
||||
_peakSlip.x = Pacejka.Extremum(longitudinalParams);
|
||||
_peakSlip.y = Pacejka.Extremum(lateralParams);
|
||||
_peakSlip.z = Pacejka.Extremum(aligningParams);
|
||||
|
||||
if(oscilloscope != null)
|
||||
{
|
||||
oscilloscope.channels[0].sampler = Sampler0;
|
||||
oscilloscope.channels[1].sampler = Sampler1;
|
||||
oscilloscope.channels[2].sampler = Sampler2;
|
||||
}
|
||||
}
|
||||
|
||||
float Sampler0()
|
||||
{
|
||||
return _slipLat;
|
||||
}
|
||||
|
||||
float Sampler1()
|
||||
{
|
||||
return localVelocity.x;
|
||||
}
|
||||
|
||||
float Sampler2()
|
||||
{
|
||||
return 0;//Mathf.Repeat(_shaft.Angle, Mathf.PI * 2);
|
||||
}
|
||||
|
||||
float Inertia{
|
||||
get{return 1.0f / _shaft.Inv_inertia;}
|
||||
set{_shaft.Inv_inertia = 1.0f / value;}
|
||||
}
|
||||
|
||||
void InitWheel() {
|
||||
if (_shaft == null) {
|
||||
_shaft = new DriveShaft();
|
||||
}
|
||||
|
||||
|
||||
if (tyre != null) {
|
||||
_totalWheelRadius = discRadius + tyre.height;
|
||||
}
|
||||
|
||||
Inertia = weight*_totalWheelRadius*_totalWheelRadius*0.5f;
|
||||
|
||||
}
|
||||
void OnValidate() {
|
||||
InitWheel();
|
||||
}
|
||||
|
||||
|
||||
public void SetBraking(float amount) {
|
||||
brakingState = Mathf.Clamp01(amount);
|
||||
|
||||
}
|
||||
|
||||
void SetTorque(float torque, float dt) {
|
||||
_shaft.ApplyMomentum(torque * dt);
|
||||
}
|
||||
|
||||
|
||||
void OnDrawGizmosSelected() {
|
||||
if (!DrawDebug) {
|
||||
return;
|
||||
}
|
||||
var p = _coeffs;
|
||||
|
||||
Curve[] curves = new Curve[]{new Curve(), new Curve(), new Curve()};
|
||||
|
||||
int sampleCnt = 250;
|
||||
float minArg = -100;
|
||||
float maxArg = 100;
|
||||
float argDiff = maxArg - minArg;
|
||||
|
||||
for (int i = 0; i < sampleCnt; ++i) {
|
||||
var arg = minArg + argDiff / ((float)sampleCnt - 1) * i;
|
||||
curves[0].AddArg(arg);
|
||||
curves[1].AddArg(arg);
|
||||
curves[2].AddArg(arg);
|
||||
|
||||
curves[0].AddVal(Pacejka.LongitudinalForce (5000.0f, arg, p));
|
||||
curves[1].AddVal(Pacejka.LateralForce (5000.0f, camber, arg, p));
|
||||
curves[2].AddVal(Pacejka.AligningForce (5000.0f, camber, arg, p));
|
||||
}
|
||||
|
||||
float maxSlip = MathEx.FindExtremumValue(x => Pacejka.LongitudinalForce(5000.0f, x, p), 0, 100, 0.5f);
|
||||
|
||||
Tools.BeginGui ();
|
||||
|
||||
GUI.Label(new Rect(20,30,300,100), "Maximum wheel slip: " + maxSlip + " Value at that slip: " + Pacejka.LongitudinalForce(5000.0f, maxSlip, p));
|
||||
Plot2D plot = new Plot2D(curves[0]);
|
||||
|
||||
plot.Title = "Longitudinal friction";
|
||||
plot.OnGUI(new Rect(90,100,500,400));
|
||||
|
||||
camber = GUI.HorizontalSlider (new Rect (10, 10, 100, 10), camber, -10, 10);
|
||||
Tools.EndGui ();
|
||||
}
|
||||
|
||||
float GetTorque(float new_angvel, float dt) {
|
||||
return _shaft.GetMomentum(new_angvel) / dt;
|
||||
}
|
||||
|
||||
|
||||
private float roll_resistance_lin = 0.00001f;
|
||||
private float roll_resistance_quad = 0.00001f;
|
||||
float getRollingResistance(
|
||||
float velocity,
|
||||
float resistance_factor)
|
||||
{
|
||||
// surface influence on rolling resistance
|
||||
float rolling_resistance = resistance_factor * roll_resistance_lin;
|
||||
|
||||
// heat due to tire deformation increases rolling resistance
|
||||
// approximate by quadratic function
|
||||
rolling_resistance += velocity * velocity * roll_resistance_quad;
|
||||
|
||||
// rolling resistance direction
|
||||
float resistance = -rolling_resistance;
|
||||
if (velocity < 0) resistance = -resistance;
|
||||
|
||||
return resistance;
|
||||
}
|
||||
|
||||
|
||||
public void UpdateContactPoint(Vector3 up){
|
||||
var raycastRay = new Ray (transform.position, -up);
|
||||
|
||||
_contact.hasHit = false;
|
||||
_contact.grounded = false;
|
||||
if (Physics.Raycast (raycastRay, out _contact.hit)) {
|
||||
_contact.hasHit = true;
|
||||
if(_contact.hit.distance <= _totalWheelRadius){
|
||||
_contact.grounded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int pp = 0;
|
||||
private Vector3 lonForce;
|
||||
private Vector3 latForce;
|
||||
private float wheel_torque;
|
||||
public float dampOsc = 0.85f;
|
||||
public float fixedTimeBase = 0.02f;
|
||||
private Vector3 localVelocity;
|
||||
|
||||
public void Integrate(float dt, Vector3 wheelWorldVelocity, float normal_force, out Vector3 force)
|
||||
{
|
||||
//normal_force = 5000.0f;
|
||||
float wheel_drive_torque = driveTorque;
|
||||
float patch_speed = AngularVelocity * _totalWheelRadius;
|
||||
localVelocity = transform.InverseTransformDirection(wheelWorldVelocity);
|
||||
|
||||
tyre.Slip(patch_speed, localVelocity, out _slipLon, out _slipLat);
|
||||
|
||||
Vector3 friction_force = tyre.FrictionForce(normal_force, _slipLon, _slipLat, 0, _coeffs);
|
||||
|
||||
lonForce = transform.forward * friction_force[0];
|
||||
latForce = -transform.right * friction_force[1];
|
||||
|
||||
Vector3 tire_force = lonForce + latForce;
|
||||
|
||||
float tire_friction_torque = friction_force[0] * _totalWheelRadius;
|
||||
|
||||
//calculate brake torque
|
||||
float wheel_lock_torque = -AngularVelocity / dt * Inertia;
|
||||
float wheel_brake_torque = wheel_lock_torque - wheel_drive_torque + tire_friction_torque;
|
||||
float maxBreakTorque = brakeTorque;
|
||||
if (wheel_brake_torque > 0 && wheel_brake_torque > maxBreakTorque)
|
||||
{
|
||||
wheel_brake_torque = maxBreakTorque;
|
||||
}
|
||||
else if (wheel_brake_torque < 0 && wheel_brake_torque < -maxBreakTorque)
|
||||
{
|
||||
wheel_brake_torque = -maxBreakTorque;
|
||||
}
|
||||
|
||||
//limit the reaction torque to the applied drive and braking torque
|
||||
float reaction_torque = tire_friction_torque;
|
||||
float applied_torque = wheel_drive_torque + wheel_brake_torque;
|
||||
if ( ( applied_torque > 0 && reaction_torque > applied_torque ) ||
|
||||
( applied_torque < 0 && reaction_torque < applied_torque ) )
|
||||
reaction_torque = applied_torque;
|
||||
Vector3 tire_torque = Vector3.right * reaction_torque;// - direction::up * friction_force[2];
|
||||
|
||||
//set wheel torque due to tire rolling resistance
|
||||
float rollResistanceCoefficient = 0;
|
||||
float rolling_resistance = getRollingResistance(AngularVelocity, rollResistanceCoefficient);
|
||||
float rolling_resistance_torque = rolling_resistance * _totalWheelRadius - tire_friction_torque;
|
||||
|
||||
|
||||
wheel_torque = wheel_drive_torque + wheel_brake_torque + rolling_resistance_torque;
|
||||
|
||||
//have the wheels internally apply forces, or just forcibly set the wheel speed if the brakes are locked
|
||||
|
||||
float oldAngularVelocity = AngularVelocity;
|
||||
SetTorque(wheel_torque, dt);
|
||||
|
||||
/ *float fixedTimeStepScalar = fixedTimeBase / Time.fixedDeltaTime;
|
||||
float invFixedTimeStepScalar = 1.0f / fixedTimeStepScalar;
|
||||
|
||||
// damp low speed wheel oscillations
|
||||
float deltaAngularVelocity = AngularVelocity - oldAngularVelocity;
|
||||
float delta = deltaAngularVelocity * dampOsc * Mathf.Clamp01(invFixedTimeStepScalar);
|
||||
AngularVelocity -= delta;* /
|
||||
|
||||
_shaft.Integrate(dt);
|
||||
|
||||
//viscous tire contact drag (hack)
|
||||
float rollingDrag = 0.0f;
|
||||
Vector3 wheel_drag = _localVelocity * rollingDrag;
|
||||
|
||||
|
||||
force = Vector3.zero;
|
||||
//apply forces to body
|
||||
// Vector3 tire_pos = wheel_position[i] - body->getCenterOfMassPosition();
|
||||
Vector3 world_tire_force = tire_force;
|
||||
//Vector3 world_tire_torque = quatRotate ( wheel_orientation[i], tire_torque);
|
||||
world_tire_force += wheel_drag;
|
||||
//world_tire_torque += tire_pos.cross(world_tire_force);
|
||||
force = world_tire_force;
|
||||
|
||||
|
||||
//torque = torque + world_tire_torque;
|
||||
}
|
||||
|
||||
float RPM
|
||||
{
|
||||
get {
|
||||
return _shaft.AngularVelocity * 30.0f / 3.1415926535f;
|
||||
}
|
||||
}
|
||||
|
||||
public DriveShaft Shaft {
|
||||
get {
|
||||
return _shaft;
|
||||
}
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
DrawLabels();
|
||||
}
|
||||
|
||||
void DrawLabels() {
|
||||
Label3D.AtWorldPosition(transform.position + Vector3.up * 1, "RPM: " + RPM);
|
||||
Label3D.AtWorldPosition(transform.position + Vector3.up * 1.3f, "F: " + _friction.x + " S: " + _slipLon);
|
||||
}
|
||||
|
||||
void OnDrawGizmos() {
|
||||
|
||||
if (tyre == null || _shaft == null) {
|
||||
return;
|
||||
}
|
||||
DrawLabels();
|
||||
var forward = transform.forward;
|
||||
var right = transform.right;
|
||||
|
||||
//draw wheel radius
|
||||
var wheelCenter = transform.position;
|
||||
var rollMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.AngleAxis(_shaft.Angle * Mathf.Rad2Deg, right), Vector3.one);
|
||||
var radiusP0 = -forward * _totalWheelRadius;
|
||||
var radiusP1 = forward * _totalWheelRadius * 1.2f;
|
||||
radiusP0 = rollMatrix.MultiplyPoint3x4 (radiusP0);
|
||||
radiusP1 = rollMatrix.MultiplyPoint3x4 (radiusP1);
|
||||
Gizmos.DrawLine (wheelCenter + radiusP0, wheelCenter + radiusP1);
|
||||
|
||||
//draw wheel outer circle
|
||||
AdvancedGizmo.DrawCircle(wheelCenter, right, _totalWheelRadius, Color.green, 36);
|
||||
|
||||
//draw wheel inner circle
|
||||
AdvancedGizmo.DrawCircle(wheelCenter, right, discRadius, Color.green, 36);
|
||||
|
||||
//draw forces
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawLine(wheelCenter, wheelCenter + lonForce);
|
||||
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawLine(wheelCenter, wheelCenter + latForce);
|
||||
|
||||
Gizmos.color = Color.blue;
|
||||
Gizmos.DrawLine(wheelCenter, wheelCenter + latForce + lonForce);
|
||||
|
||||
Gizmos.color = Color.black;
|
||||
Gizmos.DrawLine(wheelCenter, wheelCenter + forward * 100);
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0c7faf1940de454e9a95ef9e629f173
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
@@ -0,0 +1,169 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Net.Security;
|
||||
using System;
|
||||
|
||||
public struct WheelContact {
|
||||
public RaycastHit hit;
|
||||
public bool hasHit;
|
||||
public bool grounded;
|
||||
}
|
||||
|
||||
public class WheelPhysics : MonoBehaviour {
|
||||
public Tyre tyre;
|
||||
public Brake brake;
|
||||
|
||||
public double driveTorque = 0.0f;
|
||||
public double brakeTorque = 0.0f;
|
||||
|
||||
public double angularVelocity; //rad/sec
|
||||
public double inertiaMomentum = 3.45f;
|
||||
public double angle; //radian
|
||||
|
||||
private WheelContact _contact;
|
||||
public bool Grounded {
|
||||
get {
|
||||
return _contact.grounded;
|
||||
}
|
||||
}
|
||||
|
||||
public double discRadius = 0.385f; //15 inch by default
|
||||
|
||||
public double TotalWheelRadius {
|
||||
get {
|
||||
return discRadius + tyre.height;
|
||||
}
|
||||
}
|
||||
|
||||
public WheelContact Contact {
|
||||
get {
|
||||
return _contact;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Use this for initialization
|
||||
void Start () {
|
||||
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private double _slipLon;
|
||||
private double _slipLat;
|
||||
private Vector3 localVelocity;
|
||||
|
||||
private int t = 0;
|
||||
|
||||
public double reactionTorque;
|
||||
|
||||
public void Integrate(double dt, Vector3 wheelWorldVelocity, double normal_force, out Vector3 force) {
|
||||
|
||||
t++;
|
||||
localVelocity = transform.InverseTransformDirection(wheelWorldVelocity);
|
||||
double patchSpeed = angularVelocity * TotalWheelRadius;
|
||||
|
||||
|
||||
|
||||
// tyre.Slip(patchSpeed, localVelocity, out _slipLon, out _slipLat);
|
||||
|
||||
|
||||
var Vc = new Double3(localVelocity.z, localVelocity.x, localVelocity.y);
|
||||
|
||||
Double3 frictionForces = Pacejka.CalcPacejka(Vc, angularVelocity, TotalWheelRadius, TotalWheelRadius, normal_force, 0, tyre.pacejkaParams._coeffs2, true);
|
||||
frictionForces.y *= -1.0;
|
||||
// Double3 frictionForces = tyre.FrictionForce(normal_force, _slipLon, _slipLat, 0, tyre.pacejkaParams._coeffs);
|
||||
|
||||
/* if (name[name.Length - 2] == 'f' && t % 250 == 0) {
|
||||
Debug.LogFormat("Fz = {0} Fx = {1} Slip = {2} Vlon = {4} Name = {3}", normal_force, frictionForces.x, _slipLon, name, localVelocity.z);
|
||||
}*/
|
||||
|
||||
|
||||
double T_engine = driveTorque;
|
||||
double T_break = brakeTorque * -Math.Sign(angularVelocity);
|
||||
|
||||
double T_input = T_engine + T_break;
|
||||
|
||||
double T_reaction = frictionForces.x * TotalWheelRadius;
|
||||
|
||||
reactionTorque = T_reaction;
|
||||
/*if (T_input < 0 && (T_reaction < T_input) || (T_input > 0 && (T_reaction > T_input)))
|
||||
{
|
||||
T_reaction = T_input;
|
||||
}*/
|
||||
|
||||
|
||||
double T_rolling_resist = tyre.GetRollingResistance(normal_force, TotalWheelRadius, localVelocity.z, 1.0f);
|
||||
|
||||
double angularAcceleration = (T_engine - T_reaction + T_break - T_rolling_resist) /inertiaMomentum;
|
||||
angularVelocity += angularAcceleration * dt;
|
||||
//dw/dt = (T_engine + T_reaction - T_break - T_friction) / WheelInertia
|
||||
|
||||
angle += angularVelocity * dt;
|
||||
|
||||
Vector3 lonForce = transform.forward * (float)frictionForces.x;
|
||||
Vector3 latForce = -transform.right * (float)frictionForces.y;
|
||||
force = lonForce + latForce;
|
||||
}
|
||||
|
||||
public void UpdateContactPoint(Vector3 up) {
|
||||
var raycastRay = new Ray(transform.position, -up);
|
||||
|
||||
_contact.hasHit = false;
|
||||
_contact.grounded = false;
|
||||
if (Physics.Raycast(raycastRay, out _contact.hit)) {
|
||||
_contact.hasHit = true;
|
||||
if (_contact.hit.distance <= TotalWheelRadius) {
|
||||
_contact.grounded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OnDrawGizmos() {
|
||||
|
||||
if (tyre == null) {
|
||||
return;
|
||||
}
|
||||
//DrawLabels();
|
||||
var forward = transform.forward;
|
||||
var right = transform.right;
|
||||
|
||||
//draw wheel radius
|
||||
var wheelCenter = transform.position;
|
||||
//var rollMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.AngleAxis(_shaft.Angle * Mathf.Rad2Deg, right), Vector3.one);
|
||||
var rollMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.AngleAxis(0, right), Vector3.one);
|
||||
var radiusP0 = -forward * (float)TotalWheelRadius;
|
||||
var radiusP1 = forward * (float)(TotalWheelRadius * 1.2);
|
||||
radiusP0 = rollMatrix.MultiplyPoint3x4(radiusP0);
|
||||
radiusP1 = rollMatrix.MultiplyPoint3x4(radiusP1);
|
||||
Gizmos.DrawLine(wheelCenter + radiusP0, wheelCenter + radiusP1);
|
||||
|
||||
//draw wheel outer circle
|
||||
AdvancedGizmo.DrawCircle(wheelCenter, right, (float)TotalWheelRadius, Color.green, 36);
|
||||
|
||||
//draw wheel inner circle
|
||||
AdvancedGizmo.DrawCircle(wheelCenter, right, (float)discRadius, Color.green, 36);
|
||||
|
||||
//draw forces
|
||||
/*Gizmos.color = Color.red;
|
||||
Gizmos.DrawLine(wheelCenter, wheelCenter + lonForce);
|
||||
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawLine(wheelCenter, wheelCenter + latForce);
|
||||
|
||||
Gizmos.color = Color.blue;
|
||||
Gizmos.DrawLine(wheelCenter, wheelCenter + latForce + lonForce);
|
||||
|
||||
Gizmos.color = Color.black;
|
||||
Gizmos.DrawLine(wheelCenter, wheelCenter + forward * 100);*/
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f0f3b45c60aa04c4c9e0c48be7e96fa3
|
||||
timeCreated: 1453062224
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user