The Coin Flip

Ah the coin flip! Simple easy and fun. The mechanism is a platformer mainstay. You run over a spinning coin (it glitters, it calls you) it pops into the air, and it’s yours!

The Endless Elevator Coin Flip !

This is how we do it…

There is a Rigidbody and Collider on both the coin and the player character. You can clearly see the spinning frame of the coin collider in the .gif above.

The collider acts as a trigger which is being listened for by our script which executes the “pop”.

The coin has a spinning script (and also a magnetic feature as a bonus for later).

The player has a couple of behaviours that handles the trigger and action.

The coin scripts – note the use of the slider to get the spinning speed just right.

Here we have the coin’s Rigidbody and Collider settings:

The Rigidbody isKinematic and the Collider is a Trigger

This is the script we use for spinning:

public class spinCoin : MonoBehaviour {

    [Range(0.0F, 500.0F)]
    public float speed;
	
	// Update is called once per frame
	void Update () {
        transform.Rotate(Vector3.up * speed * Time.deltaTime);
    }
}

Simple and sweet.

This is the function that that handles the collision and the pop into the air! (It’s part of our character behaviours).

    void OnTriggerEnter(Collider otherObj)
    {
        if (otherObj.name == "Coin(Clone)")
        {
            coins++;
            var coin_txt = coins.ToString();
            coinsText.text = "Coins: " + coin_txt;
            Rigidbody riji = otherObj.GetComponentInParent<Rigidbody>();
            riji.useGravity = true;
            riji.isKinematic = false;
            riji.AddForce(Vector3.up * 40f,  ForceMode.Impulse);
            Destroy(otherObj.gameObject, 0.4f);
        }
     }

First of all we are incrementing our coin total variables and screen display.

The mesh for the coin is part of a child component so we need to call the Rigidbody attached to the parent object.

We set gravity to true so that it falls back down after adding the force and set isKinematic to false so we can use it’s mass to fall.

After a very short flight we destroy it (0.4 seconds).

As an added bonus here is the other coin behaviour for when a magnet power up is used in the game.

    private void OnTriggerEnter(Collider col)
    {
        colName = col.gameObject.name;
        float step = speed * Time.deltaTime; // calculate distance to move

        if (colName == "chr_spy2Paintedv2")
        {
            transform.position = Vector3.MoveTowards(transform.position, col.gameObject.transform.position, step);
        }
    }

This is when we make the collider really big on the coin. If the player gets in range of the collider then the coin moves like a magnet towards him. Which is kinda fun when there is lots of coins around and you feel like a millionaire just by standing around.

Endless Unity Camera Tricks

In our game currently under development called Endless Elevator I decided to add a new feature of more depth.  The game is 2.5D and mostly sits in a very shallow Z axis, a limited X axis, and an endless Y axis. As the name suggests your character is inside a never ending building trying to kill or avoid the enemies by escaping up elevators and escalators. The mock-up of a level below in the Scene view of Unity gives you the picture.  

Endless Elevator Opening Level Spawn

The player view is a much smaller slice of this level…about this much:

Roughly how much the camera views

I had the idea that I wanted to extend this playing field into a deeper third dimension where the character could walk down a hallway,  away from the camera, and seemingly deeper into the building.  Instead of the camera following the character down the hall on the Z axis (as it follows him on the X and Y) I wanted to pan around the edge of the building and take up the character again on the new parallel so that it looks like the camera is turning Ninety degrees and that we are looking at a new side of the building.  

Have a look at the .gif below and I’ll try and explain that better. The top half of the image is the Scene view and the bottom half is what the camera (and the player) sees in the game.  In the top half  Scene view you can see my character in green and highlighted by the handler arrows. He scoots around a bit and then disappears down the hall. When he gets to a set depth it triggers the camera to move in the game window below and you will see the levels reconfigure into a new building face (note the elevators and escalators and doors will be in different positions).

On the bottom half of the .gif that shows what the player sees it looks like once the character disappears down the hall the camera pans right, looks at the edge wall of the building as it goes around the corner in a Ninety degree turn, and then follows the character on the new level again.

(Watch the top half for a bit then the bottom half)

