Turbomode
Game development engine based on the entity-component-system pattern.
Over the years I took part in hackathons and game jam competitions. In these sort of competitions participants have to make games from scratch, obeying a series of limitations - time and theme being the most common. For these competitions I’ve made games in 72 hours, 4.8 hours or even a blistering 48 minutes. I’ve also made games that had other sort of thematic limitations, like for ScreenHack 2004 where you had to fit the game’s code in a standard DOS prompt size of 80 by 25 characters.
Working with these strict limitations is a great way to unlock your creativity, forcing you to get something out of the door and find solutions to whatever problems they impose.
My main language of choice at the time to participate in these events was C but as I learned more about ruby I got interested in its potential for rapid development and prototyping of games. The turbomode gem was born out of my exploration. I named it that way as a reference to those old Famicom-clones that had turbo buttons on their controllers that allowed rapid-fire and quicker movement.
It is a game development engine based on the entity-component-system pattern. In these type of engines, game objects (addressed as entities) are nothing more than buckets of data, with different components representing slices of entity state that are then processed by systems. It favours composition over inheritance and makes it much easier to prototype games, since changing an entity’s behaviour is as easy as adding or subtracting components.
Turbomode implements base classes for these entities and systems, managing their lifecycle and game loop. It also offers implementations for common systems in games like an animation or a collision system. It sits on top of gosu - a ruby gem used to handle graphics, sound and input - abstracting some of its functionality away.
class PlayerComponent < Component
attr_accessor :score
attr_accessor :lives
end
def get_player_entity
entity = Entity.new(PositionComponent.new, InputComponent.new, SpriteComponent.new,
CollisionComponent.new, PlayerComponent.new, StateComponent.new,
AnimationComponent.new, DeathComponent.new)
entity.player.score = 0
entity.player.lives = 3
player_sprite = @gosu.get_image('./player.bmp')
entity.animation.frames = {
alive: [{sprite: player_sprite }],
dead: [
{ sprite: @gosu.get_image('./player_dead1.bmp'), duration: 200 },
{ sprite: @gosu.get_image('./player_dead2.bmp'), duration: 200 } ]
}
entity.state.state = :alive
entity.position.x = @gosu.screen_width / 2 - player_sprite.width / 2
entity.position.y = @gosu.screen_height - 32
entity.input.keys_action = { kbleft: { x: -1 }, kbright: { x: 1 }, kbup: { message: :fire } }
return entity
end
Systems work by selecting entities with specific components and acting on them during game logic and/or when the screen is drawn.
class AnimationSystem < System
def update entity_manager, messages
entity_manager.select_with(:animation, :sprite).each do |e|
state = e.state ? e.state.state : :other
direction = e.direction ? e.direction.direction : :other
e.animation.current_frame_position = 0 unless e.animation.current_frame(state, direction)
if @wrapper.milliseconds > e.animation.last_time_frame_update + e.animation.current_frame(state, direction)[:duration] then
e.animation.current_frame_position += 1
e.animation.last_time_frame_update = @wrapper.milliseconds
e.animation.current_frame_position = 0 unless e.animation.current_frame(state, direction)
e.sprite.sprite = e.animation.current_frame(state, direction)[:sprite]
end
end
end
end