The problem
While Unity is great to produce a level; it can be problematic to use a full level when your user can re-enter an already visited level especially if you need to remember some or all of the modifications he may have done.

In J.U.M.P, levels are setup such that you can re enter an already visited level, either from just getting back or by entering the level from elsewhere

As you re enter a level, lot of stuff has to happen
  • Did you pickup some coins/time in your previous visit ? It is better if they can be automatically removed
  • Did you pickup a key/open a grid ? The key should not be there anymore and the door should be already open
  • Did you already see the introduction cut scene of that level ? No need to see it again
  • Based on your gameplay, a lot more need to not be done the second time you visit a level

Designing the solution
So what Unity can do for us about those needs ?
A solution would to use a manager of some sort that will be persistent accross the level thanks to DontDestroyOnLoad(). This manager will hold some ID of the gameObject that we want to remove on next visit.
The manager will have a RemoveID and a isRemovedID function that will be the only API needed for us to work.
For every gameObject you need to track, we will check in the Start method if it was already removed in a previous run, and remove it using Destroy. When we need to remove the object based on gameplay/user action, we will just need to call RemoveID

But how to ID a GameObject ? We need to uniquely identify any GameObject in a level. Unity comes with a nice instanceID for each GameObject that we can retrieve using gameObject.GetInstanceID(). How convenient !
But wait, there is a problem, the instanceID, while unique across the level is not persistent, that means everytime you save or load your scene the ID changes. That is pretty bad.
We need to come with a workaround. We can store the GetInstanceID() the first time the gameObject is added to the scene. For that Unity provide us with a nice Reset message that we can override in a MonoBehavior. This function will only be called once.
So lets create this UniqueID component:
UniqueID C# script
 using UnityEngine;
using System.Collections;
public class UniqueID
: MonoBehaviour
{
public int hash;
void Reset ()
{
hash = gameObject.GetInstanceID();
}
}
Pretty simple and straight forward.

Now lets move on our Manager that will hold the objects we need to remove when reentering a level. It will use the singleton pattern and will be persistent over all the levels. So you should add it to a GameObject that will be available from the start.

As the UniqueID is unique in a given level, we will need to store a list of IDs per level, as we are not sure that given 2 differents levels, there won't be any clash.
So basically we will use a Dictionnary where the Key is the levelName and the Value is a list of ints corresponding to the UniqueID.hash of every object we have tracked.
I used theApplication.loadedLevelName and not Application.loadedLevel as you may add and rearrange levels in your build, which would modify level numbering and might cause problems if you save the dictionnary.

The isRemoveID will just check that we the id in the list of tracked ids in the current level.
The RemoveID will store the id in the list. It will first create the list for the current level if it is not yet created
UniqueIDMgr C# script
 using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class UniqueIDMgr
: MonoBehaviour
{
// for singleton pattern
static private UniqueIDMgr self = null;
// will store all already tracked ids, per level
static Dictionary< string, HashSet< int > > hashes = new Dictionary> ();

void Start ()
{
// singleton check
if (self == null) {
self = this;
// prevent unloading
DontDestroyOnLoad (gameObject);
} else {
// destroy this instance, as there is already one
DestroyImmediate (gameObject);
}
}

static public bool isRemovedID (UniqueID id)
{
// first check that we have at least a hashset for the current level
if (!hashes.ContainsKey (Application.loadedLevelName))
return false;
// check if the id.hash is in the table
return hashes [Application.loadedLevelName].Contains (id.hash);
}

static public void RemoveID (UniqueID id)
{
// first check that if we don't have a hashset for that level, create it
if (!hashes.ContainsKey (Application.loadedLevelName))
hashes.Add (Application.loadedLevelName, new HashSet ());
// if the id.hash is not yet added, add it to the table
if (!hashes [Application.loadedLevelName].Contains (id.hash))
hashes [Application.loadedLevelName].Add (id.hash);
}
}
Now we have our system in place, we can start using it

We will create a Pickup script to illustrate the UniqueID/UniqueIDMgr API
Lets pretend the Pickup object will be removed when the player hit it using a collider/trigger. The collsion function will then call the our UniqueIDMgr.RemoveID function
In the Start function, we will check that our pickup has not been removed in previous runs. Based on the result we can either do our normal Start code or do some specific code. For exemple in J.U.M.P, the door will be started in 'open' state if it has been already opened previously.
As we need to have a UniqueID for the Pickup object to behave correctly, we add a
[RequireComponent( typeof( UniqueID ) )] to the script so everytime you add a Pickup component, it will add a UniqueID to the gameObject
Pickup C# script
 using UnityEngine;