You can see in the Scene view we are not really moving anything with the building. It just recreates the levels. The camera is doing all the work.  It’s not perfect yet and without any background around the building to relate the movement to it’s a bit hard to tell if we are turning Ninety or One Hundred and Eighty degrees on that camera flip but it’s getting there.

It took a while to work out how to do this and I tried several different methods but this is the basic logic of the camera move script that is attached to the character.

  1. The movement of the camera is triggered when the character moves past a certain point on the Z axis.
  2. Stop the regular character and camera movement functions by disabling that scripted behaviour on each object.
  3. I set up an empty game object called the CameraLookAtPoint that hovers at the end of the building on the far Right of the X axis.
  4. Pan the camera Right toward the CameraLookAtPoint.
  5. When the camera gets to within a certain distance to the LookAtPoint it starts to rotate towards it.
  6. The camera moves around the LookAtPoint so that it faces that end wall of the building as it turns.
  7. At this point while the only thing the camera can see is that blank edge wall of the building I destroy the old level we just walked out of down the hall and create a new randomly generated level.
  8. This is the great illusion! The camera is then moved instantly to the far Left of the building (the opposite end) and it appears as if we have just turned a corner.
  9. Lastly the camera picks up the character again and we hand control back to the normal camera and character movement scripts.
This is where the CameraLookAtPoint sits that the camera rotates around  Ninety degrees as it gets to the edge of the building.

I’ll post the whole script below with comments but I’ll walk through the interesting bits here. 

To start off with I needed to grab references to several external elements like the camera, the level instantiating script, the character controller script, and the characters Rigidbody. (I needed the rb because when the levels were destroyed and recreated gravity would take hold between them and the character would fall into the endless abyss!)

I had to generate a series of “if” conditionals and Boolean flags to control the movements of the camera. This was surprisingly hard to get right. It’s often not intuitive when you are in the Update function what the looping iterations will do with your code but this allowed me to slow things down and get control back. 

