Endless Elevator – Show Reel

DevLog March 2019: This month we did a cheesy show reel of the latest changes in development for Endless Elevator.

This is a very early “In Editor” development sketch up of some of the features currently being included… Enjoy the show.

The game will be available on the mobile app stores in 2019.
For more information subscribe to our Dev Blog at: http://www.zuluonezero.net/zuluonezero-devblog/

Violation of Usage of Android Advertising ID policy and section 4.8 of the Developer Distribution Agreement

This week we got an email from Google about our demo game Adventure Text that we had released to the public ten or so days before on the Google Play Store.

The game was removed from the Play Store with this email being the only notification after the fact. The public couldn’t see it, couldn’t search for it, and any live links from promotional material wouldn’t connect through to it. If this was a commercial product or even one that I had a vested interest in it would have been a small disaster. Those first few days of a game release are critical for generating user interest.

The reason that the app was removed was because we had included the Unity plugins for Analytics and Advertising in the packages that it was compiled with. Our app is a demo of a Text Adventure framework we built as a side project and we did not use either of these packages. They were included in our base build of the project by default even though we had the Enable Analytics switch turned off when we created the project.

One or both of these packages was capturing either user data or Android Advertiser ID’s in the background. Because we were not actively using the packages and did not capture any user data as part of our application we were kind of stumped. We did a bit of Google Foo and found that a common source was the Unity packages for Analytics and Monetization. Here they are in the project folder (below) – was kind of obvious once you look but since we weren’t using them in any of our code I figured they would not be included in the final package (just like unused assets). Wrong!

Google Play requires developers to provide a valid Privacy Policy when the app requests or handles sensitive user or device information. If they identify that your app collects and transmits the User Data or the Android Advertising Identifier then you must provide a valid Privacy Policy in both the designated field in the Play Console, and from within the app.

You can read more about the Google Play policies for Privacy in the Developer Policy Center (see Advertising and User Data Privacy). There is also more information available in the Google Play Developer Distribution Agreement.

In our case it wasn’t a big thing. We had just released a demo game for fun not for profit. But if you were going for a monetized app (and hey why not) you do need to use a valid Privacy Policy both somewhere available on the internet and also from within the application.

This was a game made in a week. There was no way we were adding another button for a privacy policy we didn’t really need. We took the offending packages out of the project in Unity (Window -> Package Manager) by using that little “Remove” button there in the top right hand corner:

We rebuilt the game and uploaded another release to the Developer Console and in a few hours the app was again available on the Google Play Store. Phew. Cool. Problem solved.

But what if we really wanted these packages? What would we have to do? Well first we needed a Privacy Policy. There were some pro-forma one’s available on the interwebs that might have been appropriate. I found several forum threads where other people had had this problem and used this site to generate one: here

I cannot endorse or even suggest that it’s a good idea to grab something off the internet that is a legally binding policy so please do your research on this subject before you leap into it.

Once you get your policy in place it needs to go somewhere on your web site so that it’s publicly available.

It also needs to be available in your Google Play Store Listing. If we wanted to use one it would go down the bottom in the Google Play Developer Console:

Finally you would need a mechanism in your game or app that allows users to easily find and see your Privacy Policy from within your game.

People’s privacy and the data you collect from your applications and games is an important topic. Both ethically and practically.

It’s kind of a dummy mistake to get caught out by this one. Don’t let it be you! Be aware that the default project settings in Unity (at least in the 2018 build) include packages that collect user data and that if you include these packages you need to have a Privacy Policy.

Maybe even if you didn’t include these packages it would be a good idea to have one anyway. It’s totally ok for app developers to gather information that’s going to enable them to make a better product and I think most game developers include either analytics and advertising in their games. It’s unfortunate that Unity includes these packages in the default project build without really making the end user aware of it but if you look it’s plain to see. Also if you are informed and have read all the Google Play Store policies that are there to protect everyone then it shouldn’t be too hard to put it all together.

Zulu out.

Demo Text Adventure Game Released on Google Play

Hi Harmony here….

We released the demo game for our Text Adventure framework on the Google Play Store this week.

https://play.google.com/store/apps/details?id=com.ZuluOneZero.AdventureText


The Code for the Project is available on GITHUB:

https://github.com/zuluonezero/AdventureText

Quick Start Instructions for building a 2D Unity project are available on the README.

More detailed information is available in the ZuluOneZero – DevBlog.

See our posts about the development of this game and more information on how to configure the project: 

I hope you enjoy playing with it.

If you have any questions or need support you can email us at zuluonezero.z10@gmail.com.

Harmony out…

Text Adventure Design and Programming

Hi Xander here….

