Last weekend I experimented with various aspects of Unity 3D to get a feel for it and to see if I would like to learn more. I was convinced after less than an hour, so now I want to learn more. First, I’ll try to get an intuitive understanding of the basics.

The Tornado Twins have an excellent series of YouTube videos where they build a simple game and explain basic concepts like movement, having turrets shoot bullets at you and more.

I watched the first 11 episodes yesterday evening and decided to see if I could build something similar with what I remember and by using the documentation.

Movement was the first thing I stumbled over. The concepts are clear to me, but the details of how to do it remain blurry. In the documentation, I found the example for CharacterController.Move().

At first the example looked overly complex to me, so I started taking out most of the code until the Update() function only contained three lines. And my sphere happily moved through the area. I was not happy with using the arrow keys to strafe to the sides, so I adjusted it to use up/down to move forward/backward and left/right to turn to the sides. I now had four lines.

While running/rolling/floating/whatever around with my sphere, I found myself tapping the space bar every couple of seconds because I wanted to jump, but the sphere stayed on the ground. It was time to add jumping back to the script.

So I started adding bits of code back in and before long I had most of the example code back, but jumping did not work yet. Fiddling around with the gravity manipulation eventually made me realize I needed to divide gravity by Time.deltaTime to reduce the per-frame effect of gravity instead of applying it about 200 times stronger than intended.

After this, my script was working and the sphere happily races around in its playground. However, it seemed useful to dive back into the code and extensively comment it for posting on our blog, to help me understand how it works. They say that the best way to learn… is to teach.

Enjoy the source code!

// This script moves and rotates the character controller using the arrow keys.
// Space can be used to jump.

// Variables available from the editor to tweak how fast the character moves
public var speed : float = 6.0;
public var gravity : float = 20.0;
public var jumpSpeed : float = 8.0;
public var rotationSpeed : float = 1.0;

// Movement vector. It's stored outside of the Update() function to
// remember the vector while jumping and increase the pull of gravity
// on the character over time.
private var moveDirection : Vector3 = Vector3.zero;

// The character controller that we're manipulating.
private var controller : CharacterController;

function Awake() {
  // Since we're not going to swap the CharacterController on a character,
  // we'll look it up once and store it in a variable.
  // In theory this should save a few nanoseconds every frame.
  controller = GetComponent(CharacterController);
}

// Update gets called every frame, so remember to multiply all numerical
// effects with Time.deltaTime to make them framerate-independent.
function Update() {

  // Rotate based on the left/right arrow. Since we spin around the Y-axis,
  // we only need to use the up direction of the Vector3.
  transform.Rotate(Vector3.up * Input.GetAxis("Horizontal") * rotationSpeed);

  // When on the ground, move. Otherwise, don't re-create a new moveDirection,
  // but let gravity do its thing on the old one to drag us back down.
  if(controller.isGrounded) {

    // First create a local space vector to describe our intended movement
    // and its speed.
    var localDirection = Vector3.forward * Input.GetAxis("Vertical") * speed;

    // Take the local movement and transform it to world space, this is because
    // CharacterControler.Move wants a movement in world space.
    moveDirection = transform.TransformDirection(localDirection);

    // Force our upward momentup to jumpSpeed when we press space.
    if(Input.GetButton("Jump")) {
      moveDirection.y = jumpSpeed;
    }
  }

  // Apply gravity to the current moveDirection object, no matter if we decide
  // to move or not. Gravity is modified by Time.deltaTime to smooth it out over
  // time. Note that it's outside the if block and thus also triggers in the
  // air.
  // This is why jumps are so smooth: we have an upward momentum from the
  // jump and now we are reducing the positive momentup each frame until it's
  // zero (at the top of the jump) and then each frame the character is
  // pulled down faster because the momentum becomes more negative.
  // When we hit the ground, the block above kicks in to reset momentup to zero.
  moveDirection.y -= gravity * Time.deltaTime;

  // Actually perform the move. Note that this is modified by Time.deltaTime to
  // make it framerate-independent.
  controller.Move(moveDirection * Time.deltaTime);
}

// Ensure we have a CharacterController component included in our character.
@script RequireComponent(CharacterController)


Screenshot of demo 6
Demo 6