Script Compatibility Talk: Object Composition

As a scripter, I write scripts with the goal of being plugged into any project and suddenly a developer has just solved a problem.

In this article I discuss a certain technique that I personally have not seen being used very often in RPG Maker scripts, though it is something I feel is extremely important for compatibility between scripts. Or Maybe I just don’t look at enough scripts.

Object Composition

In software engineering. there is a concept called Object Composition. You can read about it on Wikipedia but basically you have an object that contains another object. Wikipedia uses the analogy of a car being composed of an engine, wheels, and all of the other parts that you may expect from a fantasy automobile, such as laser turret or warp drives.

Where would I use it?

In RPG Maker, you can see examples of object composition. A battler sprite holds a reference to the battler object that it represents.

class Sprite_Battler < Sprite_Base
  def initialize(viewport, battler = nil)
    super(viewport)
    @battler = battler # <---
    @battler_visible = false
    # ...

Similarly, a character sprite holds a reference to the character object that it represents.

class Sprite_Character < Sprite_Base
  #--------------------------------------------------------------------------
  # * Object Initialization
  # character : Game_Character
  #--------------------------------------------------------------------------
  def initialize(viewport, character = nil)
    super(viewport)
    @character = character # <---
    @balloon_duration = 0

Rather than creating a single object that handles both the logic (moving around, keeping track of HP and skills) as well as the presentation (updating animations, drawing the picture), we logically separate the two sets of responsibilities into their own classes, and use object composition to link them together. There is also something called MVC which appears to be useful but that’s a different topic.

Revisiting Alias

Aliasing is a common technique that is used to increase compatibility between scripts. It is usually quite effective as well, since you are simply adding extra logic on top of existing code. However, there are times when aliasing is not the best solution.

For example, let’s say you are writing a battle system that provides a number of mechanics such as battler positions on the battlefield as well as limiting your skills to the equips that you are currently holding. If you don’t know what those mean, that’s ok. Basically, you are going to have to add new logic to classes like Game_Battler and Game_Unit. Adding new attributes and changing the way skills are determined can be done using aliases, while allowing you to preserve existing logic that may have been added by other scripts.

Everyone shares everything

This is a consequence behind aliasing methods. When you add more logic to a method, everyone else now has to cope with it, even if you didn’t intend it yourself.

This starts to become a problem when you have two or more scripts want to use the same method, but they will conflict with each other because the logic they’re adding is not what the other scripts want.

Going with the example of battle skills, the default system makes it so that you can use basically any skill you want as long as the requirements are met. If I added logic to only limit it to skills that are associated with certain equips, this change may be incompatible with the default battle system (or even outside of battle, such as in the menu screen).

There are many ways to come up with a situation where two scripts aliasing the same method causes problems, and for sufficiently complex scripts such as battle systems, you will very likely have many conflicting methods. Aliasing in these situations is effective, but what could we do?

Introducing composition

Imagine you have your basic Game_Actor object, and you want to use it in a custom battle system. You can alias all of the methods that you need like before, or you can consider “wrapping” or “decorating” it with a custom actor object that is specifically made for that battle system.

It might look something like this

class Custom_Battle_Actor

  attr_reader :x
  attr_reader :y
  attr_reader :atb
  
  #----------------------------------------------------
  # Pass in an actor that you want to decorate
  #----------------------------------------------------
  def initialize(game_actor)
    @actor = game_actor
    @atb = 0
    @x = 0   
    @y = 0
  end
  
  #-----------------------------------------------------
  # Custom skill filters. Calls the actor's skills method
  # and then filters on that
  #-----------------------------------------------------
  def skills
    @actor.skills.select do |skill|
      # some filter criteria for your skills
    end
  end

  #-----------------------------------------------------
  # All of the other methods you need...
  #-----------------------------------------------------
end

Notice a few things with this design:

  • It holds a reference to a particular actor. It’s basically that actor, except with new properties
  • It calls on the actor’s own methods. If there are scripts that add custom logic to the actor’s methods, they would be compatible with your script

This custom battle actor class would be used specifically for my custom battle system and nowhere else. It doesn’t alias existing methods, so I don’t need to worry about other scripts having a problem with new logic I’m adding. It is effectively a stand-alone battler class that does not need to know how the underlying actor is implemented.

Another thing that you should notice is that it does not inherit from any of the battler classes, despite being a battle object. Inheritance may be an appropriate choice depending on what you need, and may make things easier for you because it already comes with all of the other interface methods that other classes expect. I would recommend experimenting with different approaches to understand the advantages and disadvantages of each approach.

Is composition any good?

I’ve presented a scenario where I’ve found a lot of compatibility issues. This could easily apply to mini-games as well, where you want to use existing actors or parties, but new logic would only be used specifically for those games.

The answer to whether composition would solve the problem or not would require you to actually implement multiple battle systems in this way to show that they are indeed compatible, but the theory suggests it would work out. Perhaps in the future we will see more  large, complex, but compatible systems being built for RPG Maker.

Spread the Word

If you liked the post and feel that others could benefit from it, consider tweeting it or sharing it on whichever social media channels that you use. You can also follow@HimeWorks on Twitter or like my Facebook page to get the latest updates or suggest topics that you would like to read about.

You may also like...

1 Response

  1. RogueDeus says:

    This is the kind of thing that the community really needs more of. It would have made my life so much easier to have this easily explained back when I was starting to play with RM. 😉

Leave a Reply to RogueDeus Cancel reply

Your email address will not be published. Required fields are marked *