Saturday, October 12, 2013

Building a sliding door in Unity, Part 1

I want to create a simple sliding door for a game. The door is 3D, but is designed for a 2.5D game, so it consists of a frame along the back edge, and a door panel which can move. The same principles would apply for a 3D game, but the model may need to be a bit different, having the frame on both sides rather then just the back side, and perhaps having a top. The code should be the same no matter what the door looks like though. Here’s what the finished product looks like with the door closed, and open.

Door1 Door2

I have many requirements for my door, some I knew before I started, others I discovered as I worked with my doors. I want several ways to open the doors: Simple single switches, multiple switches which all need to be activated, momentary on style switches which must be held and toggle switches which can be changed from off to on and back again, etc. But for now, lets keep it simple. One switch opens or closes the door and we will add the rest later. So here are the requirements for the first draft of the door.

  1. The door will switch states, open or closed, when a trigger is activated.
  2. Once a door is set to closed, the player should not be able to “slip through” before the door finishes the closing animation, this game’s about solving puzzles and I don’t want to worry about players being able to quickly jump through a door when they shouldn’t be able to.
  3. The animation needs to be able to change direction part way through if the trigger is activated again before the animation finishes.

 

First, lets create a new project and a simple scene. To make things easy we can just use two rectangles, one for the frame, and one for the door panel.  Nest these two under one game object called Door. NOTE: The panel should be located at local position 0.0.0 within the Door object, and oriented so that it slides along the z axis in the positive direction when opening. Once the panel and frame are situated within the Door object correctly, the entire Door object can be repositioned and rotated as needed to fit your game level.

Add a box collider to the top level Door object and make it the same size and placement as the Panel object. (Note: for some games you might want the collider on the panel itself, that will be discussed later.)

image

Later you can skin these however you like and change there shapes as needed. For now there’s not even any animation involved, we’ll just move the door panel in the script.

To start our door script off, we will define some basic variables that we can use to customize our door: The door panel object and how far it should move when it opens and closes.

var door:GameObject;
var openDistance = -3.0;

private
var openPosition:Vector3;
private
var closePosition:Vector3;


function Start () {
openPosition
= door.transform.localPosition + new Vector3(0.0, 0.0, openDistance);
closePosition
= door.transform.localPosition;
}

In the Start function, we’ll calculate the open position and the closed position based on the location and the openDistance variable for easy access later. This insures that no mater where the door panel is positioned within the object, the open and close positions will be discovered at run time and won’t need to be recalculated multiple times.


Next we will add the Open and Close functions and a few more global variables…


private var open = false;
private
var moving = 0;

function Open() {
if (open) return;
open
= true;
gameObject.GetComponent(BoxCollider).enabled
= false;
moving
= -1;
audio.Play();
}

function Close() {
if (!open) return;
open
= false;
gameObject.GetComponent(BoxCollider).enabled
= true;
moving
= 1;
audio.Play();
}

These are fairly simple functions at this point, the set the open variable, so we can easily tell the current state of the door, enable or disable the collider to let things through or block them, set the “moving” variable which will be used in the Update function to animate the door and play a sound. I’m using the same sound for opening and closing the door, but it’s easy enough to change the sound here based on which way the door’s going if you prefer.


If you don’t have any sounds handy at he moment, just comment out the audio.Play() lines.


Notice we will animate the door panel in the Update function, but the collider I just turn on and off. This was a preference for the game I am righting because it’s more of a puzzle game where I’m more concerned about the switches being right then if the player can find a way to time it right to get through before the animation finishes. First I put the collider on the door panel itself so it moved with the door panel, but I found it to hard to insure the player couldn’t slip through where I didn’t intend. For other games where you want the collider to stay with the door panel, just attach it to the door panel instead of the top level game object and leave it enabled all the time.


Let’s add some animation.

var rate = 0.2;

private
var newz = 0.0;

function Update () {
if (moving != 0) {
newz
= door.transform.localPosition.z - (rate * moving);
if (moving == 1 && newz < closePosition.z) {
newz
= closePosition.z;
moving
= 0;
}
else if (newz > openPosition.z) {
newz
= openPosition.z;
moving
= 0;
}
door.transform.localPosition.z
= newz;
}
}
We’ve added a new global rate for the speed at which the door will open, and newz which will track where the door will be after the animation step plays.


First, if we aren’t moving, just bale out and not bother checking anything else. This saves us some overhead when the doors aren’t moving (most of the time).


Next, we calculate the newz for each frame based on the current position, the rate and the direction (based on the moving variable).


The if statements insure we do not travel past the open or close position. When the open or close position would be passed, we set the position to the exact open or close position, and the moving back to 0.


This system also allows us to change the direction of travel of the door halfway through the move. As soon at the moving variable is changed, the door will start moving in that direction, from where ever it currently is until it reaches the stopping point.


OK, let’s try out our door! Since we don’t have any switches, players or even a floor at this point, lets just toss in another random game object (cube or empty, doesn’t matter) and crate a new test script…


var door:Door;

function Start () {
while (1) {
door.Open();
yield WaitForSeconds (
2);
door.Close();
yield WaitForSeconds (
2);
}
}

Add the test script to the new object you just created, and add the Door script we built above to the top level Door object. (Full text will also be shown below.) Wire up the Door variable in the test objects inspector with the Door game object and in the Door objects inspector, wire up the door panel object. If you have a sound for your door, add the component for that too, otherwise I hope you commented it out as noted above or you’ll get an error.


A few final reminders, position your camera so you can see your door and add a light to help it show up better.


Hit play and with any luck, your door should open and close every two seconds! You will need to tweak the openDistance and rate variables to match your door size.


In the next part we’ll work on adding some of the other features to the door like multiple switches and such.


And here’s the completed Door.js code…



#pragma strict
var door:GameObject;
var openDistance = -3.0;
var rate = 0.2;


private
var open = false;
private
var openPosition:Vector3;
private
var closePosition:Vector3;
private
var newz = 0.0;
private
var moving = 0;

function Start () {
openPosition
= door.transform.localPosition + new Vector3(0.0, 0.0, openDistance);
closePosition
= door.transform.localPosition;
}

function Open() {
if (open) return;
open
= true;
gameObject.GetComponent(BoxCollider).enabled
= false;
moving
= -1;
//audio.Play();
}

function Close() {
if (!open) return;
open
= false;
gameObject.GetComponent(BoxCollider).enabled
= true;
moving
= 1;
//audio.Play();
}

function Update () {
if (moving != 0) {
newz
= door.transform.localPosition.z - (rate * moving);
if (moving == 1 && newz < closePosition.z) {
newz
= closePosition.z;
moving
= 0;
}
else if (newz > openPosition.z) {
newz
= openPosition.z;
moving
= 0;
}
door.transform.localPosition.z
= newz;
}
}

3 comments:

  1. Its always good to learn tips like you share for blog posting. As I just started posting comments for blog and facing problem of lots of rejections. I will let you know if its work for me too. Visit Now

    ReplyDelete
  2. Very informative post! There is a lot of information here that can help any business get started with a successful social networking campaign. kit per cancello automatico

    ReplyDelete
  3. This is a really good read for me. Must agree that you are one of the coolest blogger I ever saw. Thanks for posting this useful information. This was just what I was on looking for. I'll come back to this blog for sure! Visit Website

    ReplyDelete