Don’t access Unity projects via network shares

I must admit that I feel stupid for not having realized this earlier but I was updating / dual using Unity 3.5 and 4.0 as well when this happened, so I was a little unsure what was causing it, but here goes:

If you access your Unity project on a network share (like: \\server\project\game) then it will run quite well except a small error that pops up in the logs:

UriFormatException: Absolute URI is too short

This is part of the interface that is supposed to link Unity and MonoDevelop together and it is trying to tell you that it can’t generate the solution files (.sln) that MonoDevelop needs to properly integrate into Unity. It’s failure is mainly noticable because MonoDevelop’s intellisense or code complete completely stops working, degrading Mono to a simple text editor and making it very much frustrating to work with it.

If you are unsure if you are having this problem then simply try to use the > Assets > Sync MonoDevelop Project menu. That should instantly generate the error in the logs.

The easiest solution (besides having your project files on your local harddrive) is to map the network share to a network drive and everything will be ok.

Offroad Speeder v02

I updated the version of the Speeder with the CarSmoothFollow Script from the Unity Wiki.

I also added detachable spoilers via the Hinge Joints (setting the Limits very narrow so that they don’t move by themselves). The Fixed Joints seemed a good idea at first but they wouldn’t keep still. So to prevent the spoilers from wobbeling around I used the Hinge Joints instead.

I also tweaked the physics a little bit more but without getting significantly better handling from the car…

Speeder v02

Basic Car Setup

My current findings for setting up a car rig have been the following:

speeder02

As we usually deal with several wheels I use arrays to store everything. This lets me add more wheels without breaking into a sweat or having to go copy-paste all over the place.

The base vehicle Axis contains four wheel axis objects that have the WheelColliders. Parented to these we find the wheel meshes. To link these up to the script we have to add these definitions.

public Transform[] wheels;
public WheelCollider[] colliders;
public float[] powers;
public float[] brakes;
public float[] rotations;
public Transform[] steerings;
public Transform centerOfMass;

wheels is for the mesh objects
colliders is for the wheelcolliders
powers is for the amount of torque we apply to each wheel
brakes is for the amount of brakes we apply to each wheel
rotations is for the rotation ratio that is applied to each wheel
steerings is for the amount of steering we apply to each wheel
centerOfMass defines the center of gravity

I set all the WheelCollider settings via script. You can also do this in the inspector, which is much more convinient since you are now able to edit all wheels at one, but in earlier versions you had to do this for every wheel individually.

void Start () {
rigidbody.centerOfMass = centerOfMass.localPosition;

JointSpring suspensionSpring = new JointSpring();
suspensionSpring.spring = 2048.0F;
suspensionSpring.damper = 16.0F;
suspensionSpring.targetPosition = 0.0F;

WheelFrictionCurve forwardFriction = new WheelFrictionCurve();
forwardFriction.extremumSlip = 2.0F;
forwardFriction.extremumValue = 1024.0F;
forwardFriction.asymptoteSlip = 4.0F;
forwardFriction.asymptoteValue = 512.0F;
forwardFriction.stiffness = 1.0F;

WheelFrictionCurve sidewaysFriction = new WheelFrictionCurve();
sidewaysFriction.extremumSlip = 8.0F;
sidewaysFriction.extremumValue = 128.0F;
sidewaysFriction.asymptoteSlip = 16.0F;
sidewaysFriction.asymptoteValue = 64.0F;
sidewaysFriction.stiffness = 1.0F;

for (int i = 0; i < colliders.Length; i++) {
colliders[i].suspensionDistance = 1.0F;
colliders[i].suspensionSpring = suspensionSpring;
colliders[i].forwardFriction = forwardFriction;
colliders[i].sidewaysFriction = sidewaysFriction;
}
}

I first have to define the JointSpring, which defines how much the wheel colliders will bounce. Usually you'll want to set everything to "0" and then increase the spring value until the car stays up by itself. This is very dependant on the number of wheels and the weight of the car. Then set the damper until the car stops bouncing.

The WheelFrictionCurves are a bit more difficult. They will usually define when the wheels will start to spin (forwardFriction) and when the car begins to slide (sidewaysFriction). I still haven't found a good method to set these up right but usually you'll want to set the extremumValue to a force value where you want the car to skid and then set the asymptoteValue to a value of force or grip that you want to have when the wheels have begun to slide.

Finally the values are applied to each individual WheelCollider including the suspensionDistance, which defines how far down the suspension will travel when the wheels are not touching the ground.

Next are the physics calculcations which take place in the FixedUpdate function because these have to run at a constant rate, independant of the frame rate.

