#Himalaya: Parenting 101

Unlike what the title might suggest, this post does not offer advice on how to raise your kids. If you were looking for that, I’m sorry. But looking for parenting advice on the internet is probably not a good idea in the first place. Anyway, I digress…

Today I will show you how I implemented a simple parent-child type of hierarchy for game entities in Himalaya. What do I mean by that? Well, I’m glad you ask. Let’s use an example for demonstration. I’m gonna use a few sprites I created for an upcoming project of mine.

Let’s say we have one Game Entity (an Actor to be specific, see my last post) that represents the player:

axeGun1single

We want to equip our player with a gun, which is also a Game Entity:

gunHands1

So, if we combine the two layers we end up with this:

axeGun1singleWithGun

Let’s also make our player drop a shadow to be extra fancy:

axeGun1singleWithGunAndShadow

So now we have three Game Entities in total. And obviously they heavily depend on each other. If the player moves or rotates or something like that, we want this change to also be applied to the gun and the shadow, right? You’ll notice that we kinda need to build ourselves some sort of hierarchy where the player entity is on top and both the gun and the shadow branch off of it. I’m talking about something like this:


PlayerHierarchyExample

Now, this is obviously a very simple hierarchy, but eventually in our game we are very likely to have very complex hierarchies and, therefore, we need to write ourselves some code that helps us create them.

But before I go on any further, let me inform you that there has been a slight change in the design of class relations and dependencies: The “Transformable” class has been renamed “Transform” and is no longer abstract. The Game Entity class now no longer inherits from this class but implements it as a member. You will see that in action in a second.

Let’s do this!

Alright, so in order to actually implement this sort of behavior I added a few properties to the Game Entity class and to what is now the Transform class. Their job is to keep track of what Game Entity our instance is a child of and what instances it is the parent of. Pretty simple and straight-forward. The interesting part is to implement the behavior of when the hierarchy was to change. Let’s take a look at another diagram:


GameEntityHierarchy

So here we have a somewhat more complex hierarchy. We’e got “GameEntityA” with the child entities “GameEntityA_A”, “GameEntityA_B” and “GameEntityA_C” and each of those again have two children. If the position of “GameEntityA” changes, all the child and subchild entities will also move in relation. But what if we were to change the hierarchy at runtime?


GameEntitiyHierarchy2

