Reader small image

You're reading from  Game Development Patterns with Unity 2021 - Second Edition

Product typeBook
Published inJul 2021
Reading LevelBeginner
PublisherPackt
ISBN-139781800200814
Edition2nd Edition
Languages
Tools
Right arrow
Author (1)
David Baron
David Baron
author image
David Baron

David Baron is a game developer with over 15 years of experience in the industry. He has worked for some well-known AAA, mobile, and indie game studios in Montreal, Canada. His skill set includes programming, design, and 3D art. As a programmer, he has worked on various games for various platforms, including virtual reality, mobile, and consoles.
Read more about David Baron

Right arrow
Using the Decorator to Implement a Weapon System

In this chapter, we are going to build a customizable weapon system. Throughout the game, the player will be able to upgrade its bike's primary weapon by purchasing attachments that will augment specific properties, such as range and strength. The primary weapon is mounted on the front of the bike and has two expansion slots for attachments that the player can use to build various combinations. To build this system, we are going to use the Decorator pattern. It should not be a surprise because its name implies its use, as we will see further in the chapter.

The following topics will be covered in this chapter:

  • The basic principles behind the Decorator pattern
  • The implementation of a weapon system with attachments

Technical requirements

You will need to have a basic understanding of Unity and C#.

We will be using the following Unity engine and C# language concepts:

  • Constructors
  • ScriptableObjects

If you are unfamiliar with these concepts, please review Chapter 3, A Short Primer to Programming in Unity.

The code files for this chapter can be found on GitHub at https://github.com/PacktPublishing/Game-Development-Patterns-with-Unity-2021-Second-Edition/tree/main/Assets/Chapters/Chapter12.

Check out the following video to see the code in action: https://bit.ly/3r9rvJD.

We often use ScriptableObjects in the code examples in this book because we want to establish an authoring workflow for our designers to create new weapon attachments or configure existing ones without modifying a single line of code. It's good practice to make your systems, ingredients, and mechanics easy to configure for non-programmers.

Understanding the Decorator pattern

In short, the Decorator is a pattern that permits the addition of new functionalities to an existing object without altering it. And this is made possible by creating a decorator class that wraps the original class. And with this mechanism, we easily attach but also detach new behaviors to an object.

Let's review the following diagram to visualize the Decorator's structure before diving deeply into the subject matter:

Figure 12.1 – UML diagram of the Decorator pattern

The IWeapon interface establishes an implementation contract that will maintain a consistent method signature between the decorated object and its decorators. WeaponDecorator wraps the target object, and the concrete decorator classes (WithGrip and WithStabilizer) decorate it by enhancing or overriding its behaviors.

The method signatures and the overall structure of the decorated object are not modified during the process, just its behaviors or property...

Benefits and drawbacks of the Decorator pattern

The following are some of the benefits of the Decorator pattern:

  • Alternative to subclassing: Inheritance is a static process. Unlike the Decorator pattern, it doesn't permit extending an existing object's behavior at runtime. You can only replace an instance with another with the same parent class that has the desired behavior. Therefore, the Decorator pattern is a more dynamic alternative to subclassing and overcomes the limits of inheritance.
  • Runtime dynamics: The Decorator pattern permits us to add functionality to an object at runtime by attaching decorators to it. But this also means that the reverse is possible, and you can restore an object back to its original form by removing its decorators. 

The following are some of the potential drawbacks of the Decorator pattern:

  • Relationship complexity: Keeping track of the chain of initialization and the relationships between decorators can become very complicated if there...

When to use the Decorator pattern

In this chapter, we are implementing a weapon system with attachments. We have a couple of specifications to consider, such as the following:

  • We need to be able to attach multiple attachments to a weapon.
  • We need to be able to add and remove them at runtime.

The Decorator pattern offers us a way to fulfill these two core requirements. Therefore, it's a pattern to consider when implementing a system in which we need to support the ability to add and remove behaviors to an individual object in a dynamic manner. 

For instance, if we are assigned to work on a CCG (collectible card game), we might have to implement a mechanism in which the player can augment a base card's powers with artifact cards stacked on top of each other. Another use case is imagining having to implement a wardrobe system in which players can decorate their armor with accessories to buff specific stats.

In both cases, using the Decorator pattern could be a good...

Designing a weapon system

By default, every bike model in our game comes equipped with a front-mounted gun that fires laser beams. The player can use this weapon to destroy obstacles that might be in the way, and very skilled players can shoot at flying drones while doing wheelies. The player can also purchase various attachments that boost the basic stats of the bike's weapon, as illustrated in the following diagram:

Figure 12.2 – Diagram of the weapon attachment system

