Subscribe to Jupiter Lighthouse Studio        RSS Feed
-----

ORGANIZING A GROUP OF GAMEOBJECTS IN UNITY

Icon Leave Comment
So I'm going to be spending the next few weeks introducing you to the code behind our project. So you can see what I as a programmer do when trying to design video games. I wracked my brain about where to start though, there's so many things I could show you. Random snippets of code here and there. But... there's a problem with that. A lot of my code is full of references to other bits of code that at first glance may or may not make sense unless you've been introduced to the architecture of the framework I've been building. A framework that I want to be useful for any game that I write in Unity, not just the current project we're working on.

The framework is called SpacePuppy, and it attempts to solve various problems that I found while working with Unity. Problems like an inconsistent interface for moving objects around with the CharacterController or Rigidbody, or any other method you may hack up on your own time. Or a way to send structured messages to and from GameObjects in a type safe manner. Everything I want to discuss though boils down to the first problem I ran into when first starting out with Unity.


Entities

One of the most common problems I find when designing avideo game is the representation of players, enemies, and other NPCs. These 'entities' will be made up of quite a bit of graphical parts, animations, and a pile of code. It's critical that we organize all of these parts in a way that is manageable by the various sorts of people that will have their hands on it. In a small team like an indie project, non-coders may have to deal with these entities and need to be able to handle basic wiring of them with out necessarily needing to play with the code.

Posted Image

An entity like the one above may consist of multiple GameObjects parented in each other creating a hierarchy. All of these GameObjects have some super parent, it's the GameObject in which the entire hierarchy begins. We can look at this as the main GameObject for the entire entity. I like to call it the 'root'.

In this example the root has been named 'Roller_ham' and the designer organized all its parts out so its easy to manage in the level editor. We have some GameObjects representing the various abilities like its MobileMotor and its AI. There is also a GameObject called Rig in which the skeleton and mesh for the entity are kept, along with a hitbox. Hitboxes will often be somewhere in the hierarchy of the skeleton so that it inherits the position of various bones during animation.

Posted Image Posted Image

Attached to the Attributes GameObject is a HealthMeter and attached to the HitBox_Body is a SphereCollider set to a Trigger. When something comes along that can do damage it will enter the SphereCollidertrigger and need to deal damage to the HealthMeter of this entity.

This is our problem.

1) We need a way to determine what entity the SphereCollider is a member of.

2) We need a way to get a reference to the HealthMeter of that entity.



Root Tag

The way I resolved the first problem is a fairly simple method. What I do is define a tag called 'Root' and any GameObject that should be considered a root gets tagged as so. Now all I have to do is loop up the parent chain until I find the first GameObject that is tagged as root, and there we've found the entity.

We can write this loop into a utility method to make it easy to access on the fly. Lets go ahead and make the method an extension method for ease of use. Also if we don't find a root lets consider the self to be the root.

public static GameObject FindRoot(this GameObject go)
{
	if(go == null) return null;
	if(go.tag == "Root") return go;
	
	Transform t = go.transform.parent;
	while(p != null)
	{
		if(p.tag == "Root") return p.gameObject;
		p = p.parent;
	}
	return go;//if no root found, consider self root
}



Note that this design allows for entities to be within entities. We could have a destructibleweapon with a hitbox and its own HealthMeter. When we go up the chain we will find the entity that is the weapon, and not the entity holding the weapon.



Multi Tag

Now what if you wanted to tag that root as something else as well as being the root. Lets say that GameObject also tagged if it was an enemy or not. This was another problem I ran into when using tags, the fact you can only use one tag at a time. So I wrote up a script that allows you to add multiple tags to a GameObject. This is a fairly simple script with just one array to store all the tags in. I make the array private because I'm a stickler about keeping my fields private. The rest of the script is just methods that make it easy to add/remove/test for tags. Lastly on Awake we make sure that the attached GameObject is tagged as 'MultiTag'. We're going to need this to reduce the overhead of finding GameObjects in the scene with a certain tag. We can just search for GameObjects with the 'MultiTag' tag, and then test their MultiTag component. Where as without it we'd have to gather up every GameObject and test if they have a MultiTag component or not.

Spoiler


We don't want to keep track of all the available tags out there and have to modify the array in the inspector. So lets instead create a custom inspector that displays all known tags as toggle switches that we can turn on and off. Lets also leave out some tags that a MultiTag object should never be. The object shouldn't be considered Untagged ever. MultiTag is reserved for reprsenting objects that have a MultiTag component so it's redundant. I also personally left out EditorOnly for my own personal reasons, if you want it, just remove the test for it in this code.

Spoiler

Posted Image

As a result we get a component that allows us to give a component multiple tags. You may notice though that the GameObject is actually tagged as 'MultiTag'. This means that the built in methods for finding GameObjects by tag won't work anymore and instead must be rewritten to meet our new standard.

