Design Patterns in Unity3D #2 - Decorator
Today let's talk another design pattern that is commonly used in game programming, Decorator pattern. This pattern, as the name stands, decorates/modifies an object, adding some more functionalities to it without changing the structure of the decorated object.
One of the example of the usage of this pattern in game would be in a shooting game where we want to have some additional effects when the bullet collides with the target (poison effect, stun, receive more damage, etc).
Let's see how to do that in Unity3D and C#.
We will make a shooting game simulation and then we modify the behaviour of the bullet once it collides with the victim.
First let's make an interface called IDamageable, which is an interface for the target/victim object. It will have receiveDamage() method for a single damage, and receiveDamageOverTime() method for a damage that is received every second, with the parameter consists of the damage and the duration.
Next, we will make the IDamager interface script, and that will be the interface for anything that can inflict damage to IDamageable.
So, make a function called inflictDamage() with the parameter consisting of the target that will be damaged, which is a IDamageable.
And then make a method that can modify the damage just in case there will be some cases where we need to do damage modification.
Now we will make the concrete damager object. In this case (remember, we are doing shooting simulation), is the bullet. Inside the bullet class, make a list of BulletModifier (which we will create it after this) objects. As it's name stands, it is an object that will modify/decorate the bullet with various effects.
We will make it as a list because we want the bullet to have multiple additional effects once it hits the target.
In the start method, call the RegisterBulletModifier method to register a modifier to the modifier list. We will make two types of modifiers, the BonusDamageModifier and the PoisonModifier, which we will create it after this.
Inside the inflictDamage method, we loop through all modifiers that is in the modifier list, and do the modification logic of every modifier.
Once we have done with the Bullet, now its time to make the BulletModifier. Make it abstract because we only want to have the instance of it's subclass.
In the constructor, initialize the modifiedDamager.
Next for the inflictDamage() method, before we go into the modification process, first we need to check if there is a damager that needs to be modified. If there is, then go ahead and modify. Otherwise, terminate the method.
Next, we make an abstract method modify(), which is where we put the modification logic for each subclass of BulletModifier.
Next, as I said before, we will make two kinds of modifier. The first one is the bonus damage modifier, and the second one is the poison modifier, which will give poison effect (deal extra damage per second for some duration after it hits the target).
Let's start with the bonus damage. Make a new script BonusDamageModifier and inherit it from BulletModifier.
Make an int variable bonusDamage, which is the amount of bonus damage that it provides, and initialize it in the constructor.
Then, inside the modify method's body, which is where the modification logic takes place, call the modifiedDamager's AddDamage() method and put the bonusDamage as the parameter. This way, we will be able add the modifiedDamager's damage.
Next, create the second modifier script called PoisonModifier. This one is kinda similar to bonus damage except it has a poison duration variable which will specify how long will the poison damage takes effect.
So in the modify, just call the victim's receiveDamageOverTime and pass the poison damage and duration as the parameter.
Finally, create the DamageableObject script, which is a concrete class of IDamageable, and declare a hit point variable in it for the damage simulation later on.
Next make the DamageOverTime coroutine that will deal damage for every second.
Then, make a Unity built-in method OnTriggerEnter method to detect the trigger from the bullet. If it detects the trigger, then call the bullet's inflictDamage() function which not only deals damage to the damageable object, but also executes modification processes inside it.
Once we finished doing the code, let's jump to the editor. Create two game objects, a cube and a sphere.
We will make the cube act as the damageable object and the sphere as the bullet. Make sure that both have the same x and z transform position, with the sphere having higher y position than the cube.
So, attach the DamageableObject script into the sphere, and put 1000 in the HP variable field. Put a rigidbody as well, and uncheck the "use gravity" bool, because we want the object to just stay in it's position instead of falling down due to gravity.
Next for the sphere object, assign the Bullet script and RigidBody component to the sphere. For this tutorial, let's assign the damage value in the Bullet script to 10. This time make sure that the use gravity remain checked, so that the bullet can fall down and hit the cube. That is why we have to make sure that both have x and z position, and with bigger y position for the bullet.
Then once we hit play, pay attention to the HP variable in the cube's DamageableObject, and compare how much damage did it took from the bullet with these 2 given conditions : 1. Both modifiers registered in the bullet's modifier list
2. Only one of them registered in the bullet's modifier list.
If you notice that in the first experiment when the bullet hit the cube, it will deal 10 + 40 = 50 damage and then followed by 5 poison damage per second. While in the second test, the bullet will only deal 10 damage and then followed by 5 poison damage per second, and that is because in the second experiment we didn't register any BonusDamageModifier object.
That is my tutorial on how to implement Decorator design pattern in game programming in Unity3D. Hopefully this tutorial helps all of you who read this. Thank you.