using System.Collections;

[RequireComponent( typeof( UniqueID ) )]
public class Pickup
: MonoBehaviour
{
// hold the UniqueID reference
private UniqueID id;

void Awake()
{
// grab the reference
id = GetComponent< UniqueID >();
}

void Start ()
{
// check if already removed
if( UniqueIDMgr.isRemovedID( id ) )
{
// so destroy it
Destroy( gameObject );
// maybe you need to do more stuff, like display another mesh etc...
}
else
{
// normal code
}
}

// this will be called by your gameplay code when the pickup is actually picked
// usually from a 'OnTriggerEnter' or 'OnCollisionEnter' on the collider of this gameObject
void RemoveFromGame()
{
// tell the manager that this ID is removed
UniqueIDMgr.RemoveID( id );
}
}
Picture
The manager
Picture
One pickup with its UniqueID
Picture
Another pickup with different ID
Look like everything is nice and simple

Problem again
Unfortunatly this is not the end of our journey.
If you create a prefab from one of the Pickup object and instanciate it in the scene or even if you just Duplicate the object (CTRL-D) the Reset method is not called and hence we end up with 2 objects with the same ID, preventing us to use correctly the UniqueIDMgr

Solution again
I have not find a way to intercept duplication or prefab instanciation in Unity editor for calling Reset myself.
However we can create an editor window that will take care of setting up the hash member of our UniqueIDs
by just pressing a button :
Picture
Using an EditorWindow we can grab all UniqueID components in the scene and then set up the hash member using the gameObject.GetInstanceID(). This will make sure we don't have duplicate anywhere in the scene.

However a last problem might occurs, if we made a prefab, changing the hash member will not be saved, even if correctly displayed the first time in the Inspector. We need to notify Unity that it is an override value of the prefab instance.
Fortunatly Unity provides with PrefabUtility.RecordPrefabInstancePropertyModifications(). So we just call it when we have a UniqueID in a prefab.

To avoid any problem, we just remove the Reset function from the UniqueID script, leaving just a public member.
JUMPTool editor window C# script
should be under an 'Editor' folder in your 'Assets' folder
 using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;

public class JUMPTool
: EditorWindow
{
[MenuItem("Window/JUMP tool")]
static void Init()
{
JUMPTool win = (JUMPTool)EditorWindow.GetWindow (typeof (JUMPTool));
win.Show();
}

void OnGUI()
{
if( GUILayout.Button( "Set Unique IDs" ) )
SetIDs();
}

void SetIDs()
{
// Grab all UniqueID object in the scene
UniqueID[] uniques = FindObjectsOfType( typeof(UniqueID) ) as UniqueID[];
foreach( UniqueID uid in uniques )
{
// set the hash to the InstanceID of the gameObject
uid.hash = uid.gameObject.GetInstanceID();
// if the gameObject is part of a prefab,
// we need to change the instance value, so that the modification is recorded/saved
if( PrefabUtility.GetPrefabType( uid ) == PrefabType.PrefabInstance )
{
PrefabUtility.RecordPrefabInstancePropertyModifications( uid );
}
}
}
}
Final words
IMHO the solution is quite simple, a caveat is that you need to press the button to for setting unique ID before saving to make sure you don't have any duplicates.
It might be intersting to know if Unity provide a way to be notified when a scene is saved to automatically set the IDs.

Bonus:
If you have a serialisation function for savegame, you can add the UniqueIDMgr hashes dictionnary to it.
 
In J.UM.P, I set up levels in different 'world' or theme

What this means, is that I have a bunch of Materials, colors, music to apply to various elements in a level.

While I use prefabs all over the place, it is a tedious task to go each of the prefab instances to change the 'default' material. Too bad Unity3D don't do prefab in prefab, as it should have solve the problem in a quite elegant and maintanable manner.

Unfortunatly, I have to run my own solution to circumvent the problem.

First I have a 'themeable' prefab, which only resides in the asset database, that contains all materials of a given theme, as well as other variable like fog color and music etc..

Here is a quick preview of my Themeable class :
 public class Themeable : MonoBehaviour { 
public AudioClip levelMusic;
public Material floor;
public Material wall;
public Material line;
public Material ceiling;
public Material mediumBump;
public Material highBump;
public Material normal;
public Color32 fog;

public Material[] materials
{
get {
List< Material > mats = new List< Material >();
if( floor != null && !mats.Contains( floor ) )
mats.Add ( floor );
if( wall != null && !mats.Contains( wall ) )
mats.Add ( wall );
if( line != null && !mats.Contains( line ) )
mats.Add ( line );
if( ceiling != null && !mats.Contains( ceiling ) )
mats.Add ( ceiling );
if( mediumBump != null && !mats.Contains( mediumBump ) )
mats.Add ( mediumBump );
if( highBump != null && !mats.Contains( highBump ) )
mats.Add ( highBump );
if( normal != null && !mats.Contains( normal ) )
mats.Add ( normal );
return mats.ToArray();
}
}
}
The materials property is just here to get all materials of the theme in one array