void FixedUpdate() {
float speed = Vector3.Magnitude(rigidbody.velocity);
float rpm = (colliders[0].rpm + colliders[1].rpm) / 2.0F;
float torque = 0.0F;
float brake = 0.0F;
float throttle = Input.GetAxis("Vertical");
float steering = Input.GetAxis("Horizontal");
float steering_magnitude = speed;
float steering_range = 45.0F;

if (throttle > 0.0F) {
torque = throttle;
} else if (throttle < 0.0F) {
brake = Mathf.Abs(throttle);
}

if (steering_magnitude > 0.0F) {
float ratio = steering_magnitude / 45.0F;
steering_range *= 1.0F - ratio;
}
steering_range = Mathf.Clamp(steering_range, 15.0F, 45.0F);

speed gets the velocity of the car
rpm gets the rpm values from the front wheels
throttle reads the throttle input from the player
torque torque applied by the player
brake brakes applied by the player
steering amount of steering applied by the player
steering_magnitude controls the amount of steering and is decreased at higher speeds to prevent the car from rolling over
steering_range maximum steering angle

for (int i = 0; i < steerings.Length; i++) {
steerings[i].localEulerAngles = new Vector3(0.0F, steering * steering_range, 0.0F);
}

for (int i = 0; i < colliders.Length; i++) {
colliders[i].motorTorque = torque * powers[i];
colliders[i].brakeTorque = brake * brakes[i];
wheels[i].Rotate(colliders[i].rpm * rotations[i] * Time.deltaTime,0 ,0);
UpdateWheelHeight(wheels[i], colliders[i]);
}

rigidbody.AddForce(Vector3.down * downforce * Vector3.Magnitude(rigidbody.velocity));
}

