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;
}
}

Tuesday, February 12, 2013

Setting the default time zone for a whole application

Dates, yuck. Every time I get into dealing with a lot of dates from different datasources, it becomes a huge hairball.

Here's one trick that can make it a little easier. You can force the default timezone used for an app server to be different then the timezone of the server it's running on, so at least that will remain consistant if your various servers are not all set the same. And it's quite simple.

First, create a simple ServletContextListener like so...


Second, initiate the context listener from your web.xml file...


That's it, your app will now run with the default timezone you set no matter what server it's running on, or what the timezone is on that server.

Unfortunately the log files it produces will still be in the servers timezone, which is annoying. Anyone got an easy fix for that?

Monday, January 28, 2013

Opening a link in a modal or new window

For a web application I'm working on we have a need to open a page to show more details for a specific report.

Opening the page in a modal keeps the coherency of the application intact and provides a good look and feel, but there are times when users will want to open several of the modals at once and be able to reference them all without needing to close one before opening the next. Opening to a new window or tab makes more sense at these times.

To accomplish this I setup a standard HTML link so the users can right click and open the link in a new tab or window, but I also used JQuery to highjack the regular click action to open the link in a modal on the existing page.

Lets look at how to get this working:

1. Here's the full html for the demo page, you'll notice I'm using JQuery, and the JQuery UI package for the modal...

2. You will need the following css setup so the modal isn't visible right away, and to set the modal size. I also get rid of the ugly border around the iframe.

3. Here's the jquery magic to highjack the link and make it open in a modal for a standard left click. We'll attach this event handler to all links with the modal-link class, and the modal will open the url from the links href attribute.