The calls to the instantiateScene1 script were needed to copy variables used there in the main flow of the game to track what level we were on and how high up the building we had climbed. That way I could decouple that mechanism from this one and happily destroy levels and recreate them without interrupting the flow of the rest of the game.

 


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpinLevelDownHall : MonoBehaviour {
public GameObject cameraLookAtPoint;
public bool triggered;
public bool cleanAndRebuild;
public bool moveRightEnd;
public bool moveRightEndBack;
public bool moveLeftEnd;
public bool movebacktoCharacter;
public int levelHolder;
public float cleanUpHeightHolder;
public bool firstRunHolder;
public Vector3 target_camera_Position;
private GameObject the_camera;
private RigidbodyCharacter move_script;
private CameraFollow cameraFollow_script;
private InstantiateScene1 instantiate_script;
private Rigidbody rb;
// Use this for initialization
void Start () {
the_camera = GameObject.FindGameObjectWithTag("MainCamera");
move_script = GetComponent();
cameraFollow_script = the_camera.GetComponent();
instantiate_script = GetComponent();
rb = GetComponent();
}
// Update is called once per frame
void Update () {
if (transform.position.z > 15)
{
triggered = true;
moveRightEnd = true;
cleanAndRebuild = false;
if (triggered)
{
// Stop the character and the camera moving
move_script.enabled = false;
cameraFollow_script.enabled = false;
if (moveRightEnd)
{
// Set the target camera position on the x axis at the far right of the building
Vector3 target_camera_Position = new Vector3(78f, the_camera.transform.position.y, the_camera.transform.position.z);
// Set the camera look at point on the y axis (so it's on the same level as the player)
cameraLookAtPoint.transform.position = new Vector3(cameraLookAtPoint.transform.position.x, transform.position.y + 4f, cameraLookAtPoint.transform.position.z);
// start moving the camera
the_camera.transform.position = Vector3.MoveTowards(the_camera.transform.position, target_camera_Position, 50 * Time.deltaTime);
// When you get close to the end start swinging the camera around to look at the wall
if (the_camera.transform.position.x > cameraLookAtPoint.transform.position.x - 10)
{                        the_camera.transform.LookAt(cameraLookAtPoint.transform);
}
// When you get really close to the first position move the camera beyond the wall to the side
if (the_camera.transform.position.x > target_camera_Position.x - 0.5f)
{
target_camera_Position = new Vector3(78f, the_camera.transform.position.y, 4f);  // 4f is perfect when the camera is at -90 deg
moveRightEnd = false;
moveRightEndBack = true;
cleanAndRebuild = true;
if (moveRightEndBack)
{
the_camera.transform.position = Vector3.MoveTowards(the_camera.transform.position, target_camera_Position, 50 * Time.deltaTime);
// When you get really REALLY close to the second position move the camera to the negative X Axis side
if (the_camera.transform.position.z > target_camera_Position.z - 0.2f)
{
moveLeftEnd = true;
if (moveLeftEnd)
{
target_camera_Position = new Vector3(-78f, the_camera.transform.position.y, 4f);  // The other side
the_camera.transform.position = target_camera_Position;  // snap move the camera
the_camera.transform.LookAt(cameraLookAtPoint.transform);
moveRightEndBack = false;
moveLeftEnd = false;
movebacktoCharacter = true;
}
}
}
}
}
if (cleanAndRebuild)
{
// Call cleanup on everything
rb.useGravity = false;  // so the character doesn't fall through the floor
//cleanUp;
cleanUpHeightHolder = instantiate_script.floorCntr;
cleanUpHeightHolder = cleanUpHeightHolder * 8;  // so it cleans up all the floors
firstRunHolder = instantiate_script.firstRun;
instantiate_script.cleanUp(cleanUpHeightHolder, firstRunHolder);  // cleanup height is usually two levels below the character - we are raising it to two levels above him
//then make three levels
levelHolder = Mathf.RoundToInt(instantiate_script.player_level);
instantiate_script.makeLevel(levelHolder);
instantiate_script.makeLevel(levelHolder + 8);
instantiate_script.makeLevel(levelHolder + 16);
cleanAndRebuild = false;
}                
triggered = false;  // makes it only run once
}
}
if (movebacktoCharacter)
{
// new position is back to the character
// Start breaking and making your new levels in here
// first move the character
transform.position = new Vector3(transform.position.x, transform.position.y, -4);
target_camera_Position = new Vector3(transform.position.x, transform.position.y + 4.9f, -20.51f);  // the starting camera position
the_camera.transform.position = Vector3.MoveTowards(the_camera.transform.position, target_camera_Position, 50 * Time.deltaTime);
the_camera.transform.LookAt(transform);
// Once again when you get close to your original camera position disable and enable normal camera tracking again
if (the_camera.transform.position.x > transform.position.x - 0.02f)
{
movebacktoCharacter = false;
rb.useGravity = true;
move_script.enabled = true;
cameraFollow_script.enabled = true;
}
}
}
}

It’s not the prettiest code, and I admit to hacking my way through it, but it works.  Maybe you have a better method for doing something similar – if you do please feel free to add a comment – I’d like to hear it.

Zulu Out.

Making a Custom Navigation Mesh for AI

Hi Xander here…

This week I decided to totally redo the way I have been handling character movement.

I used to have a free ranging character controller that basically moved in the direction your joystick wanted. I never really had that as my vision for this game as I wanted a more 2.5D feel to the game and a limited number of places you could move to. I got to this point because I was working on the enemy AI scripts using a custom mesh to navigate around.

The game level (or rather endless pattern of a level) is 16 Units wide and two deep.  In this case a Unit is a building component like a piece of floor or doorway or elevator shaft etc.  All these components are 8 x 8 blocks in Unity scaling. This is an example of an elevator shaft:

When you build them all together it looks something like this (that’s a very basic mock up below):

So you got a forward position where you can go up the stairs on the right and a middle position where you can go up the stairs on the left (see they are set back a bit) and a very back position which is through a doorway. So basically there are three parallel positions along the X axis.

What I wanted to do was to create a “patrol point” on every floor space within that grid of a floorplan and also create a patrol point if there is a door that is open.

On the floor positioned at the top or bottom of a stair you do not have a patrol point so there is never anyone at the top or bottom of a stair to block you going up or to knock you back down.

This all gets created at instantiate time and every level is random so I cannot use any of the mesh or nav components that Unity provides.