This post is about the design and build of our Text Adventure game over the course of a week. This was a “proof of concept” project to make a framework for a text based game that could be re-used and expanded on for future use. It’s not a polished game but an exercise in execution – and a jolly fun one at that.

The main features of the game are:

A simple 2D UI with Directional buttons an Action/Attack button, and a “Show Map” button. A HUD bar at the top with Player HP (hit points) the outcome of combat interaction (Player vs Monster) and during combat the Monster HP.

A large text area for describing your location and the environment around you. This area also shows the random actions taken by both Players and Monsters during combat.

A map that is gradually exposed as you explore the game.

This is the starting layout (digging the “panelled wood” feel here):

In the Scene the main Game Objects are:

The Canvas for the UI.

An InputControl object to handle what happens when a button is pressed and provide the appropriate response.

A FightClub object to handle combat events.

Another Map Canvas to show the map and hold all the map components.

And what I call the WayPoints which are the different locations and events that can happen during the game. Each one gets a new object and in this sample game there are Thirty Four of these nodes. These objects hold all the information about a particular location including which WayPoint is next in any give direction and what Monster if any appears here.

This is what it looks like in the explorer:

I’ll go through the main scripts one by one.

I will also put all the scripts in our GIT Repository (link below).

WayPoint Script

This is a custom class that mostly just holds data about a given location and only if required Generates a Monster. See the example below (which shows a section of the full details):

The first boolean tick box Way Point Visited let’s the InputControl script know if an area has been explored before. This allows alternate short descriptions of the location (which makes navigating known areas faster) and is used by the mapping component to tell if an area of the map can be revealed or not.

The Name, UID and Description fields are not actually used but I added them in for future use. Since the Object name includes a UID and is a descriptive name (Eg. 06_StatueFight) I didn’t really need them for coding.

The Write_rate and Text_size are passed to the Main Printing System (also attached the InputControl object) so that there is some control over the speed and size of the text that prints on the screen like a typewriter for managing the pace of the game. Faster and larger for combat and exciting events and slower for more descriptive passages.

The Text Sections are self explanatory. When a Player enters a location the first time or a repeated time this is the text that is printed.

WayPoint _forward, _backwards, _left, and _right point to the name of the next WayPoint object in that direction. If it’s NULL then there is nothing in that direction.

The rest of the script is for defining if a Monster spawns in the given location, it’s attributes, and the text options passed to the FightClub combat module (each Monster has different attack and defence descriptions to make it more variable and exiting).

The WayPoint script has one Function called GenerateMonster() which when called by the InputControl system does just that with the info in the WayPoint class for the monster we defined above).

Input Control Script

The Input Control Script handles input from the four directional buttons (Forwards/Backwards/Left/Right or North/South/East/West), the Action/Attack button and the Map button. The Map button shows the map canvas and the progress the player has made within the dungeon. The Action/Attack button is only used once the Combat feature is triggered. The main body of the script is for handling navigating around with the directional buttons and triggering special events attached to some locations.

The Current Waypoint variable is the most important piece of the whole game system. It holds all the data for the current location such as the available text to print and if there is a Monster to fight or other special action.

The next public variables hold references to all the components we will need in the Input Control Script: The Combat System (FightClub), the UI buttons, and the Map Canvas.

We also have two bools. One to let us know if the game has just started so we can show the appropriate text and one to let us know if there is a Monster present (so we can initiate the Combat System).

The Generate Map function in this script does a find on all the WayPoint objects in the game and collects their names. Each map piece is tagged with it’s related WayPoint name and if the “visited” bool has been check it displays that part of the map.

Print Main Text

This is the printing workhorse of the game. It takes the input from the WayPoint (and a text size and speed of typing) and prints it out on the main screen like a typewriter.

It handles all the printing to the other smaller text components as well like the Title, HP and Combat stats.

This script is made into a Singleton so it’s easy to call from all the other scripts that have printing needs.

Fight Club

The Fight Club script is the combat system. Very simply the Player and any Monster gets a maximum and minimum limit to their random integer attack. The player gets an optional “Weapon bonus” if he/she picks up a magical sword during the course of their adventures which adds the random int. Whoever has the higher number wins. The value of their integer is also the amount of damage done. Damage is done against another integer (Hit Points or HP) which if it reaches 0 indicates that the combatant has died (usually horribly).

This is the public behaviour of the script:

The Monster’s HP and Min/Max Damage is recorded in the WayPoint for that location and passed from the MonsterStuff class that is attached to the Monster.

Monster Stuff

The Monster Stuff script is attached to a Monster prefab and is instantiated when a monster spawns. This is the behavior:

