Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Creating games with entities and components

Creating games with entities and components

Ruby is an object-oriented language, and that is the paradigm we mainly use to write software.

Object orientation is not always the best solution for some situations. In real-time simulations, such as real-time games, techniques such as inheritance, modules, traits, etc break down.

This talk explains the entity-component-system architecture, a common pattern in modern game architecture, which explicitly breaks away from object orientation to achieve large gains in flexibility and speed.

Denis Defreyne

December 04, 2014
Tweet

More Decks by Denis Defreyne

Other Decks in Technology

Transcript

  1. 3

  2. 4

  3. 5

  4. 6

  5. 9

  6. If all you have is the data,
 you know what

    it’s about.
 If all you have is the behavior,
 you’re still in the dark. 11
  7. class Spaceship def update # Move @position_x += @velocity_x @position_y

    += @velocity_y # Calculate acceleration @acceleration = 0.0 if Keyboard.key_down?('w') @acceleration = 10.0 # TODO: Play accelerate sound elsif Keyboard.key_down?('s') @acceleration = -3.0 end # … end end 23
  8. class Spaceship def update # Move @position_x += @velocity_x @position_y

    += @velocity_y # Calculate acceleration @acceleration = 0.0 if Keyboard.key_down?('w') @acceleration = 10.0 # TODO: Play accelerate sound elsif Keyboard.key_down?('s') @acceleration = -3.0 end # Rotate if Keyboard.key_down?('a') @rotation -= 5 elsif Keyboard.key_down?('d') @rotation += 5 end # TODO: Add rotational velocity # … end end 24
  9. class Spaceship def update # Move @position_x += @velocity_x @position_y

    += @velocity_y # Calculate acceleration @acceleration = 0.0 if Keyboard.key_down?('w') @acceleration = 10.0 # TODO: Play accelerate sound elsif Keyboard.key_down?('s') @acceleration = -3.0 end # Rotate if Keyboard.key_down?('a') @rotation -= 5 elsif Keyboard.key_down?('d') @rotation += 5 end # TODO: Add rotational velocity # Accelerate @velocity_x += Math.cos(@rotation) * @acceleration @velocity_y += Math.sin(@rotation) * @acceleration # … end end 25
  10. class Spaceship def update # Move @position_x += @velocity_x @position_y

    += @velocity_y # Calculate acceleration @acceleration = 0.0 if Keyboard.key_down?('w') @acceleration = 10.0 # TODO: Play accelerate sound elsif Keyboard.key_down?('s') @acceleration = -3.0 end # Rotate if Keyboard.key_down?('a') @rotation -= 5 elsif Keyboard.key_down?('d') @rotation += 5 end # TODO: Add rotational velocity # Accelerate @velocity_x += Math.cos(@rotation) * @acceleration @velocity_y += Math.sin(@rotation) * @acceleration # Cap speed @velocity_x = [@velocity_x, @max_velocity_x].min @velocity_y = [@velocity_y, @max_velocity_y].min # … end end 26
  11. class Spaceship def update # Move @position_x += @velocity_x @position_y

    += @velocity_y # Calculate acceleration @acceleration = 0.0 if Keyboard.key_down?('w') @acceleration = 10.0 # TODO: Play accelerate sound elsif Keyboard.key_down?('s') @acceleration = -3.0 end # Rotate if Keyboard.key_down?('a') @rotation -= 5 elsif Keyboard.key_down?('d') @rotation += 5 end # TODO: Add rotational velocity # Accelerate @velocity_x += Math.cos(@rotation) * @acceleration @velocity_y += Math.sin(@rotation) * @acceleration # Cap speed @velocity_x = [@velocity_x, @max_velocity_x].min @velocity_y = [@velocity_y, @max_velocity_y].min # Render Graphics.translate(@position_x, @position_y) do Graphics.rotate(@rotation) do @sprite.draw end end end end 27
  12. class Spaceship def update # Move @position_x += @velocity_x @position_y

    += @velocity_y # Calculate acceleration @acceleration = 0.0 if Keyboard.key_down?('w') @acceleration = 10.0 # TODO: Play accelerate sound elsif Keyboard.key_down?('s') @acceleration = -3.0 end # Rotate if Keyboard.key_down?('a') @rotation -= 5 elsif Keyboard.key_down?('d') @rotation += 5 end # TODO: Add rotational velocity # Accelerate @velocity_x += Math.cos(@rotation) * @acceleration @velocity_y += Math.sin(@rotation) * @acceleration # Cap speed @velocity_x = [@velocity_x, @max_velocity_x].min @velocity_y = [@velocity_y, @max_velocity_y].min # Render Graphics.translate(@position_x, @position_y) do Graphics.rotate(@rotation) do if @acceleration > 0.0 @flame_animation.step @flame_sprite = @flame_animation.sprite @flame_sprite.draw end @sprite.draw end end end end 28
  13. 31

  14. class Spaceship attr_reader :position_x, :position_y attr_reader :velocity_x, :velocity_y attr_reader :acceleration_x,

    :acceleration_y attr_reader :rotation attr_reader :shield_cur, :shield_max, :shield_rate attr_reader :armor_cur, :armor_max def initialize(params = {}) … end end 35
  15. A system is essentially a procedure* that is called thirty

    times per second. 39 * A function with only side effects
  16. spaceship = Spaceship.new(
 position_x: 200,
 position_y: 150,
 velocity_x: 10,
 velocity_y:

    -5,
 armor_cur: 100,
 armor_max: 100)
 movement_system = MovementSystem.new movement_system.update(spaceship)
 p [spaceship.position_x, spaceship.position_y] # => [210, 145] 41
  17. class Spaceship attr_reader :position_x, :position_y attr_reader :velocity_x, :velocity_y attr_reader :acceleration_x,

    :acceleration_y attr_reader :rotation attr_reader :shield_cur, :shield_max, :shield_rate attr_reader :armor_cur, :armor_max def initialize(params = {}) … end end 46
  18. class Spaceship attr_reader :position attr_reader :velocity attr_reader :acceleration attr_reader :rotation

    attr_reader :shield attr_reader :armor def initialize(params = {}) … end end 47
  19. Position = Struct.new(:x, :y) Velocity = Struct.new(:x, :y) Acceleration =

    Struct.new(:x, :y) Rotation = Struct.new(:rad) Shield = Struct.new(:cur, :max, :rate) Armor = Struct.new(:cur, :max) 48
  20. class Spaceship attr_reader :position attr_reader :velocity attr_reader :acceleration attr_reader :rotation

    attr_reader :shield attr_reader :armor def initialize(params = {}) … end end 51
  21. # Become invisible spaceship.remove(Sprite) # Become mortal angel.add(Health.new(100)) # Become

    a ghost asteroid.remove(CollisionShape) # Mind control enemy enemy.remove(AISteering) enemy.add(PlayerSteering) 58
  22. 61

  23. 62

  24. 65 position velocity 14 used bytes / 32 total bytes

    = 44% efficiency (for movement system) rotation armor shield
  25. 66

  26. 67

  27. positions[47] = Position.new(400, 200) velocities[47] = Velocity.new(0, 0) shields[47] =

    Shield.new(cur: 100, max: 100, rate: 2) armors[47] = Armor.new(cur: 50, max: 50) 69
  28. Smart use of the CPU cache can lead to a

    50x speedup! 70 * Source: Game Programming Patterns by Robert Nystrom
  29. { "position": [400, 200], "velocity": [0, 0], "shield": {"cur": 100,

    "max": 100, "rate": 2}, "armor": {"cur": 50, "max": 50} } 74
  30. 76

  31. 80 This talk would not have been the same without

    some great assets that I could use, either for free or for a well-deserved donation. The fonts in this presentation are Clear Sans by Intel (01.org/clear-sans) and Ubuntu Mono by Canonical Ltd (font.ubuntu.com). Most of the sprites are by Kenney Vleugels (kenney.nl) and are part of the Kenney Donation Pack (kenney.itch.io/kenney-donation). The planet sprite in the 2D space shooter example is by Justin Nichol. Assets in the initial screenshot are by Sven Ahlgrimm.