Given that prefab, I need to apply the various theme materials to the current level. As I have no way of knowing that a given object is the scene is 'themeable', I have to resort on a semi automatic solution and iterate some way over all materials in the scene and allow user to change it to one of the Themeable object.

In order to change materials, one can create an Editor Window that list all materials in the scene, and provide a way to assign a choosen material to another one, effectivly replacing the old material by the new

Creating a custom Editor is quite simple and straight forward, you need to derive your class from UnityEditor.EditorWindow and place the file in the 'Editor' folder in the assets database
 using UnityEngine; 
using UnityEditor;
using System.Collections;
using System.Collections.Generic;

public class MyTool : EditorWindow
{
[MenuItem("Window/My tool")]
static void Init()
{
MyTool win = (MyTool)EditorWindow.GetWindow (typeof (MyTool));
win.Show();
}
void OnGUI()
{
}
}
With that setup, you just have a small empty window, when you go under 'Window' and select "My tool" ( this is wired by using the [MenuItem("Window/My tool")] attribute above the Init function

Time to add some functionnalities. 
All the gui stuff will happen as always with Unity3D in the OnGui function of the class.

So first, we need to know what Themeable to use. For that we can use an ObjectField. You can use it either from EditorGUILayout or EditorGUI depending if you want automatic layout or if you want to deal with the layout yourself. The call will return the object dropped, so better store it somewhere for further process.

To add a little prefix in front of it, we can use PrefixLabel to just do that.
As I'm using EditorGUILayout , I will enclose those 2 calls between BeginHorizontal/EndHorizontal for the automatic layout to know I want them side by side
   public class MyTool : EditorWindow 
{
Themeable theme;

....

void OnGUI()
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel( "Theme" );
theme = EditorGUILayout.ObjectField( theme, typeof( Themeable ), true ) as Themeable;
EditorGUILayout.EndHorizontal();
}
}
We can check if we have a dropped object by checking the theme variable. As soon as we have one, we will start listing all materials from the scene.

To collect all materials, we will have to find all Renderable objects, iterate over them and store the sharedMaterials used in them
  void OnGUI() 
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel( "Theme" );
theme = EditorGUILayout.ObjectField( theme, typeof( Themeable ), true ) as Themeable;
EditorGUILayout.EndHorizontal();

// make sure we have a theme dropped
if( theme != null )
{
// list of all materials
List< Material > mats = new List();
// get all MeshRenderer
MeshRenderer[] all = FindObjectsOfType< MeshRenderer >();
// iterate over all
for( int i = 0 ; i < all.Length; ++i )
{
// iterate over all sharedMaterials
for( int m = 0; m < all[i].sharedMaterials.Length; ++m )
{
// make sure we have an actual material,
// and we don't have it already in the list
if( all[i].sharedMaterials[m] != null
&& !mats.Contains( all[i].sharedMaterials[m] ) )
{
mats.Add( all[i].sharedMaterials[m] );
}
}
}
}
}