As you can see it’s the same sort of stuff that the Player has in the Fight Club script (HP, Max and Min Damage) but there are also arrays of text fields. The Monster prefab gets passed all this data from the WayPoint script on instantiation so they are really just placeholders waiting for data.

The Dead bool is pretty self explanatory – you couldn’t get more boolean!

The Monster Stuff class presents four functions:

MonsterAttack(), MonsterTakesDamage(), MonsterAttackText(), and MonsterDefenseText(). The attack and damage functions return integers for calling from the FightClub and the text functions return the appropriate text to the FightClub. Each Monster has its own special attacks and defences so the text generated for the fights is unique. The attacks and defences are arrays so you can expand the available text options at will thus making the game more interesting and variable.

Other Miscellany

That is basically all the scripting that there is to the game! It’s a Text Adventure so most of the work goes into the writing and the story that you build into the WayPoints. It’s pretty light and flexible and able to be extended a number of ways.

There is also script to open the map and close it and to reset the game back to it’s starting position but I won’t talk about them here. They are pretty generic.

GIT Hub

All of the code is available publicly on our GIT Repository: https://github.com/zuluonezero/AdventureText

Xander out.

A Week of Text Adventures

Hi Harmony here….

Last week we spent completely side tracked from our usual projects and decided to create a Text Adventure game in a week. It was a great idea and everyone feels like they have had a holiday from the daily grind. Just going with a new idea and playing it out in a limited time frame was awesome fun. We did this sort of thing when we were just starting out with the Five Games in Ten Weeks challenge and it really freshens up the creative juices.

The initial idea was to do a text adventure like I used to play when I was a kid on my Trash-80. Zulu loved those games in the days before computer graphics were interesting. So we gave it a limited scope of just using text to tell a story in the manner of a dungeon crawler that incorporated some kind of hand drawn map just like we used to do when playing D&D.

Gene did up a super simple UI and Trixie and Xander worked on getting the programming of the text and mapping underway. The incremental mapping proved to be the biggest challenge and we had to try three different ways of working it till we got it right. Xander will post the code and methodology used in a later entry.

This is how the UI ended up (Kudos to all of you who spotted the callout to “Impossible Mission” another childhood favourite):

In keeping with the theme and the timeframe we used a really simple text call to print the story. This is the opening scene:

Then we worked on the exciting monster battles (all in text):

Finally we implemented the mapping system which tracks your progress through the tomb!

So as you can see it’s pretty simple but really fun to do. I guess this is one of the real strengths of using Unity is that you can quickly mock up a working game in a short amount of time. We will post it into the Play Store as a Beta later this week so you can go for an adventure yourself.

If you are interested in this sort of game I found the following groups a great place to start:

8-Bit Text Adventures
TEXT ADVENTURES & INTERACTIVE FICTION // PLAY / DESIGN / DEVELOPMENT
r/textadventures

Harmony out.

Text Adventure

I started the New Year feeling a little dispirited and un-enthused. It takes Sooooo long to finish a game so that when your attention is flagging it just makes it seem worse.

To remedy that I woke up one morning with a new idea for a game and feeling spirited again I decided it would be really quick and easy to make it. So that’s what I did. In a couple of sessions I had worked up an interface and a print method for a text based adventure game.

I used to love these as a kid on my Trash-80. They were the bridge between the role playing genre and computer games.

So this is the start of it. It’s really simple. Laid out in nice “wood feel” with simple movement buttons to navigate, a “show map” button, and an Action/Attack button to fight off those all important monsters and to fiddle widgets with.

First Draft

Honestly I just wanted to have fun and do something quick and easy. I’ll release it for my own pleasure when it’s done. There is still a bit to go like getting the map working so that it displays where you’ve been and getting combat simulation going – but easy-pease little steps. I don’t want to spend more than a week doing it.

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

Oversharing About Overriding

The idea of a class and how objects, scripts or behaviours are implemented in Unity is pretty straightforward. Each object or type is a class and each script is a sub class of the MonoBehaviour class. So we are using classes all the time. Even if a script isn’t a behaviour, like a controller script or similar, the common pattern in a Unity project is to make an empty object and attach the script to it.

Classes are useful for separating code that is re-usable or for segregating data structures. Good Class usage is supposed to make your code easier to read if you have a sensible naming convention and self explanatory methods. I don’t know if it really does help personally I find it hard to keep flipping between scripts and classes in different locations when writing and debugging. Sometimes I find it easier to have all behaviours in one big class and split up code into functions where possible.

I’ve published two games and written many more but I have never used a class that wasn’t derived from standard MonoBehavoir. But lately I’ve changed things up a bit. I started working with the concept of Overriding. I’m not sure if it’s any easier or better at this stage but I’ll describe the general concept and you can try it for yourself (of course you’ve probably been doing this for years!).

