Saturday 11 May 2013

2Abstract || !2Abstract

    In the last entry, I talked about the multitude of actions that a game like this needs, and how it's difficult deciding what to cut and what to keep.  I then mentioned the term 'abstraction' and how it can help expand the number of actions available to the player without significantly increasing development time.  Now I'll delve into abstraction and what it means for game development.
  
    Abstraction is a somewhat nebulous term, and can be used to mean a variety of approaches within game design and development.  In this case, I use it to mean a kind of separation between objects and their interactions, and a move away from specific actions to a more generic form.  For example, one action can be used on many different items, without the need to code many different responses.


    There are two main ways in which the game is achieving this.  First up, the game makes heavy use of Object-Oriented Programming practices (OOP for short - if you're familiar with OOP you might want to skip ahead to the second point).  One of the strengths of OOP is something called polymorphism - this is when objects inherit features and abilities from a parent object, but also change and add to the fabric of this object.  For example, in the game, there are a series of classes, with each one inheriting from another, so that they add new functionality and retain the parent functionality.  As a simplified example for this blog, the order of a wooden chest item is:
  
    WorldObject -> Scenery -> Containers
  
    The WorldObject is the base class for the vast majority of 'in-world' items, that is items that the player will see and interact with.  This class provides basic functionality such as positioning, rotation, health, and simple interactivity.
    Below WorldObject we have Scenery.  Anything in this class gets all the goodies from WorldObject, but adds new goodies, such as a complex damage system with debris and states of being, and the ability to provide cover for creatures, and some other fun stuff.  Using this setup, anything that is a generally static object acting as décor/furniture/clutter would be a Scenery object.  Finally we have the Containers class, which extends Scenery even further to add in features such as locks, booby traps, and contents.  This allows us to make a chest that acts like a WorldObject (has health, is interactive, etc) and a Scenery item (damage states, debris systems, etc), and finally a Container (locks, traps, contents).  Without OOP it would be a horrible slog having to fit all that functionality into one large amount of code, and have it be suitable for a variety of purposes.  Ugh.
  
    The second way in which we separate interaction from the items is to make the interactions themselves be very abstract in nature.  In the game, interactions are known as 'actions' and each action can be used on anything that inherits from the base WorldObject class.  Each action then defines a set of rules which filter out unwanted interactions - for example, the action to open a chest has rules that do not allow the opening of a stone wall, or opening a table.  In fact, these restrictions are most often gained simply by disallowing the opening of anything that isn't a Container class instance.
    Using this abstraction of actions we can break down every interaction in the game to a small number of number of generic interactions.  For example, the list of every available action might be something like:
  
    Open, close, cast spell, melee attack, fire weapon, use item, sort inventory, trade, open menu, and rest.
  
    This simple list of actions covers the majority of everything in the game that players can do.  The beauty of this system is that for each possible interaction, the results differ only in the animations or visual effects.  Most projectile-based weapons will operate in a very similar fashion, so it makes no sense to have two actions - one to fire a bow and one to fire a pistol.  The only real difference between the two attacks are the animation that plays and the projectile produced at the end of it.  Instead we have one action for every projectile-firing weapon, and the action itself tells the game which animations to play, and what projectile to produce.  With this method all of the different projectile-firing weapons use just one action, which in essence has distilled the process of firing the weapon down to a handful of variables in the weapon itself.
  
    But why stop there?  In the game, I have distilled and abstracted all player interactions into three simple systems - spell-casting, melee combat, and item usage.  That's crazy talk, you might say, but it works.  It works because when you consider what a projectile weapon and a projectile spell do, you realize they are performing the same action but with different animations/projectiles.  So I was able to integrate the weapons system into the spell-casting system and save having to write a whole new slew of code just to handle projectile weapons.
    Non-projectile weapons are only used for melee combat, and the design of the melee combat in the game is way beyond simple attacks, so I had to make them a separate system.  However, if the melee combat was a simple attacking affair, then there's no reason melee weapon use couldn't have been integrated into the spell-casting code too.  After all, it's just a very-short range weapon being used.
    The final of three system is item usage.  This does require its own system because there is such a wide variety of items, performing a wide variety of functions, that to try and integrate it into an existing system would cause more work than it would save.

    As you can see, the use of abstraction allows me to create complex systems of interaction without the need for overly complex code.  This idea of abstraction and generalizing your code helps in more than just player interactions.  There are countless ways in which you can make parts of your game abstract and generic, making it easier to implement new items/features without the need for new code.

    In the next entry I will talk a little more on abstraction, and how it is used in the game to create unlimited effectors that change the characters in the game in a myriad of ways, without the need for a labyrinth of code.

No comments:

Post a Comment