So all my patrol points get made into lists when the floor is instantiated and added to an array of levels.

When an enemy AI agent starts off they read in all the available patrol point nodes on the floor and work out the available nodes around it to move to.

So the agent knows about the nodes around it in a four square plus it’s own central location. 

As the game mostly scrolls along the left – right axis during game play the nodes are weighted so that travel along the X axis is more likely than the Z. 

At the end of the frame after moving (in late update) the nodes list is refreshed if a new node has been reached.

How does an agent find all the nodes around it?  Using a Raycast is too expensive.  So on each move the agent parses the list of nodes and works out the closest in each direction.

Basically for each node in the list get the x and z and subtract it from your own then put that value in a temporary location – every node gets tested the same way and if the return value is less than the temporary value then you got your closest node in that direction. You would need to do this four times (left, right, forward and back) and handle the null values when there is no space to move next to you. 

At an agent update interval when a new node is reached we first check all the nodes in the list and make a new list of nodes on the floor and our closest points. This gets added to the basic agent control behaviour of “looking around” where the AI stays in one spot and looks left and right in a rotation.  In all cases if they are looking left and the character is right then they cannot pursue him.  If he fires then they will turn.  All of these behaviours are then blended by weight.

I’m not sure if I will continue with this method for the character controller but it’s pretty good for the enemy AI scripts.

Getting a Foot in the Door of Game Design

First of all – sorry about the misleading title – this post is about getting the doors working in the Endless Elevator game that we are currently developing. I thought it was a good pun and that as this post is all about our development process that it wasn’t too bad. The only career advice I got is just to start making games…any games.

You’d think making a door that opens and closes would be a pretty simple thing to do but surprisingly it wasn’t.

I designed and built some 3D models of building components in MagicaVoxel and exported them as .obj files. I use MagicaVoxel because it’s free and really quick and simple to use. When I model components I can be sure that they are all exactly the same scale and that all the different bits (no matter when I made them) are going to fit together without any hassle. But the models are not optimised for game engines as they have a reasonably high poly count due to the modelling process within the program. Most of the models come out with heaps of unnecessary triangles that need to be managed first. In most cases I will import the object into Blender and use the “decimate” modifier on the planes (with an angle of about 20 if you are interested). In the case of the door object it was pretty simple, it is just a door after all, and I didn’t need to optimise it.

Here is what the door looks like in MagicaVoxel:

Notice that the door object is sitting just forward of the enclosing frame and that when exporting the object the center is at the middle bottom plane of that frame. The door is off center because it’s modelled to fit exactly in a doorway that sits exactly in that position within the frame. This saves me a huge amount of time lining everything up when it gets to Unity as the position of the door (or any other object for that matter) is already in the right spot. The problem is that the point of origin for the door is in the wrong spot. It exports a few units behind the door and on the floor! This becomes important when you try and rotate that object (like you would when opening and closing a door) and the pivot point is not where the hinges should be.

To fix this I had to import the .obj file into Blender and reposition the point of origin for the model.

This is what it looks like in Blender when I did this:

To do it I selected the edge that I wanted the door to pivot on when opened.

Then in Edit Mode:
Mesh -> Snap -> Curser to Selected

In Object Mode:
Object -> Transform -> Origin to 3D Curser

So that puts the curser onto the correct position in the middle of that edge where the hinges would be and resets the point of origin (which is where it will pivot when rotated in Unity) to the right spot.

Once we were all imported into Unity and looking good I set up a prefab for the “Doorway” object with the door as a child object. The doorway object has a bunch of colliders to stop the player walking through the walls and a big sphere collider where the door would be to trigger the open / close function when the player walks into it.

This is what the doorway looks like in Unity:

Next I scripted up a few options for opening the door. I’ll post the script at the end of this article but basically there were three options of opening the door that I wanted to test. (Actually I tried it about six ways but whittled it down to the most simple methods – and just as an aside there is an actual “hinge” object type in Unity if you ever need it).

This is how the script looks in the editor:

Notice the slider at the bottom to control how far I want the door to open. It’s really handy to have this when playing in the editor and getting your settings right. If you want to know more about using it see this post

The three tick boxes are for testing the three different ways of opening the door.