Of course, we would have to make some recalculations. Changing the parent of “GameEntityA_B_A” obviously also changes the list of child entities inside “GameEntityA_A” and vice versa. Also “GameEntityA_A_C” (formerly “GameEntityA_B_A” needs to be removed from the list of child entities of “GameEntityA_B”. But we don’t want to check specific conditions and write paragraphs of code every time we change our hierarchy. We want to be able to do something as simple as this:

GameEntityA_B_A.Parent = GameEntityA_A;

Or alternatively:

GameEntityA_A.AddChildEntity(GameEntityA_B_A);

The underlying code should then “automagically” change the hierarchy appropriately.

Now, take a look at my implementation. The following code is obviously work in progress and subject to change:

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Linq;

namespace Yetibyte.Himalaya.GameElements {

	public abstract class GameEntity : IUpdate, ITimeScale, IDraw {

        // Fields

        private bool _isActive = true;
		private List<GameEntity> _childEntities = new List<GameEntity>();
        private GameEntity _parentEntity;

		// Properties

        /// <summary>
        /// A <see cref="GameEntity"/> that is inactive will be ignored by the <see cref="Yetibyte.Himalaya.GameElements.Scene"/> and will
        /// therefore not be updated.
        /// </summary>

        public bool IsActive {

            get { return _isActive; }
			set { _isActive = value;}

		}

		public String Name { get; protected set; }

        /// <summary>
        /// The <see cref="Yetibyte.Himalaya.GameElements.Scene"/> this GameEntity lives in.
        /// </summary>

	    public Scene Scene { get; set; } 

		public Game Game {

			get { 

				if(Scene == null)
					return null;

				return Scene.Game;

			}

		}

        /// <summary>
        /// Sets the parent <see cref="GameEntity"/> of this <see cref="GameEntity"/>. Will automatically call AddChildEntity and RemoveChildEntity methods where needed.
        /// That means: When the parent entity changes, this GameEntity will be added to the child entity list of the future parent and removed from the orignal parent's list of child entities
        /// (unless the original parent was null.
        /// </summary>

        public GameEntity ParentEntity {

            get { return _parentEntity; }

            set {

                GameEntity futureParent = value;

                if(this.HasParent) {

                    _parentEntity.RemoveChildEntity(this);

                }

                futureParent?.AddChildEntity(this);

                _parentEntity = futureParent;

            }

        }

        /// <summary>
        /// A list of Game Entities that are children of this <see cref="GameEntity"/>.
        /// </summary>

		public List<GameEntity> ChildEntities {

			get { return _childEntities; }
			protected set { _childEntities = value; }

		}

        public float TimeScale { get; set; } = 1f;

        public bool IsDestroyed { get; protected set; }

        public Transform Transform { get; set; } = new Transform();

        public bool HasParent => _parentEntity != null;

        public int DrawOrder { get; set; }

        // Constructor

        protected GameEntity(string name, Vector2 position) {

			this.Name = name;
			this.Transform.Position = position;

		}

        // Methods

		public virtual void Initialize() {

		}

		public virtual void Update(GameTime gameTime, float globalTimeScale) {

		}

		public virtual void Draw(SpriteBatch spriteBatch, GameTime gameTime) {

		}

        /// <summary>
        /// Destroys this <see cref="GameEntity"/>. It will be removed from the <see cref="Yetibyte.Himalaya.GameElements.Scene"/> it lived in.
        /// </summary>

		public void DestroyEntity() {

			foreach(GameEntity childEntity in ChildEntities)
				childEntity.DestroyEntity();

            if(!IsDestroyed) {

                IsDestroyed = true;
                Scene.RemoveGameEntity(this);

            }

		}

        /// <summary>
        /// Adds the given <see cref="GameEntity"/> to the list of child entities. Also sets the parent entity respectively. The child
        /// Game Entity will also optionally be added to the Scene the parent lives in (if it wasn't already).
        /// </summary>

        /// <param name="childEntity">The child entity to add.</param>
        /// <param name="doAddToScene">Should the child Game Entity be added to the Scene as well? True by default.</param>
        public void AddChildEntity(GameEntity childEntity, bool doAddToScene = true) {

            if (!IsParentOf(childEntity)) {

                ChildEntities.Add(childEntity);
                Transform.AddChild(childEntity.Transform);
                childEntity.ParentEntity = this;

                if(doAddToScene)
                    Scene.AddGameEntity(childEntity);

            }

        }

        /// <summary>
        /// Removes the given  <see cref="GameEntity"/> from the list of child entities. Also sets the parent of the given entity to null.
        /// The child Game Entity will also optionally be removed from the Scene the parent lives in (if it wasn't already).
        /// </summary>

        /// <param name="childEntity">The child entity to remove.</param>
        /// <param name="doRemoveFromScene">Should the child Game Entity be removed from the Scene as well? False by default.</param>
        public void RemoveChildEntity(GameEntity childEntity, bool doRemoveFromScene = false) {

            if (IsParentOf(childEntity)) {

                ChildEntities.Remove(childEntity);
                Transform.RemoveChild(childEntity.Transform);
                childEntity.ParentEntity = null;

                if (doRemoveFromScene)
                    Scene.RemoveGameEntity(childEntity);

            }

        }

        /// <summary>
        /// Checks whether the given <see cref="GameEntity"/> is included in the list of child entities fo this <see cref="GameEntity"/>.
        /// </summary>

        /// <param name="childEntity">The child game entity.</param>
        /// <returns>True if the given entity is a child of this GameEntity.</returns>
        public bool IsParentOf(GameEntity childEntity) => ChildEntities.Contains(childEntity);

    }

}

As you can see, I highlighted the most relevant lines of code for you. Luckily, I already included some XML documentation in the code, so I don’t have to come up with a super lengthy explanation now. 😀

If you study the code a bit, you will notice that there are apparently also methods in the Transform class that handle child-parent relations. This is because I did not want to couple the two classes Transform and GameEntity too tightly. Even though the main purpose of Transform is to implement it in the GameEntity class it may also be used elsewhere. So the Transform hierarchy is responsible only for transferring changes in position, scale and rotation throughout the “family tree”. Changing the hierarchy of Game Entities obviously also changes the hierarchy of their respective Transforms.

For clarification, take a look at the Transform class (also work in progress):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using MonoGame.Framework;

namespace Yetibyte.Himalaya.GameElements {

    /// <summary>
    /// A class that can be used as a component for any object that can be positioned, scaled and rotated.
    /// </summary>

    public class Transform {

        // Fields

        private Transform _parent;
        private Vector2 _position = Vector2.Zero;

        // Properties

        public Transform Parent
        {

            get { return _parent; }

            set
            {

                Transform futureParent = value;

                if (this.HasParent) {

                    _parent.RemoveChild(this);

                }

                futureParent?.AddChild(this);

                _parent = futureParent;

            }

        }

        public List<Transform> Children { get; private set; } = new List<Transform>();

        ///<summary>
        /// The position relative to the parent Transform.
        /// </summary>

        public Vector2 LocalPosition {

            get
            {
                Vector2 relation = Parent != null ? Parent.LocalPosition : Vector2.Zero;
                return Position - relation;
            }

            set
            {
                Vector2 relation = Parent != null ? Parent.Position : Vector2.Zero;
                Position = value + relation;
            }

        }

        /// <summary>
        /// The global scale of this Transform (read-only). Use <see cref="LocalScale"/> to manipulate the scaling value.
        /// </summary>

        public Vector2 Scale
        {

            get
            {
                Vector2 relation = Parent != null ? Parent.Scale : Vector2.One;
                return LocalScale * relation;
            }

        }

        /// <summary>
        /// The global rotation of this Transform in radians (read-only). Use <see cref="LocalRotation"/> to manipulate the rotation value.
        /// </summary>

        public float Rotation
        {

            get
            {
                float relation = Parent != null ? Parent.Rotation : 0f;
                return relation + LocalRotation;
            }

        }

        /// <summary>
        /// The global position. Setting this value will also manipulate the position of all child Transforms.
        /// </summary>

        public Vector2 Position
        {

            get { return _position; }

            set
            {

                Vector2 delta = value - _position;

                foreach (Transform child in Children)
                    child.Position += delta;

                _position = value;

            }

        }
        public Vector2 Origin { get; set; }
        public Vector2 LocalScale { get; set; } = Vector2.One;
        public float LocalRotation { get; set; }

        public bool HasParent => _parent != null;

        // Methods

        public Vector2 Translate(Vector2 offset) {

            Position += offset;
            return Position;

        }

        public Vector2 TranslateLocally(Vector2 offset) {

            LocalPosition += offset;
            return LocalPosition;

        }

        public void AddChild(Transform childTransform) {

            if (IsParentOf(childTransform))
                return;

            Children.Add(childTransform);
            childTransform.Parent = this;

        }

        public void RemoveChild(Transform childTransform) {

            if (!IsParentOf(childTransform))
                return;

            Children.Remove(childTransform);
            childTransform.Parent = null;

        }

        /// <summary>
        /// Checks whether the given <see cref="Transform"/> is included in the list of child entities of this <see cref="Transform"/>.
        /// </summary>

        /// <param name="childTransform">The child transform.</param>
        /// <returns>True if the given Transform is a child of this Transform.</returns>
        public bool IsParentOf(Transform childTransform) => Children.Contains(childTransform);

    }

}

I don’t want to go into too much detail. We got a little bit of recursion going on.  You may have also noticed that I’m a friend of expression bodied members. What you may have noticed as well is that the code handling child-parent relations are very, very similar in both classes. What? We don’t want redundant code, right? Right!

That’s why I created a generic class for cases where we want to implement this sort of hierarchy. Since this can be a reoccurring pattern, an abstract class called “ParentChildHierarchy” can now be found in my utility library:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Yetibyte.Utilities {

    public abstract class ParentChildHierarchy<T> where T : ParentChildHierarchy<T> {

        // Fields

        protected T _parent;

        // Properties

        public T Parent
        {

            get { return _parent; }

            set
            {

                T futureParent = value;

                if (this.HasParent) {

                    _parent.RemoveChild((T)this);

                }

                futureParent?.AddChild((T)this);

                _parent = futureParent;

            }

        }

        public List<T> Children { get; protected set; } = new List<T>();
        public bool HasParent => _parent != null;

        // Methods

        public bool AddChild(T child) {

            if (IsParentOf(child))
                return false;

            Children.Add((T)child);
            child.Parent = (T)this;

            return true;

        }

        public bool RemoveChild(T child) {

            if (!IsParentOf(child))
                return false;

            Children.Remove((T)child);
            child.Parent = null;

            return true;

        }

        public bool IsParentOf(T child) => Children.Contains(child);

        public T GetAncestor() {

            ParentChildHierarchy<T> currentChild = this;

            while (currentChild.Parent != null) {

                currentChild = currentChild.Parent;

            }

            return (T)currentChild;

        }

    }

}

This class will soon be added to the mix to simplify both the Game Entity and the Transform class. Both will then simply inherit from the ParentChildHierarchy class. As you can see this class also has a method for returning the “ancestor”, which is the term I use for the instance that is at the very top of the hierarchy (see the diagrams above).

Thanks for your attention

So, I guess that’s it for today. Take the code in for a moment if you like. Remember, the full source code of the project is available in my GitHub repository:

https://github.com/Yeti47/Yetibyte.Himalaya

himalayaEngineLogoBigger

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s