But first let’s go back to Classes.

Classes

A useful metaphor for a class that’s been used many times is the Vehicle. The basic behaviour for a vehicle is that it moves. Be it a bike, a car, a motorcycle, a truck, or a tank they can all be categorised as a type of “Vehicle”. Our basic variables for all Vehicles could be maxSpeed, acceleration, and direction. Our base class could also have a function called Move().

If we want to make a new Vehicle we can go:
Vehicle someVehicle = new Vehicle(); // and it get’s all those parameters (maxSpeed, acceleration, direction) and can access the Move() function.

If we want to make a constructor to handle those default parameters on instantiation we can do something like this:

class Vehicle {
float maxSpeed, acceleration;
Vector3 direction;
string makeNmodel;  // Added this one to so we could have a name in there
//The constructor
Vehicle(float _maxSpeed, float _acceleration, Vector2 _direction, string _makeNmodel)	
{
maxSpeed = _maxSpeed; 
acceleration = _acceleration; 
direction = _direction; 
makeNmodel = _makeNmodel;       
}
// Basic move function
public virtual void Move()   // is this the fun bit?
{	        
transform.Translate(direction * acceleration * Time.deltaTime);
}
} 

Using the constructor I can make a new sportsCar instance of a Vehicle object and initialize the base values immediately:
Vehicle sportsCar = new Vehicle(210, 21, Vector2.right, “MazdaRX7”);

Then if I upgrade my RX7 with a new fuel injector I can access those values like this:
sportsCar.MaxSpeed = 240;

And of course I can drive it round now too:
SportsCar.Move();

Now I can keep going building automobiles with different variables that represent their base characteristics – fill a whole garage if I want.
But what if I made an asumption about my Vehicle() class that they all had wheels and I wanted to add a plane?
I could write the plane a new class to handle flying or I could keep the sanctity of the Vehicle concept intact and just override the Move() function to include a bit of up and down action.

Overriding

What we can do is override the Move() function in the Vehicle class with an Airplane Move() function.
What we are doing here is also called up Upcasting. We are effectively overriding a Parent method in a Child Class.
The Vehicle class and the new Airplane class form part of an Inheritance Hierarchy.

Vehicle -> Airplane (Parent -> Child)

This is how to do it.
To make a Parent method able to be overridden use the ‘virtual’ keyword on it’s method and the ‘override’ keyword in the child method.

The parent Vehicle class has:
public virtual void Move()
// The direction variable is a Vector2 as it moves around on a flat surface using wheels

The child Airplane class has:
public override void Move()
// The direction variable is a Vector3 which adds the up and down movement using wings

For example the Airplane Class

using UnityEngine;
using System.Collections;
public class Airplane : Vehicle  // the Airplane class inherits from the Vehicle class
{
public Airplane ()
{
Vector3 flightDirection;
}
//This method overrides the virtual method in the parent
public override void Move()
{
transform.Translate(flightDirection * acceleration * Time.deltaTime);   
}
}

You can also add the Parent method into the new Class by using the base keyword but unfortunately this is where my example above falls down. You would never need to keep the 2D movement and add the 3D movement on a vehicle. Unless maybe you were giving the RX7 a jetpack and wings – it could be that kind of game.

If you have to do this the syntax is to declare the function in the new Child class using “base” to indicate that the parent(base) method is being used:
base.Move();

I’ve put links at the bottom of the post for more info here if you want it.

But why use a custom class and the override feature? One good reason is when you don’t need the stuff that you inherit from MonoBehaviour.
You could define your own class when you don’t need functions like Update(), Awake() and similar things like FixedUpdate(), OnCollision(), or UI behaviours.

You could define classes when they are not game objects like in-game information, a score multiplier, number of bullets left, etc. or you could use them when you have a common data structure that is shared by several objects like in our Vehicle example but you certainly don’t have to. One of my favourite things about old programming and scripting philosophy is the concept ‘that there is more than one way of doing it’. You don’t have to write perfect code that follows a pattern or schema. It just has to work (mostly) and be understandable (at least to you) next time you have to look at it.

https://unity3d.com/learn/tutorials/topics/scripting/overriding
https://answers.unity.com/questions/15448/when-do-we-need-to-use-class.html
https://unity3d.com/learn/tutorials/topics/scripting/classes
https://www.studica.com/blog/unity-tutorial-classes
https://stackoverflow.com/questions/36539708/c-sharp-in-unity-3d-2d-am-i-required-to-use-classes-for-every-script
https://www.reddit.com/r/Unity3D/comments/32c9nj/how_to_create_custom_classesobjects_in_unity/