What we want to display is the list of above materials, with a combo from where the user will be able to choose a material from the Themeable object. We just have to store the name of the material. That is why I have a materials property in the Themeable object
 void OnGUI() 
{

......

// make sure we have a theme dropped
if( theme != null )
{

......

// list of name of Materials from the Theme
List< string > matNames = new List< string >();
for( int i = 0 ; i < theme.materials.Length; ++i )
{
matNames.Add ( theme.materials[i].name );
}
}
}
Unfortunalty there is no 'ComboBox' like widget in the EditorGUI, but we can use the Popup widget that will do the job perfectly well
The Popup function can take an array of string and will return you the selected item or -1 if none selected
 void OnGUI() 
{

......

// make sure we have a theme dropped
if( theme != null )
{

......

// display the materials
foreach( Material mat in mats )
{
// we will have the name of the scene material and a popup with materials names from the theme
// side by sie
// one material on each line
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel( mat.name );
int choosen = EditorGUILayout.Popup(-1, matNames.ToArray() );
EditorGUILayout.EndHorizontal();
}
}
}
Once we have a material choosen ( so that choosen will not be -1 ), we can replace all occurence of the current material in the scene by the one from the Themeable. which will be theme.materials[choosen]
 void OnGUI() 
{

......

// make sure we have a theme dropped
if( theme != null )
{

......

// display the materials
foreach( Material mat in mats )
{

.....
int choosen = EditorGUILayout.Popup(-1, matNames.ToArray() );
// a material from the theme has been chosen
if( choosen != -1 )
{
Material replace = theme.materials[ choosen ];
// replace in all renderer that use that material
for( int i = 0 ; i < all.Length; ++i )
{
// need to use a temporary material list as
// MeshRenderer.sharedMaterials item can not be modified
// we need to modified the whole list
List< Material > newMats = new List();
for( int sharedIdx = 0; sharedIdx < all[i].sharedMaterials.Length; ++sharedIdx )
{
if( all[i].sharedMaterials[sharedIdx] == mat )
{
newMats.Add( replace );
}
else
{
newMats.Add ( all[i].sharedMaterials[sharedIdx] );
}
}
all[i].sharedMaterials = newMats.ToArray();
}
}
EditorGUILayout.EndHorizontal();
}
}
}
Everything is now in place and we can easily change a material all over the scene by one click.
Window editor
The Custom themable window and the World Theme in inspector. Changing one material of the scene to one of the selected theme is just a matter of selecting material from a popup
Photo
The original level, within the 'World01' theme
Photo
10 seconds later, it is setup with the bonus theme
Photo
Another option, that will take less space in the editor window, would be to replace the list of all scene materials by using a Popup like the one used in for the Theme materials (as in the picture ).


While it seems a viable and better option, I tend to prefer seeing all my materials and just have to do a one click operation.


Your opinion may vary, and in the end, it is up to you to choose the correct way to display and provide info for the end user to be the most productive.


Here is the code for the above Popup display :

 void OnGUI() 
{

......

// make sure we have a theme dropped
if( theme != null )
{

......

// display the materials
EditorGUILayout.BeginHorizontal();
matFromScene = EditorGUILayout.Popup( Mathf.Min ( matFromScene, matSceneNames.Count-1), matSceneNames.ToArray() );
int choosen = EditorGUILayout.Popup(-1, matNames.ToArray() );
if( choosen != -1 )
{
Material original = mats[ matFromScene ];
Material replace = theme.materials[ choosen ];
for( int i = 0 ; i < all.Length; ++i )
{
List< Material > newMats = new List();
for( int ma = 0; ma < all[i].sharedMaterials.Length; ++ma )
{
if( all[i].sharedMaterials[ma] == original )
{
newMats.Add( replace );
}
else
{
newMats.Add ( all[i].sharedMaterials[ma] );
}
}
all[i].sharedMaterials = newMats.ToArray();
}
}
EditorGUILayout.EndHorizontal();
}
}
You can use the code, modify it as you please

Thanks for reading this far
 
J.U.M.P started as a little experiment in Unity3D earlier this year.

First off, I use Unity3D on my daily job, and it was my first encounter with C#. I used to and still do code in C/C++, and so was a bit lost when I had to switch and also not very productive (in regards with my productivity in C++). 

I wanted to play with the integrated physics and in the same time try to learn and practice more C# stuffs.

Knowing that I'm not at all an artist, I looked for something very simple graphic wise, and end up playing with cubes and cylinders

I quickly came up with the spring idea, as it was quite natural to play with rigidbody, joints and physical materials

Then, contrary to all my previous home projects, when I usually get to where I learned or succesfully achieved what I was looking for, I decided to get it finished and hopefully can make some money out of it. 

Adding platforms was the next step, with bumper all over the place and somewhere to go.

As I don't have many hours to play per day, and lot of people like me neither, I came up with  the idea of small rooms to complete in less than 3 minutes, so I can play at least my game when I have 5 minutes free. While I like the small room idea, I feel the need to still have something like a 'world', so I just make the room connected to each others. While in the current version it is still not very likely you ever notice it, I plan to change that soon, so you can see how all the rooms are connected together and are correctly arranged in 3D

But what is a platformer if you don't have something to collect all around : something boring ! So I added coins and time bonuses.

That were the ideas from the first month ( remember, I only work on J.U.M.P only in my commute time and sometimes in the evening )


So J.U.M.P was born
 
Welcome to my first blog ever

A bit of presentation:
I'm a video game programmer with between 15 and 20 years of experience, started my career way back in the x86 assembler age.


My day job is more focussing on serious gaming and A.I. but I'm still making games in my spare time.


I will try to posts here about all my game related projects, and you can also expect some technical ramblings and code snippets

Hope you'll enjoy the trip