Snappy was a quick simple transform of the position from open to closed with no “in betweening”. It looks a bit unnatural as the door magically goes from closed to open but it’s not too bad and most people are pretty used to similar behaviour in video games.

The active line in the code is:
the_door.transform.Rotate(-Vector3.up * 90 * Time.deltaTime);

The next method works more like a door should. In that it swings open and closed the whole way in a steady fashion. But the big problem with this method was that it was OK when the character was going into the doorway and with the swing of the door but when it was time to come back out of the doorway the door was in the way. There was not enough room to trigger the door opening from the other side without being hit by the door as it opened. Plus if the player enters the collider from the wrong trajectory the character gets pushed through a wall by the swinging door which is sub-optimal. I called this method InTheWay!

The active line here is:
the_door.transform.rotation = Quaternion.Euler(targetPositionOpen);

In an effort to combat this I chose to do a hybrid method that opened the door to a point that wouldn’t hit the player and then do the magic transform to get all the way open. I call this one aBitBoth. It looks a little weird too. Like there is an angry fairy pulling the door closed with a snap after the character enters.

Here are all three to compare.

Snappy

In The Way

A Bit of Both

I’m not too sure which one I’m going to use at this stage as the Snappy method works best for now but I like the In The Way method better. I looks more normal and I like that you have to wait just a few milliseconds for the door to swing (adds tension when you are in a hurry to escape a bullet in the back). I could do something like halt the player movement from the rear of the door when it triggers to open from the closed side or maybe play around with the radius of the sphere. Neither solutions seem like great ideas to me right now but something like that will need to be done if I’m going to use that method. Maybe I could just have the door swing both ways and open to the outside when he is behind it but that’s probably a bit weird for a hotel door.

Here is that script that I was testing with:

using UnityEngine;
public class OpenDoor : MonoBehaviour {
public bool openMe;
public GameObject the_door;
public bool snappy;
public bool inTheWay;
public bool aBitBoth;
public Vector3 targetPositionOpen;
public Vector3 targetPositionClosed;
[Range(0F, 180F)]
public float turningOpen;
void Start ()
{
targetPositionClosed = new Vector3(0f, 180f, 0f);
targetPositionOpen = new Vector3(0f, turningOpen, 0f);
}
void Update()
{
if (openMe)
{
OpenMe();
}
else
{
CloseMe();
}
}
private void OpenMe()
{
if (inTheWay)
{
if (the_door.transform.rotation.eulerAngles.y > turningOpen)
{
the_door.transform.Rotate(-Vector3.up * 90 * Time.deltaTime);
}
}
if (snappy)
{
the_door.transform.rotation = Quaternion.Euler(targetPositionOpen);
}
if (aBitBoth)
{
if (the_door.transform.rotation.eulerAngles.y > turningOpen)  // 144f
{
the_door.transform.Rotate(-Vector3.up * 90 * Time.deltaTime);
}
else
{
the_door.transform.rotation = Quaternion.Euler(targetPositionOpen);
}
}
}
private void CloseMe()
{
if (inTheWay)
{
if (the_door.transform.rotation.eulerAngles.y <= 180)
{
the_door.transform.Rotate(Vector3.up * 90 * Time.deltaTime);
}
}
if (snappy)
{
the_door.transform.rotation = Quaternion.Euler(targetPositionClosed);
}
if (aBitBoth)
{
if (the_door.transform.rotation.eulerAngles.y <= turningOpen)  // 144f
{
the_door.transform.Rotate(Vector3.up * 90 * Time.deltaTime);
}
else
{
the_door.transform.rotation = Quaternion.Euler(targetPositionClosed);
}
}
}
void OnTriggerEnter(Collider col)
{
string colName = col.gameObject.name;
Debug.Log("Triggered OpenDoor!!! : " + colName);
if (col.gameObject.name == "chr_spy2Paintedv2" || col.gameObject.name == "BadSpy_Package(Clone)") 
{
openMe = true;
}
}
void OnTriggerExit(Collider col)
{
if (col.gameObject.name == "chr_spy2Paintedv2" || col.gameObject.name == "BadSpy_Package(Clone)") 
{
openMe = false;
}
}
}