Archive for November, 2008

Simple 3rd Person Camera Controller in Unity

Thursday, November 27th, 2008

Someone in the Unity Forums asked for help with a 3rd person camera controller. I decided to post my code there for him/her, and so I’m going to post it here, too. This is a javascript that will track an object in the world and move the camera towards it if the object gets too far away.

I like code examples that are simple and easy to read, and while this code could be even more stripped down, it’s pretty simple. I included comments for every line.

// Put this script on a GameObject with your camera and a character controller on it.

var target: GameObject;  // Assign this to your player
var maxDist = 20.0; // distance before triggering follow
var minDist = 10.0; // distance before untriggering follow
var speed = 6.0;  // Should be set to the same speed as your character
private var acc = 0.0; // Amount of movement on the camera. this starts at 0 and builds until it reaches speed.

function LateUpdate () // Had to be late update becuase it was studdering otherwise.
{
var myDist = 0; // This variable is a (changing) threshhold to determine whether or not our camera should be moving or not.
// when the camera is not moving (acc==0), it is asigned to maxDist.
// Once the camera starts moving, we set myDist to minDist. This means the camera needs to get even CLOSER to the player before it stops
// moving than it took to trigger it in the first place. this behavior does 2 things:
// When the camera is stopped, it lets the character walk around a bit before the cam starts to move.
// When the camera is moving, it prevents studdering as the character walks in-and-out of threshold.

if (acc == 0) // If the camera is not moving
{
myDist = maxDist; // Set our threshhold to Max
}
else // else the camera IS moving
{
myDist = minDist; // set threshold to min
}
transform.LookAt(target.transform);  // Aim at the character

var idist = Vector3.Distance(transform.position, target.transform.position); // Get distance between the cam and the player

if (idist > myDist) // If distance beyond our current threshold
{
acc = Mathf.Min(acc + 2, speed); // accelerate by a fixed amount (+2) until we're at max speed (speed)
}
else  // Else the distance is inside our threshold
{
acc = Mathf.Max(acc - 2, 0);  // deacclerate by (-2) until we're at stopped (0).
}

if (acc > 0) // If we're moving
{
    //transform.Translate(0, 0,  acc * Time.deltaTime); // Old way without collision;

//--VV-- This turns the local angle into a world coord that can be passed to the Move command
var iangle = transform.TransformDirection(Vector3(0,0, acc * Time.deltaTime));
//--AA--
var controller : CharacterController = GetComponent(CharacterController); // Get controller
var flags = controller.Move(iangle);// Move with collision but without gravity.
}
}

Creating elevators in Unity

Monday, November 17th, 2008

Unity’s Character Controller is an example of what makes Unity great. It’s a part of Unity’s engine that is specifically designed for creating moving characters in video games. It has built-in support handling slopes, walls, and steps, all rolled into a single component so that you don’t have to (re)write this for every game, like you would in most general purpose 3d engines. But all that built-in functionality comes at a price: if you want to make your character do something that the built-in stuff doesn’t support, then you gotta roll it yourself. Still, it’s better than having to roll everything yourself.

One of the limitations of the Character Controller is that it does not support elevators and moving platforms very well. The Character Controller will work fine with platforms when they’re not moving, but if they are moving, the character controller will not stick to them. This was the first real shortcoming I’ve run into while using Unity, so it was really my first hurdle I had to jump. After a bit of Googling around I confirmed that it indeed was a shortcoming of the Character Controller. Some suggested workarounds, but I couldn’t find a good solution with code snippets posted anywhere. In one of the posts I found about it, someone suggested building platforms with triggers above them that would parent the object to the elevator itself.

I liked the idea of using parenting, but I wanted a solution that was built into my character itself, and didn’t require triggers placed above surfaces on the elevators themselves. So I wrote one.

My Elevator Solution


Note: As I’m just getting started with Unity, I’m attaching code snippets here that should be complete, but I may have left out variable declarations. I stripped out all the unrelated code to make reading this example easier.