We will need a method to test if a GameObject has a tag, a method to search the scene for GameObjects with a tag, find a child with a tag, and lastly lets throw in a method to find a parent with a tag. This can act as our 'FindRoot' function from before. Again they'll be extension methods and lets also create overrides to make it work with both GameObject and Component types. This is to emulate the composition of other methods already present on GameObject and Component in Unity like 'GetComponent'.

Spoiler


Using this new code we can now rewrite our 'FindRoot' method to use these new methods.

        /// <summary>
        /// Attempts to find a parent with a tag of 'Root', if none is found, self is returned.
        /// </summary>
        /// <param name="go"></param>
        /// <returns></returns>
        public static GameObject FindRoot(this GameObject go)
        {
            if (go == null) return null;

            var root = FindParentWithTag(go.transform, SPConstants.TAG_ROOT);
            return (root != null) ? root : go; //we return self if no root was found...
        }

        public static GameObject FindRoot(this Component c)
        {
            if (c == null) return null;
            
            //return FindRoot(c.gameObject);
            var root = FindParentWithTag(c.transform, SPConstants.TAG_ROOT);
            return (root != null) ? root : c.gameObject;
        }



With all of this we have come up with a reusable way to locate the root of an entity. The great part is that it is independent of any other architecture. We don't need to create custom components to attach to the various colliders to reference their roots. We don't have some custom component defining this as an entity. The root could be used for any type of entity. It could just be a lamp post for all we care, it will still have a root, and we can programmatically reference it in code consistently no matter what the entity is.

Furthermore we got an extra little feature to allow for multi-tagging GameObjects. This could be used for all sorts of tasks completely independent of the root design.

This leaves the second half of our problem. We know how to get a reference to the root, but how do we get a reference to the HealthMeter. See, the designer in this situation doesn't like having 30+ components on the root of some entity representing every single aspect of the entity. Instead the designer has created GameObjects as the children of that entity and organized similar components together. Components dealing with AI going on the AI GameObject. Components dealing with the Mobility of the entity go on the MobileMotor GameObject. Our HealthMeter is on the Attributes GameObject.

So now we might say that OK, as long as the Designer keeps naming those GameObjects consistently, all we need to do is get the root and then we could use the 'Find' method of the transform of that root.

var attribs = hitCollider.FindRoot().transform.Find("Attributes");
var health = attribs.GetComponent<HealthMeter>();



Personally though, I don't like this option. What happens if in the future we decide we don't like the name 'Attributes' for this GameObject. What if we decide that we're going to move the HealthMeter to a different GameObject all together for organizational purposes. Even more likely, what if a designer misspells 'Atributs'. Personally I've never really been a fan of using strings for things, and if I do I always put those strings in some constant somewhere so I don't have to spell them out all the time. Problem them arises, I'm going to have constants for all of the possible hierarchies out there. And there's another problem, what if a designer NEEDS to put the HealthMeter somewhere else. Well, maybe not the HealthMeter... but what about a Weapon script. That could be attached to any given child of the skeleton rig, and moved for various reasons!



FindComponent

There's another way to look at this though. An entity is going to have several components attached to it that it will only have one of.

Instead how about we assume this expectation for components like the 'HealthMeter' and instead just search the Entity for the component. Lets also use the 'Root' tag to glue this all together.

First some utility functions to get all the GameObject within an entity.

Spoiler


And lets complete that with methods to find components of whatever type within an entity.

Spoiler


With these methods we can now easily access an entity and all its constituentparts as if it was a single object. Lets give this a try with a script that OnTriggerEnter finds the HealthMeter of what it struck and applies damage.

void OnTriggerEnter(Collider collider)
{
	var health = collider.FindComponent<HealthMeter>();
	if(health != null)
	{
		health.Strike(this.AttackDamage);
		if(!health.IsDead)
		{
			//if not dead, cause some knockback
			MobileKnockback.KnockBack(collider.FindRoot(), this.KnockbackIntensity);
		}
	}
}




Next Time

Next time I'll be discussing our Notification system and sharing that piece of code with everyone to use. It uses the concept of entities to send and receive messages between not just GameObjects, but entities as well. You can register as an observer of a specific GameObject or even its root and receive event callbacks when that GameObject or the entity (represented by the root) posts some notification. I find this tool very powerful, and I've also noticed that other people have written and shared various versions of it. I think the bonus features of the entity, and type safety, of this one will make it that much better for those looking for a notification system.

See you next time.



This was a post from Jupiter Lighthouse Studio.
You can find this article at and other at: http://jupiterlighth...jects-in-unity/

0 Comments On This Entry

 

Trackbacks for this entry [ Trackback URL ]

механические захваты kenworth ремонт лстк технология from механические захваты kenworth ремонт лстк технология

Tracked on Oct 17 2014 02:16 PM

тентовая ткань административные здания проектирование промышленных объектов from тентовая ткань административные здания проектирование промышленных объектов

Tracked on Oct 17 2014 02:16 PM

December 2017

S M T W T F S
     12
3456789
1011 12 13141516
17181920212223
24252627282930
31      

Tags

    Recent Entries

    Search My Blog

    0 user(s) viewing

    0 Guests
    0 member(s)
    0 anonymous member(s)

    Categories