Let's review a shortlist of potential attachments that the player could buy:

  • Injector: A plasma injector that amplifies the weapon's damage capacity.
  • Stabilizer: An attachment that reduces vibrations caused when the bike hits its top speed. It stabilizes the weapon's shooting mechanism and expands its range.
  • Cooler: A water-cooling system that attaches itself to the weapon's firing mechanism. It enhances the rate of fire and reduces the cool-down duration.

These examples are high...

Implementing a weapon system

In this section, we are going to implement the core classes of our weapon system. However, please note that we are not going to write the weapon's core behaviors for reasons of brevity. We want to keep the focus on understanding how the Decorator pattern works. But if you wish to review a more advanced version of this code example, check out the FPP folder in this book's GitHub repo. The link can be found in the Technical requirements section.

Implementing the weapon system

Many steps need to be performed as we have a lot of code to review together:

  1. To start, we are going to implement the BikeWeapon class. Because it's quite long, we will split it up into three segments:
using UnityEngine;
using System.Collections;

namespace Chapter.Decorator
{
public class BikeWeapon : MonoBehaviour
{
public WeaponConfig weaponConfig;
public WeaponAttachment mainAttachment;
public WeaponAttachment secondaryAttachment;

private bool _isFiring;
private IWeapon _weapon;
private bool _isDecorated;

void Start()
{
_weapon = new Weapon(weaponConfig);
}

The first segment is the initialization code. Note that we are setting the weapon's configuration at the Start().

For the second segment of the class, it is just GUI labels that will help us with debugging:

void OnGUI()
{
GUI.color = Color.green;

GUI.Label (
new Rect (5, 50, 150, 100),
"...

Testing the weapon system

If you wish to test the code we just reviewed in your own instance of Unity, you need to follow these steps:

  1. Copy all the classes we just reviewed in your Unity project.
  2. Create an empty Unity scene.
  1. Add a new GameObject to the scene.
  2. Attach the following client script to the new GameObject:
using UnityEngine;

namespace Chapter.Decorator
{
public class ClientDecorator : MonoBehaviour
{
private BikeWeapon _bikeWeapon;
private bool _isWeaponDecorated;

void Start() {
_bikeWeapon =
(BikeWeapon)
FindObjectOfType(typeof(BikeWeapon));
}

void OnGUI()
{
if (!_isWeaponDecorated)
if (GUILayout.Button("Decorate Weapon")) {
_bikeWeapon.Decorate();
_isWeaponDecorated = !_isWeaponDecorated;
}

if (_isWeaponDecorated)
if (GUILayout.Button("Reset Weapon"))...

Reviewing the weapon system

Our Decorator implementation is unorthodox because we are mixing native C# language features such as constructors while trying to utilize the best parts of the Unity API. By converting decorator classes into configurable ScriptableObject assets, we gain the ability to make our weapon attachments authorable and configurable by non-programmers. And under the hood, our attachment system is constructed on the foundation of a solid design pattern.

Therefore, we endeavored to strike a balance between usability and maintainability. However, as we are going to see in the next section, there are always alternative approaches.

Design patterns are guidelines, not commandments. Therefore, it's OK to experiment with patterns and test out different ways of implementing them. The code example in this chapter is experimental because it's not a traditional implementation of the Decorator pattern. But we encourage you, as the reader, to continue experimenting with...

Reviewing alternative solutions

In the context of the use case presented in this chapter, we could have implemented the weapon system without the Decorator pattern and with ScriptableObjects only. We could have iterated through a list of acquired weapon attachments and apply each of their properties to those of a Weapon class. We would lose the ability of chaining decorators, but our code will be more straightforward.

The core benefit of using the Decorator pattern in our case was that it gave us a structured and repeatable approach to implementing our system. Still, in consequence, we added additional complexity to our code base.

Summary

In this chapter, we implemented a weapon attachment system that's authorable and configurable. Non-programmers will be able to create and adjust new attachments without writing a single line of code. Therefore, we can focus on building systems while designers work on balancing them. The Decorator pattern has proven itself to be a handy pattern for game development, and so it's a pattern to keep in our programmer's toolbox.

In the next chapter, we will explore the Spatial Partition pattern—a subject matter that's quite important to understand when building games with large maps.

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Game Development Patterns with Unity 2021 - Second Edition
Published in: Jul 2021Publisher: PacktISBN-13: 9781800200814
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
David Baron

David Baron is a game developer with over 15 years of experience in the industry. He has worked for some well-known AAA, mobile, and indie game studios in Montreal, Canada. His skill set includes programming, design, and 3D art. As a programmer, he has worked on various games for various platforms, including virtual reality, mobile, and consoles.
Read more about David Baron