This solution is part of a script that you’d attach to your character GameObject. Your character should have a character controller attached to it. The code will parent itself to any object it touches that has the tag “elevator” on it. It will unattach itself when you move off of it. You don’t need to add a trigger to your elevator; you just need to make sure it has a non-trigger collider on it. I’ve used a bit of code taken from the Unity docs to check to see if the object is below the character, so that it won’t attach itself if it bumps into the sides of elevators and whatnot, but that shouldn’t be a big problem anyway if your character has gravity applied to it.

How to implement it
The heart of it is two functions: one is the OnControllerColliderHit function, and one that should be called immediately after every Move you make with your character.

Declare these at top of your script:
private var onelevator = 0; // this st used to keep track of whether or not you're parented to the elevator
private var touchingelevator = 0; // this is a flag used to determine whether or not you're actually touching the elevator (regardless of parented state).

Here’s the code to call whenever you move a character:

touchingelevator = 0; // Set this to zero before EVERY character controller Move() you make.
var charcon = GetComponent(CharacterController); // You don't need to do it this way; it's just the way I tend to do it
var flags = charcon.Move(moveDirection * Time.deltaTime); // Here's your character Move()
postMoveCheck();  // call this after EVERY Move() you make.

Here’s functions you need to include in the script:

function OnControllerColliderHit (hit : ControllerColliderHit)
{// This function gets called every time your character collides with an object, before Move() returns.
//We use this to check to see if we're touching the elevator.
var body : Rigidbody = hit.collider.attachedRigidbody;
  if (hit.moveDirection.y > -0.3)
return;
if (hit.gameObject.tag != "elevator")
return;

// At this point we know we're sitting on top of an elevator. :) 
      touchingelevator = 1;  // This is used to track whether or not we touched the elevator THIS UPDATE.
      // We do it this way because this function doesn't get called when we are not touching the elevator, and we need some
      // way to know that so that so we can unparent once we're not touching it anymore.

if (!onelevator)
{ // this moves the character into the elevator's hiearchy
// should only happen once, after collision.
transform.parent = hit.gameObject.transform;  // Make us a parent of the elevator. Nach.
        onelevator = 1;
}
}


function postMoveCheck ()
    {// This checks to see if we're on an elevator anymore, and if we aren't, we clear the parent.
    // Call this immediately after moving the character.
    if (!touchingelevator && onelevator)
    {
    transform.parent = null;
    onelevator = 0; // We're not on the elevator anymore! ;) 
    }
    }

Comments and suggestions are always welcome. Additionally, if you have a different solution to this problem, I’ve love to hear about it. Contact me or post in the comments below.

More Lightwave and Unity

Sunday, November 9th, 2008

Unity logo[see all of my Unity posts] I’m past the point of the Unity trial, and it was simple enough, powerful enough, and (most importantly) fun enough for me to buy it, with the goal of eventually making a commercial game with it. Although, for the time being, I’m just using it as hobby -- that is, using it for fun to learn it, as opposed to diving in and methodically developing a commercial game with it.

Importing models and animations from NewTek Lightwave 3D to Unity


I’ve been using LightWave 3D version 9.3.1 for all my modeling, and so far it’s been going fine -- with one little problem: the models import into Unity rotated so that the object is laying on it’s side. It’s odd because when the model is dragged into the scene, it appears right side up, but Unity sets the object’s rotation to 270,270,0 -- as if Unity knows the object is rotated incorrectly, and fixes it for me (I assume that in a normal situation, an object with rotation set to 0,0,0 should appear right-side-up in the world). It all seems like a minor detail until I went to use something like LookAt to point the object towards a destination, and the object returns to it’s un-normal sideways position.

The solution was to create an empty game object that the object is a child of, set the Parent to [0,0,0], and fix the child’s rotation [270,270,0]. It means all the scripts get attached to the parent GameObject instead of the model itself, and that meant I had to rewrite all the little scripts I had written to target the child object. No big deal at this point, but I’m glad I discovered this early. I hadn’t noticed it earlier since all my scripting tests were done with primitives.

I have yet to see if you can use this same trick for objects (trees) in the Terrain engine. I wonder how much processing overhead there is in adding a GameObject to an object, esp. if it’s going to be mass-populated via the Terrain engine. I hope to have some more to report on soon.