In a roguelike I'm developing in Ruby, I started with a traditional object hierarchy where all game logic relevant to creatures were contained in the
Creature class. Movement logic, for example:
Creature.instance_methods(false) # => [ :move ]
In my design, the map and creatures are decoupled: the map has no idea what's on a tile and the creature has no idea which kind of tile it is on. What brings them together are coordinates.
Movement logic requires information about where on the map the creature is, so the creature would need access to the map. The map belonged to the game, so I just added a game attribute to every creature as a quick solution. I didn't really want to pass a map instance every time the creature needed to move around because the map the creature is on isn't likely to change.
In the end, I removed the game logic from the
Creature class, and separated them into modules:
Game::Logic::Movement.singleton_methods(false) # => [ :move ]
The way I understand it, these logic modules integrate multiple game parts which depend on each other in a way not easily expressed hierarchically. The methods are just algorithms that operate on the given information. For example, the implementation of the
def self.move(creature, map, dx, dy) coordinates = Map::Coordinates[dx + creature.x, dy + creature.y] tile = map[coordinates] creature.coordinates = coordinates if tile and tile.passable? # Teleport! end
These methods are called directly from the game loop:
def handle(input) case input when :up then Logic::Movement.move character, map, 0, 1 # ... end end
This is how the game is currently organized. Movement is the only game logic I've added as of now, so I think it would be wise if I revised the architecture while it is still small. So, what do you think of it? What are the problems, and what can I do to improve it?
I'm also interested in how it compares to component-based game architecture. I've read a lot about it during my research, and it seems to be the most maintainable and reusable way to write game code. I think it would fit naturally in the Ruby world, due to its dynamic nature and message-based method invocation. The thing is, I really never understood how different components interact with each other, and I'm looking for something I can make sense of.