- Home /
Question is off-topic or not relevant
Android car controls c#
ok, so a while back I made a simple car game for android and put it on the amazon appstore. shortly after I did, my project became corrupt and could not work with it. I brought my project back to life but the physics were all messed up. so I found this C# script called jcar with somewhat good physics, works nicely on a pc build but I want to make an updated version of my app. the problem is I only know javascript and BARELY any c#. I need to add touch controls to this script using gui textures like I did with my old scripts. the thing is I have little to no idea how jcar applies its engine values and stuff like that. could you guys please please help me port this script to android. I have seen a version ported to jave but doesn't really help because the way its setup is ridiculous. I will attatch a copy of the jcar script and a copy of my android control script just for reference (so you can see how the gui textures work). please help me port this to android. thanks Luke
/**
* A simple car physics script using wheel colliders.
* Jaap Kreijkamp [jaap] at [ctrl-j.com.au]
*
* orientation should be that front of car is in direction of
* the 'blue arrow' in Unity, the roof should be in direction of
* the green angle. Thus with rotation 0, 0, 0, adding 1 to Z
* will move car 1m forward, adding 1 to Y will move car 1m
* upward. The wheels should be children of the car object this
* script is added to and connected to the wheelFL, wheelFR, ...
* variables.
*
* Please modify script and do whatever you like with it,
* in it's current state it should give a working car,
* but by no means perfect (or even close) behavior.
* It's my first attempt and don't really need in my current
* project so haven't put too much effort into it to perfect
* it. As often people are looking for help to getting
* a car working with wheel colliders, I'd appreciate when
* improvements are posted back on the unity forums.
*
* Lastly, thanks to all the people helping me on forums (especially
* the order of initialisation problem and other example
* code that helped me much in learning how to do stuff like
* this).
*/
using UnityEngine;
using System.Collections;
public enum JWheelDrive {
Front = 0,
Back = 1,
All = 2
}
public class JCar : MonoBehaviour {
// if connected the controls will block if object not active
// (for example steer only if car camera is active).
public GameObject checkForActive;
public Transform wheelFR; // connect to Front Right Wheel transform
public Transform wheelFL; // connect to Front Left Wheel transform
public Transform wheelBR; // connect to Back Right Wheel transform
public Transform wheelBL; // connect to Back Left Wheel transform
public float suspensionDistance = 0.2f; // amount of movement in suspension
public float springs = 1000.0f; // suspension springs
public float dampers = 2f; // how much damping the suspension has
public float wheelRadius = 0.25f; // the radius of the wheels
public float torque = 100f; // the base power of the engine (per wheel, and before gears)
public float brakeTorque = 2000f; // the power of the braks (per wheel)
public float wheelWeight = 3f; // the weight of a wheel
public Vector3 shiftCentre = new Vector3(0.0f, -0.25f, 0.0f); // offset of centre of mass
public float maxSteerAngle = 30.0f; // max angle of steering wheels
public JWheelDrive wheelDrive = JWheelDrive.Front; // which wheels are powered
public float shiftDownRPM = 1500.0f; // rpm script will shift gear down
public float shiftUpRPM = 2500.0f; // rpm script will shift gear up
public float idleRPM = 500.0f; // idle rpm
public float fwdStiffness = 0.1f; // for wheels, determines slip
public float swyStiffness = 0.1f; // for wheels, determines slip
// gear ratios (index 0 is reverse)
public float[] gears = { -10f, 9f, 6f, 4.5f, 3f, 2.5f };
// automatic, if true car shifts automatically up/down
public bool automatic = true;
public float killEngineSoundTimeout = 3.0f; // time until engine sound is cut off (in s.)
// table of efficiency at certain RPM, in tableStep RPM increases, 1.0f is 100% efficient
// at the given RPM, current table has 100% at around 2000RPM
float[] efficiencyTable = { 0.6f, 0.65f, 0.7f, 0.75f, 0.8f, 0.85f, 0.9f, 1.0f, 1.0f, 0.95f, 0.80f, 0.70f, 0.60f, 0.5f, 0.45f, 0.40f, 0.36f, 0.33f, 0.30f, 0.20f, 0.10f, 0.05f };
// the scale of the indices in table, so with 250f, 750RPM translates to efficiencyTable[3].
float efficiencyTableStep = 250.0f;
int currentGear = 1; // duh.
// shortcut to the component audiosource (engine sound).
AudioSource audioSource;
// every wheel has a wheeldata struct, contains useful wheel specific info
class WheelData {
public Transform transform;
public GameObject go;
public WheelCollider col;
public Vector3 startPos;
public float rotation = 0.0f;
public float maxSteer;
public bool motor;
};
WheelData[] wheels; // array with the wheel data
// setup wheelcollider for given wheel data
// wheel is the transform of the wheel
// maxSteer is the angle in degrees the wheel can steer (0f for no steering)
// motor if wheel is driven by engine or not
WheelData SetWheelParams(Transform wheel, float maxSteer, bool motor) {
if (wheel == null) {
throw new System.Exception("wheel not connected to script!");
}
WheelData result = new WheelData(); // the container of wheel specific data
// we create a new gameobject for the collider and move, transform it to match
// the position of the wheel it represents. This allows us to do transforms
// on the wheel itself without disturbing the collider.
GameObject go = new GameObject("WheelCollider");
go.transform.parent = transform; // the car, not the wheel is parent
go.transform.position = wheel.position; // match wheel pos
// create the actual wheel collider in the collider game object
WheelCollider col = (WheelCollider) go.AddComponent(typeof(WheelCollider));
col.motorTorque = 0.0f;
// store some useful references in the wheeldata object
result.transform = wheel; // access to wheel transform
result.go = go; // store the collider game object
result.col = col; // store the collider self
result.startPos = go.transform.localPosition; // store the current local pos of wheel
result.maxSteer = maxSteer; // store the max steering angle allowed for wheel
result.motor = motor; // store if wheel is connected to engine
return result; // return the WheelData
}
// Use this for initialization
void Start () {
// 4 wheels, if needed different size just modify and modify
// the wheels[...] block below.
wheels = new WheelData[4];
// setup wheels
bool frontDrive = (wheelDrive == JWheelDrive.Front) || (wheelDrive == JWheelDrive.All);
bool backDrive = (wheelDrive == JWheelDrive.Back) || (wheelDrive == JWheelDrive.All);
// we use 4 wheels, but you can change that easily if neccesary.
// this is the only place that refers directly to wheelFL, ...
// so when adding wheels, you need to add the public transforms,
// adjust the array size, and add the wheels initialisation here.
wheels[0] = SetWheelParams(wheelFR, maxSteerAngle, frontDrive);
wheels[1] = SetWheelParams(wheelFL, maxSteerAngle, frontDrive);
wheels[2] = SetWheelParams(wheelBR, 0.0f, backDrive);
wheels[3] = SetWheelParams(wheelBL, 0.0f, backDrive);
// found out the hard way: some parameters must be set AFTER all wheel colliders
// are created, like wheel mass, otherwise your car will act funny and will
// flip over all the time.
foreach (WheelData w in wheels) {
WheelCollider col = w.col;
col.suspensionDistance = suspensionDistance;
JointSpring js = col.suspensionSpring;
js.spring = springs;
js.damper = dampers;
col.suspensionSpring = js;
col.radius = wheelRadius;
col.mass = wheelWeight;
// see docs, haven't really managed to get this work
// like i would but just try out a fiddle with it.
WheelFrictionCurve fc = col.forwardFriction;
fc.asymptoteValue = 5000.0f;
fc.extremumSlip = 2.0f;
fc.asymptoteSlip = 20.0f;
fc.stiffness = fwdStiffness;
col.forwardFriction = fc;
fc = col.sidewaysFriction;
fc.asymptoteValue = 7500.0f;
fc.asymptoteSlip = 2.0f;
fc.stiffness = swyStiffness;
col.sidewaysFriction = fc;
}
// we move the centre of mass (somewhere below the centre works best.)
rigidbody.centerOfMass += shiftCentre;
// shortcut to audioSource should be engine sound, if null then no engine sound.
audioSource = (AudioSource) GetComponent(typeof(AudioSource));
if (audioSource == null) {
Debug.Log("No audio source, add one to the car with looping engine noise (but can be turned off");
}
}
void Update() {
if (Input.GetKeyDown("page up")) {
ShiftUp();
}
if (Input.GetKeyDown("page down")) {
ShiftDown();
}
}
float shiftDelay = 0.0f;
// handle shifting a gear up
public void ShiftUp() {
float now = Time.timeSinceLevelLoad;
// check if we have waited long enough to shift
if (now < shiftDelay) return;
// check if we can shift up
if (currentGear < gears.Length - 1) {
currentGear ++;
// we delay the next shift with 1s. (sorry, hardcoded)
shiftDelay = now + 1.0f;
}
}
// handle shifting a gear down
public void ShiftDown() {
float now = Time.timeSinceLevelLoad;
// check if we have waited long enough to shift
if (now < shiftDelay) return;
// check if we can shift down (note gear 0 is reverse)
if (currentGear > 0) {
currentGear --;
// we delay the next shift with 1/10s. (sorry, hardcoded)
shiftDelay = now + 0.1f;
}
}
float wantedRPM = 0.0f; // rpm the engine tries to reach
float motorRPM = 0.0f;
float killEngine = 0.0f;
// handle the physics of the engine
void FixedUpdate () {
float delta = Time.fixedDeltaTime;
float steer = 0; // steering -1.0 .. 1.0
float accel = 0; // accelerating -1.0 .. 1.0
bool brake = false; // braking (true is brake)
if ((checkForActive == null) || checkForActive.active) {
// we only look at input when the object we monitor is
// active (or we aren't monitoring an object).
steer = Input.GetAxis("Horizontal");
accel = Input.GetAxis("Vertical");
brake = Input.GetButton("Jump");
}
// handle automatic shifting
if (automatic && (currentGear == 1) && (accel < 0.0f)) {
ShiftDown(); // reverse
}
else if (automatic && (currentGear == 0) && (accel > 0.0f)) {
ShiftUp(); // go from reverse to first gear
}
else if (automatic && (motorRPM > shiftUpRPM) && (accel > 0.0f)) {
ShiftUp(); // shift up
}
else if (automatic && (motorRPM < shiftDownRPM) && (currentGear > 1)) {
ShiftDown(); // shift down
}
if (automatic && (currentGear == 0)) {
accel = - accel; // in automatic mode we need to hold arrow down for reverse
}
if (accel < 0.0f) {
// if we try to decelerate we brake.
brake = true;
accel = 0.0f;
wantedRPM = 0.0f;
}
// the RPM we try to achieve.
wantedRPM = (5500.0f * accel) * 0.1f + wantedRPM * 0.9f;
float rpm = 0.0f;
int motorizedWheels = 0;
bool floorContact = false;
// calc rpm from current wheel speed and do some updating
foreach (WheelData w in wheels) {
WheelHit hit;
WheelCollider col = w.col;
// only calculate rpm on wheels that are connected to engine
if (w.motor) {
rpm += col.rpm;
motorizedWheels++;
}
// calculate the local rotation of the wheels from the delta time and rpm
// then set the local rotation accordingly (also adjust for steering)
w.rotation = Mathf.Repeat(w.rotation + delta * col.rpm * 360.0f / 60.0f, 360.0f);
w.transform.localRotation = Quaternion.Euler(w.rotation, col.steerAngle, 0.0f);
// let the wheels contact the ground, if no groundhit extend max suspension distance
Vector3 lp = w.transform.localPosition;
if (col.GetGroundHit(out hit)) {
lp.y -= Vector3.Dot(w.transform.position - hit.point, transform.up) - col.radius;
floorContact = floorContact || (w.motor);
}
else {
lp.y = w.startPos.y - suspensionDistance;
}
w.transform.localPosition = lp;
}
// calculate the actual motor rpm from the wheels connected to the engine
// note we haven't corrected for gear yet.
if (motorizedWheels > 1) {
rpm = rpm / motorizedWheels;
}
// we do some delay of the change (should take delta instead of just 95% of
// previous rpm, and also adjust or gears.
motorRPM = 0.95f * motorRPM + 0.05f * Mathf.Abs(rpm * gears[currentGear]);
if (motorRPM > 5500.0f) motorRPM = 5500.0f;
// calculate the 'efficiency' (low or high rpm have lower efficiency then the
// ideal efficiency, say 2000RPM, see table
int index = (int) (motorRPM / efficiencyTableStep);
if (index >= efficiencyTable.Length) index = efficiencyTable.Length - 1;
if (index < 0) index = 0;
// calculate torque using gears and efficiency table
float newTorque = torque * gears[currentGear] * efficiencyTable[index];
// go set torque to the wheels
foreach (WheelData w in wheels) {
WheelCollider col = w.col;
// of course, only the wheels connected to the engine can get engine torque
if (w.motor) {
// only set torque if wheel goes slower than the expected speed
if (Mathf.Abs(col.rpm) > Mathf.Abs(wantedRPM)) {
// wheel goes too fast, set torque to 0
col.motorTorque = 0;
}
else {
//
float curTorque = col.motorTorque;
col.motorTorque = curTorque * 0.9f + newTorque * 0.1f;
}
}
// check if we have to brake
col.brakeTorque = (brake)?brakeTorque:0.0f;
// set steering angle
col.steerAngle = steer * w.maxSteer;
}
// if we have an audiosource (motorsound) adjust pitch using rpm
if (audioSource != null) {
// calculate pitch (keep it within reasonable bounds)
float pitch = Mathf.Clamp(1.0f + ((motorRPM - idleRPM) / (shiftUpRPM - idleRPM) * 2.5f), 1.0f, 10.0f);
audioSource.pitch = pitch;
if (motorRPM > 100) {
// turn on sound if it's not playing yet and RPM is > 100.
if (!audioSource.isPlaying) {
audioSource.Play();
}
// how long we should wait with engine RPM <= 100 before killing engine sound
killEngine = Time.time + killEngineSoundTimeout;
}
else if ((audioSource.isPlaying) && (Time.time > killEngine)) {
// standing still, kill engine sound.
audioSource.Stop();
}
}
}
public void OnGUI() {
if (checkForActive.active) {
// calculate actual speed in Km/H (SI metrics rule, so no inch, yard, foot,
// stone, or other stupid length measure!)
float speed = rigidbody.velocity.magnitude * 3.6f;
// message to display
string msg = "Speed " + speed.ToString("f0") + "Km/H, " + motorRPM.ToString("f0") + "RPM, gear " + currentGear; // + " torque " + newTorque.ToString("f2") + ", efficiency " + table[index].ToString("f2");
GUILayout.BeginArea(new Rect(Screen.width -250 - 32, 32, 250, 40), GUI.skin.window);
GUILayout.Label(msg);
GUILayout.EndArea();
}
}
}
and now my script:
// These variables allow the script to power the wheels of the car.
var FrontLeftWheel : WheelCollider;
var FrontRightWheel : WheelCollider;
var BackLeftWheel : WheelCollider;
var BackRightWheel : WheelCollider;
var gasButton : GUITexture;
var brakeButton : GUITexture;
var leftTurnButton : GUITexture;
var rightTurnButton : GUITexture;
var reverseButton : GUITexture;
var motorInputTouch : int = 0;
var brakePower : float = 200;
// These variables are for the gears, the array is the list of ratios. The script
// uses the defined gear ratios to determine how much torque to apply to the wheels.
var GearRatio : float[];
var CurrentGear : int = 0;
// These variables are just for applying torque to the wheels and shifting gears.
// using the defined Max and Min Engine RPM, the script can determine what gear the
// car needs to be in.
var EngineTorque : float = 230.0;
var MaxEngineRPM : float = 3000.0;
var MinEngineRPM : float = 1000.0;
private var EngineRPM : float = 0.0;
function Awake() {
gasButton = GameObject.Find("gas").guiTexture;
brakeButton = GameObject.Find("Brake").guiTexture;
leftTurnButton = GameObject.Find("left").guiTexture;
rightTurnButton = GameObject.Find("right").guiTexture;
reverseButton = GameObject.Find("reverse").guiTexture;
}
function Start () {
// I usually alter the center of mass to make the car more stable. I'ts less likely to flip this way.
rigidbody.centerOfMass += Vector3(0, -.75, .25);
}
function Update () {
for (var touch : Touch in Input.touches)
{
if (touch.phase == TouchPhase.Stationary && gasButton.HitTest (touch.position)) {
motorInputTouch = 1;
}
else if (touch.phase == TouchPhase.Ended && gasButton.HitTest) {
motorInputTouch = 0;
}
if (touch.phase == TouchPhase.Stationary && brakeButton.HitTest (touch.position)) {
brakePower = 90;
}
else if (touch.phase == TouchPhase.Ended && brakeButton.HitTest) {
brakePower = 0;
}
if (touch.phase == TouchPhase.Stationary && leftTurnButton.HitTest (touch.position)) {
FrontLeftWheel.steerAngle = -15.0;
FrontRightWheel.steerAngle = -15.0;
}
else if (touch.phase == TouchPhase.Ended && leftTurnButton.HitTest) {
FrontLeftWheel.steerAngle = 0;
FrontRightWheel.steerAngle = 0;
}
if (touch.phase == TouchPhase.Stationary && rightTurnButton.HitTest (touch.position)) {
FrontLeftWheel.steerAngle = 15.0;
FrontRightWheel.steerAngle = 15.0;
}
else if (touch.phase == TouchPhase.Ended && rightTurnButton.HitTest) {
FrontLeftWheel.steerAngle = 0;
FrontRightWheel.steerAngle = 0;
}
if (touch.phase == TouchPhase.Stationary && reverseButton.HitTest (touch.position)) {
motorInputTouch = -1;
}
else if (touch.phase == TouchPhase.Ended && reverseButton.HitTest) {
motorInputTouch = 0;
}
}
// Compute the engine RPM based on the average RPM of the two wheels, then call the shift gear function
EngineRPM = (FrontLeftWheel.rpm + FrontRightWheel.rpm)/2 * GearRatio[CurrentGear];
ShiftGears();
// set the audio pitch to the percentage of RPM to the maximum RPM plus one, this makes the sound play
// up to twice it's pitch, where it will suddenly drop when it switches gears.
audio.pitch = Mathf.Abs(EngineRPM / MaxEngineRPM) + 1.0 ;
// this line is just to ensure that the pitch does not reach a value higher than is desired.
if ( audio.pitch > 2.0 ) {
audio.pitch = 2.0;
}
// finally, apply the values to the wheels. The torque applied is divided by the current gear, and
// multiplied by the user input variable.
FrontLeftWheel.motorTorque = EngineTorque / GearRatio[CurrentGear] * motorInputTouch;
FrontRightWheel.motorTorque = EngineTorque / GearRatio[CurrentGear] * motorInputTouch;
BackRightWheel.brakeTorque = brakePower;
BackLeftWheel.brakeTorque = brakePower;
// the steer angle is an arbitrary value multiplied by the user input.
//FrontLeftWheel.steerAngle = 10 * Input.GetAxis("Horizontal");
//FrontRightWheel.steerAngle = 10 * Input.GetAxis("Horizontal");
}
function ShiftGears() {
// this funciton shifts the gears of the vehcile, it loops through all the gears, checking which will make
// the engine RPM fall within the desired range. The gear is then set to this "appropriate" value.
if ( EngineRPM >= MaxEngineRPM ) {
var AppropriateGear : int = CurrentGear;
for ( var i = 0; i < GearRatio.length; i ++ ) {
if ( FrontLeftWheel.rpm * GearRatio[i] < MaxEngineRPM ) {
AppropriateGear = i;
break;
}
}
CurrentGear = AppropriateGear;
}
if ( EngineRPM <= MinEngineRPM ) {
AppropriateGear = CurrentGear;
for ( var j = GearRatio.length-1; j >= 0; j -- ) {
if ( FrontLeftWheel.rpm * GearRatio[j] > MinEngineRPM ) {
AppropriateGear = j;
break;
}
}
CurrentGear = AppropriateGear;
}
}
Sorry, but your question is not suitable for Unity Answers. Please use the Unity Forum for discussions such as "Port my code" : http://forum.unity3d.com/forums/17-Collaboration
Unity Answers is here to help you solve any specific problems you have.