Apply steering to the wheels (by multiplying these with the array values I can freely set up steering of the wheels in the front or of all wheels or even only the back wheels

Then apply the throttle and/or brake forces to all wheels, again multiplying these with the array values to switch between FWD / RWD / 4WD, and even braking can be set up to equal the usual 60% front / 40% rear braking force

The visible wheel meshes don't realy contribute to the simulation but are necessary for the player to see what's going on. Therefore the rotation values for the wheels are somewhat approximated. I haven't yet found a reliable calculation example for this allthough I am almost certain there should be one. Simple experiment for the time beeing.

Last but not least the function that sets the wheel height. This was taken from some other example I currently don't remember - probably the official Unity car tutorial.

void UpdateWheelHeight(Transform wheelTransform, WheelCollider collider) {
Vector3 localPosition = wheelTransform.localPosition;
WheelHit hit = new WheelHit();
// see if we have contact with ground
if (collider.GetGroundHit(out hit)) {
// calculate the distance between current wheel position and hit point, correct
// wheel position
localPosition.y -= Vector3.Dot(wheelTransform.position - hit.point, transform.up) - collider.radius;
} else {
// no contact with ground, just extend wheel position with suspension distance
localPosition = -Vector3.up * collider.suspensionDistance;
}
// actually update the position
wheelTransform.localPosition = localPosition;
}

There's also a good thread in the Unity forums which deals with adding anti rollbars to the simulation. I removed these again since they didn't improve the handling in later tests of the setup and I wanted to eliminate factors that may make the handling unstable. There'll probably be setups where these anti rollbars serve a usefull purpose:

Unity Forum - anti rollbars

Offroad Speeder

I’ve experimented with an offroad vehicle, based on the experiences gained with the truck. The basic setup wasn’t too difficult and even jumping and speeding over the rough terrain was pretty nice, but trying to point the car in the right direction or even getting it through the poles grew into quite a challenge because the car slips away much to easily. I’m not sure this can be fixed with simple adjustments to the wheel friction curves.

In it’s normal state the car reacts wonderfully. It accellerates, with little wheel spin, the suspension reacts accordingly. When going into a turn the car begins to slip smoothly outwards, drifting nicely. When going on rough terrain the car jumps an, naturally, is unsteerable while the wheels are airborne. The trouble starts when the car slams onto the ground or is pressed onto the terrain, as it increases its pressure. Somehow the wheel friction curves lose control and the back of the car brakes away violently – sometimes even instantly. I do guess that this is an intended behaviour, but the result leads to an uncontrollable car (most of the time – if a certain speed is reached) and this is not what I had in mind for a fun offroader.

Another challenge was proposed with the following camera. The usual smooth following camera script has the problem that the camera is unable to maintain a fixed distance to the vehicle. My current attempt locks the camera behind the car and smooths out the vertical position. It’s nice but the camera tends to lose the car from view if it goes into a dip… ah well, that’ll also require improvements.

speeder01

Speeder v01

Improved Trucking Physics

Had another go at the WheelCollider, as I wasn’t convinced that completely custom built vehicle physics would save that much time, and I have made a couple of improvements. Most of all the Suspension is now correct, as I was doing it wrong most of the time:

  • The best way to start is to set “Spring”, “Damper” and “Target Position” to zero.
  • Set “Suspension Distance” to the distance you want the wheels to drop when the vehicle is in the air.
  • Set “Spring” to something high (try 10240, depending on the weight and number of wheels of the vehicle) and start the game. Tweak the “Spring” value until the suspension is strong enough to keep the body of the vehicle from hitting the ground.
  • Now set the “Damper” to roughly 1/20 of the “Spring” value and tweak it until the vehicle stops bouncing (depending on how much you like it to bounce)
  • Note that everything you attach to the vehicle as well as the center of mass will affect the suspension behaviour and you will probably have to tweak it all over again

Here’s the new version with the improved settings.

Trucking v02

Trucking Physics

I experimented a little with the Unity physics and wheel collider features to try to get a working truck + trailer configuration working for rough terrain.

As I had already done some truck and trailer physics for a farm simulator at work I wasn’t expecting this to be that tricky.

trucking01

I uploaded my current state with still quite a number of problems and my research in forums has only resulted in finding many other developers not really happy with the build in car physics, saying you should do your own or use one off the Asset Store.

Trucking v01

I’m not yet ready to give up on the built in system (but close).

Not working at the moment are:

  • Suspension isn’t following the ground properly, especially with the twin-axles. Resulting in one axle having ground contact while the other is hanging in the air. I may have to reduce the real twin axles to a single virtual one, driving the visible twin axles.
  • The truck tips over to easily. Lowering the center of gravity will fix this, but when tipping over the truck will then flip back to it’s wheels, looking pretty weird. I may have to animated the center of gravity so that it will rise if the truck is tipping over and lower it while it is going normally
  • The trailer sometimes drags the truck pretty violently. It uses a configurable joint with restrictions but these sometimes pull very hard on the truck (or it may lift the truck into the air when braking hard).
  • Sometimes the truck will not be able to gain speed. Very strange, I can only think of there being some large friction between truck and trailer leading to this behaviour.
  • Grip and slip isn’t configured properly. Either the wheels simply spin or the wheels stick so violently to the ground that it tips over. It would be nice to get more information out of the wheel collider. This again speaks towards using a custom made system.

iTween

iTween must be one of the single most usefull scripts that are available. There is just one thing that makes working with it slightly obscure, because of the way extended options have to be added via the hash table by using the iTween.Hash() function.

iTween.MoveTo(exhibitObject, iTween.Hash(
    "position", presenterTransform.position,
    "time", reloadDelay,
    "islocal", false,
    "easetype", iTween.EaseType.easeOutQuad
));

Realtime Cubemap for Reflections

A nice script for creating a real time cubemap from an object position for use with a reflection shader to create realtime reflections in Unity. Converted from the JavaScript example. Note: This script requires the Unity Pro license.

using UnityEngine;
using System.Collections;

public class RealtimeCubemap : MonoBehaviour {

    public int cubemapSize = 128;
    public bool oneFacePerFrame = false;
    private Camera cam;
    private RenderTexture rtex;
    private GameObject go;

    [ExecuteInEditMode]
    void Start() {
        // render all six faces at startup
        UpdateCubemap(63);
    }

    void LateUpdate() {
        if (oneFacePerFrame) {
            int faceToRender = Time.frameCount % 6;
            int faceMask = 1 << faceToRender;
            UpdateCubemap(faceMask);
        } else {
            UpdateCubemap(63); // all six faces
        }
    }

    void UpdateCubemap(int faceMask) {
        if (!cam) {
            go = new GameObject("CubemapCamera");
            go.AddComponent(typeof(Camera));
            go.hideFlags = HideFlags.HideAndDontSave;
            go.transform.position = transform.position;
            go.transform.rotation = Quaternion.identity;
            cam = go.camera;
            cam.farClipPlane = 100; // don't render very far into cubemap
            cam.enabled = false;
        }

        if (!rtex) {    
            rtex = new RenderTexture(cubemapSize, cubemapSize, 16);
            rtex.isCubemap = true;
            rtex.hideFlags = HideFlags.HideAndDontSave;
            renderer.sharedMaterial.SetTexture ("_Cube", rtex);
        }

        cam.transform.position = transform.position;
        cam.RenderToCubemap(rtex, faceMask);
    }

    void OnDisable() {
        DestroyImmediate (cam);
        DestroyImmediate (rtex);
    }
}

String formatting

Something small but usefull is the String.Format function, with which you can format your numerical outputs:

using System;
using System.Collections;

...

// formatted float with three zero padded digits
// in front of the "." and three digits after it.
debugText += String.Format("H{0:000.000}\n", heading);

// formatted int with formats for positive;negative;zero values
debugText += String.Format("S{0:+000;-000; 000}\n", speed);

Calculate intersection between to lines

Here’s something that will calculate where two lines will intersect. If they don’t intersect then the two resulting vectors will mark the points where both lines are closest together.

/* setup vectors */
 Vector3 l1begin = playerCurrentPosition;
 Vector3 l1end = playerTargetPosition - l1begin;
 Vector3 l2begin = enemyCurrentPosition;
 Vector3 l2end = enemyTargetPosition - l2begin;
/* calculate vectors */
 Vector3 n = Vector3.Cross(l1end, l2end);
 Vector3 u = Vector3.Cross(n, l1begin - l2begin) / Vector3.Dot(n, n);
 Vector3 intersection1 = l1begin - l1end * Vector3.Dot(l2end, u);
 Vector3 intersection2 = l2begin - l2end * Vector3.Dot(l1end, u);
/* draw vectors */
 Debug.DrawRay(l1begin, l1end, Color.cyan);
 Debug.DrawRay(l2begin, l2end, Color.magenta);
 Debug.DrawLine(intersection1, intersection2, Color.white);