The Way Things Used To Be :: Reset Components and Unity 3D

One of the very interesting aspects of Unity 3D and its architecture is the fact that it is component based. This one factor makes it very easy to implement some very interesting behavior in your games that separates concerns rather nicely. One such interesting notion is that of a Reset component.

When the Horseman created Hug Monster he realized quickly that he’d need to store the initial positions of the game’s objects somehow. Re-loading the Scene every time the player dies is simply unacceptable given the time Unity takes to perform loads, so somehow there must be a way to easily store initialization data. In other langauges such as AS3 the most tempting way to do this would be to implement something like :

HugMonsterGameObject
  public function getInitialValuesFromScene():void{ /*collect initial values*/ }
  public function reset():void{ /*reset to initial values*/ }

As a consequence, all game objects would carry this baggage around, and they all must remember to subclass HugMonsterGameObject whether they needed the functionality or not. This doesn’t sound bad on its face, but it can bloat the number of responsibilities of a given class, such that it simply has to concern itself with “too much.” On the bright side, Unity’s component-based architecture allows us to split this behavior out completely! I do have a Player class, and a Hugmonster class, but neither of them knows a thing about how to grab initial coordinates or restore to an “initial” state. Instead, the gameObjects that have Player and Hugmonster components (as well as everything that can be moved or altered) have a ResetBehaviour object sitting alongside their “main” component.

How it works is as such.

// In the file ResetBehaviour.cs
//    This is merely an abstract class, to give 
//    us a typed Component that is responsible 
//    for Resetting things.
 
using UnityEngine;
using System.Collections;
 
public class ResetBehaviour : MonoBehaviour {	
	virtual public void RecordStartingVariables(){}	
	virtual public void ResetGO(){}
}
 
 
// In the file GenericResetBehaviour.cs
//    Our most basic implementation of ResetBehaviour.
//    This is ideal for things like the blocks that 
//    the Hug Monster can shove around, or for
//    other miscelaneous objects that only move 
//    with position / rotation and have a 
//    changing velocity.
 
using UnityEngine;
using System.Collections;
 
public class GenericResetBehaviour : ResetBehaviour {
 
	public Vector3 resetToPosition;
	public Quaternion resetToRotation;
	public Vector3 resetRigidBodyToVelocity;
	public Vector3 resetAngularVelocity;
 
	// Use this for initialization
	void Start () {
                // Unity's default Start method
                // is an ideal spot to initialize
                // since this is where the object
                // was placed in the editor.
		RecordStartingVariables();
	}
 
	override public void RecordStartingVariables(){
		resetToPosition = transform.position;
		resetToRotation = transform.rotation;
		if(rigidbody != null){
			resetRigidBodyToVelocity = rigidbody.velocity;
			resetAngularVelocity = rigidbody.angularVelocity;
		}
	}
 
	override public void ResetGO(){
		Debug.Log("Default ResetBehavoiur");
		transform.position = resetToPosition;
		transform.rotation = resetToRotation;
		if(rigidbody){
			rigidbody.velocity = resetRigidBodyToVelocity;
			rigidbody.angularVelocity = resetAngularVelocity;
		}
	}
}

If an object needs a more specialized form of ResetBehaviour, I can simply derive from GenericResetBehaviour without necessarily overriding anything in the Player, Hugmonster, Switch, Wall, Camera, or any other class definition. This makes content creation and behavioral scripting ridiculously flexible.

But Mr. Horseman, why make Reset components for all these objects? Don’t you have to then grab that component from each game object, by specific type, and call ResetGO() on it?

Aaaah, I’m glad you asked. You do NOT have to do any such thing! The reset level code, which lives inside the GameManager class looks something like this…

public void RestartLevel(){
 
		ResetBehaviour[] rbs = Component.FindObjectsOfType(typeof(ResetBehaviour)) as ResetBehaviour[];
		foreach(ResetBehaviour rb in rbs){
			rb.ResetGO();
		}
 
                // a few more lines of code for displaying the Game Over text go here.
}

Because the ResetBehaviour class is virtual, and because all the Reset components derive from it in some manner, all I have to do is find every component derived from ResetBehaviour using the handy function shown above and then iterate through the resulting array an call ResetGO() on each. Every item in the scene will reset to its initial position as specified by the component without any further work on the developer’s part.

I have to say that I first looked at the component system and saw the potential for chaos, but in fact the philosophy underpinning the Unity Editor gives you ways to funnel that chaos into pure elegance.

Tags: , , , ,

  1. #1 written by OvermindDL1 March 12th, 2012 at 12:28

    You know something that is really bad about Hug Monster? It uses Unity. Flash works well on linux, as do quite a variety of other things (like Panda3D), but Unity just says that this is an unsupported platform and as such the game is completely unplayable.

  2. #2 written by The Horseman March 12th, 2012 at 12:43

    Hi Overmind,

    Since you’re on Linux, would you mind trying out the NaCl version I’ve published? It works on Windows Chrome and Mac Chrome, so it would be interesting to see if you can load it up on a linux install of Chrome or Chromium. The load bar will probably look funny but it should otherwise play exactly as the Unity web player version. I’ve thusfar been unable to test it on a linux-based NaCl browser

    http://hug-monster.com/nacl/

    ^^ link above

  3. #3 written by Joshimoo April 28th, 2012 at 05:02

    Any reason the “ResetBehaviour” class is not defined as abstract?

    It mentions in the comments, that it’s supposed to be abstract, but the actual implementation is not?

    So I was wondering if there is any reason why you did not define it as abstract?

    Thanks for the article

  4. #4 written by The Horseman April 28th, 2012 at 07:52

    @Joshimoo It’s because that doesn’t appear to work, at least for all subclasses of the ResetBehaviour.

    The void Start() method of the GenericResetBehavior doesn’t appear to have any effect for many of my GameObjects when ResetBehaviour is declared as “abstract” in the C# keyword sense. It’s mysterious since not all subclasses exhibit this behavior (and indeed some of those subclasses are subclasses of GenericResetBehaviour, making it doubly confusing).

    Probably not the nuts-and-bolts answer you were hoping for, but it’s what I’ve got for now.