baku 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 28ac783aa88b25f6c40da785a9455b8ceffc57ce
4
- data.tar.gz: e1352417ee1b095d41da14d033fa84bdadb57097
3
+ metadata.gz: 1653036472420b349cb3e013e0d55a589ebe29cb
4
+ data.tar.gz: 4ce15a89513b81e4803b800a3083b7e5d51ce45a
5
5
  SHA512:
6
- metadata.gz: 5c68d176ad7eda25d66ec1674c3cf5a739c7eea941a3edcbe0133c8aa14242af7fd7277e95e20198e8fc9fd6b06bbead8e04ff5210ac270bb40a0a7472e3255f
7
- data.tar.gz: 0fb615ce7c1b69ed0aad6147c96e9f02b5da16e62e8daf6c77dcd37194c0316ac5c75aef5c08135c3a06279f4249596500bcff3bca0b93d291af26d68249a234
6
+ metadata.gz: cc818103da055af5358e1dfeab535945c31be1b847f931b54febebbe407e6ab3a3427660de8abeb5063d24ed2bf01ad958ce32af3b89bed1338061dd71ef6b35
7
+ data.tar.gz: 497c1f10325f892d97dbe9a48ef30052b4d03edc26e6cf20453e9ca52b32f7cee10d3a7bf7c5215fb85f4f74f1f4d2cf86eafda98b346bd926801dcb1445b487
data/.gitignore CHANGED
@@ -10,3 +10,6 @@
10
10
 
11
11
  # rspec failure tracking
12
12
  .rspec_status
13
+ .byebug_history
14
+ .ruby-version
15
+ .ruby-gemset
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # 0.2.0
2
+
3
+ * Add ability to remove components from an entity.
4
+ * Extract component mask logic into its own class.
5
+ * Remove a few instances of unnecessary coupling.
6
+ * Write remaining tests.
7
+
8
+ # 0.1.0
9
+
10
+ * Initial release!
data/README.md CHANGED
@@ -58,7 +58,6 @@ class MovementSystem < Baku::System
58
58
  def process_entity(entity, transform, velocity)
59
59
  transform.x += velocity.x
60
60
  transform.y += velocity.y
61
- transform.z += velocity.z
62
61
  end
63
62
  end
64
63
  ```
data/baku.gemspec CHANGED
@@ -24,4 +24,5 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency "bundler", "~> 1.15"
25
25
  spec.add_development_dependency "rake", "~> 10.0"
26
26
  spec.add_development_dependency "rspec", "~> 3.0"
27
+ spec.add_development_dependency "pry-byebug", "~> 3.5.0"
27
28
  end
@@ -1,9 +1,7 @@
1
1
  module Baku
2
2
  class Component
3
- attr_reader :id
4
-
5
3
  def initialize
6
- @id = SecureRandom.uuid
4
+
7
5
  end
8
6
  end
9
7
  end
@@ -0,0 +1,51 @@
1
+ module Baku
2
+ class ComponentMask
3
+ attr_reader :value
4
+
5
+ @@component_set = Set.new
6
+
7
+ class << self
8
+ def record_components(components)
9
+ components.each do |component|
10
+ @@component_set << component
11
+ end
12
+ end
13
+
14
+ def from_components(components)
15
+ record_components(components)
16
+
17
+ mask_value = 0
18
+
19
+ @@component_set.each_with_index do |component, index|
20
+ mask_value |= (1 << index) if components.include?(component)
21
+ end
22
+
23
+ ComponentMask.new(mask_value)
24
+ end
25
+ end
26
+
27
+ def initialize(value)
28
+ @value = value
29
+ end
30
+
31
+ def add_component(component_class)
32
+
33
+ end
34
+
35
+ def matches?(other_mask)
36
+ @value & other_mask.value == @value
37
+ end
38
+
39
+ def ==(other)
40
+ @value == other.value
41
+ end
42
+
43
+ def eql?(other)
44
+ self == other
45
+ end
46
+
47
+ def hash
48
+ value
49
+ end
50
+ end
51
+ end
data/lib/baku/entity.rb CHANGED
@@ -1,12 +1,11 @@
1
1
  module Baku
2
2
  class Entity
3
- attr_reader :id, :components, :tags
3
+ include Baku::EventDispatcher
4
4
 
5
- def initialize(world, tags = [])
6
- @world = world
5
+ attr_reader :components, :tags
6
+
7
+ def initialize(tags = [])
7
8
  @tags = tags
8
-
9
- @id = SecureRandom.uuid
10
9
  @components = {}
11
10
  end
12
11
 
@@ -16,9 +15,11 @@ module Baku
16
15
  new("Entity already has component: #{component.class}")
17
16
  end
18
17
 
19
- # TODO: this is some pretty ugly coupling, figure out if there's a cleaner
20
- # way to do this. Callbacks or something.
21
- @world.entity_manager.entity_add_component(self, component)
18
+ components[component.class] = component
19
+
20
+ update_component_mask
21
+
22
+ dispatch_event(:component_added, self, component)
22
23
  end
23
24
 
24
25
  def remove_component(component_class)
@@ -27,13 +28,25 @@ module Baku
27
28
  new("Entity does not have component: #{component_class}")
28
29
  end
29
30
 
30
- # TODO: this is some pretty ugly coupling, figure out if there's a cleaner
31
- # way to do this. Callbacks or something.
32
- @world.entity_manager.entity_remove_component(self, get_component(component_class))
31
+ @components.delete(component_class)
32
+
33
+ update_component_mask
34
+
35
+ dispatch_event(:component_removed, self, @components[component_class])
33
36
  end
34
37
 
35
38
  def get_component(component_class)
36
39
  @components[component_class]
37
40
  end
41
+
42
+ def component_mask
43
+ @component_mask ||= ComponentMask.from_components(@components)
44
+ end
45
+
46
+ private
47
+
48
+ def update_component_mask
49
+ @component_mask = ComponentMask.from_components(@components)
50
+ end
38
51
  end
39
52
  end
@@ -1,54 +1,51 @@
1
1
  module Baku
2
+ # The EntityManager stores entities in such a way that they can be retrieved
3
+ # efficiently by either ComponentMask or tag. Before storing any entities, the
4
+ # ComponentMasks that we wish to match on should be registered. This step is
5
+ # performed transparently by the World whenever a System is added.
2
6
  class EntityManager
3
7
  def initialize
4
- @entities_by_system_mask = {}
8
+ @entities_by_component_mask = {}
5
9
  @entities_by_tag = {}
6
-
7
- @component_set = Set.new
8
- @system_mask_cache = {}
9
10
  end
10
11
 
11
- def register_entity(entity)
12
+ def register_component_mask(component_mask)
13
+ @entities_by_component_mask[component_mask] = []
14
+ end
15
+
16
+ def add_entity(entity)
17
+ add_entity_to_matching_component_lists(entity)
18
+
19
+ entity.add_event_listener(:component_added,
20
+ method(:on_entity_component_added))
21
+ entity.add_event_listener(:component_removed,
22
+ method(:on_entity_component_removed))
23
+
12
24
  entity.tags.each do |tag|
13
25
  @entities_by_tag[tag] ||= []
14
26
  @entities_by_tag[tag] << entity
15
27
  end
16
28
  end
17
29
 
18
- def register_system(system)
19
- system.components.each do |component|
20
- @component_set << component
30
+ def remove_entity(entity)
31
+ entity.tags.each do |tag|
32
+ @entities_by_tag[tag].delete(entity)
21
33
  end
22
-
23
- system_component_mask =
24
- get_component_mask(system.components)
25
-
26
- @system_mask_cache[system] = system_component_mask
27
34
 
28
- @entities_by_system_mask[system_component_mask] = []
29
- end
30
-
31
- # TODO: there may be a more efficent way to do this, like by comparing
32
- # the diff of the mask before and after, but this works for now.
33
- def entity_add_component(entity, component)
34
- old_mask = get_component_mask(entity.components)
35
- entity.components[component.class] = component
36
- new_mask = get_component_mask(entity.components)
37
-
38
- update_system_membership(entity, old_mask, new_mask)
39
- end
35
+ entity.remove_event_listener(:component_added,
36
+ method(:on_entity_component_added))
37
+ entity.remove_event_listener(:component_removed,
38
+ method(:on_entity_component_removed))
40
39
 
41
- def entity_remove_component(entity, component)
42
- old_mask = get_component_mask(entity.components)
43
- entity.components.delete(component.class)
44
- new_mask = get_component_mask(entity.components)
45
-
46
- update_system_membership(entity, old_mask, new_mask)
40
+ @entities_by_component_mask.each do |component_mask, entities|
41
+ if component_mask.matches?(entity.component_mask)
42
+ entities.delete(entity)
43
+ end
44
+ end
47
45
  end
48
46
 
49
- def get_entities_for_system(system)
50
- system_mask = @system_mask_cache[system]
51
- @entities_by_system_mask[system_mask]
47
+ def get_entities(component_mask)
48
+ @entities_by_component_mask[component_mask]
52
49
  end
53
50
 
54
51
  def get_entities_by_tag(tag)
@@ -57,27 +54,30 @@ module Baku
57
54
 
58
55
  private
59
56
 
60
- def get_component_mask(components)
61
- mask = 0
62
-
63
- @component_set.each_with_index do |c, i|
64
- mask |= (1 << i) if components.include?(c)
57
+ def add_entity_to_matching_component_lists(entity)
58
+ @entities_by_component_mask.each do |component_mask, entities|
59
+ if component_mask.matches?(entity.component_mask)
60
+ entities << entity
61
+ end
65
62
  end
63
+ end
66
64
 
67
- mask
65
+ def on_entity_component_added(entity, component)
66
+ add_entity_to_matching_component_lists(entity)
68
67
  end
69
68
 
70
- def update_system_membership(entity, old_mask, new_mask)
71
- @entities_by_system_mask.each do |system_mask, entities|
72
- old_match = (system_mask & old_mask == system_mask)
73
- new_match = (system_mask & new_mask == system_mask)
69
+ def on_entity_component_removed(entity, component)
70
+ old_mask = ComponentMask.from_components(entity.components + component)
71
+ new_mask = entity.component_mask
72
+
73
+ @entities_by_component_mask.each do |component_mask, entities|
74
+ old_match = component_mask.match?(old_mask)
75
+ new_match = component_mask.match?(new_mask)
74
76
 
75
77
  if old_match && !new_match
76
78
  entities.delete(entity)
77
- elsif !old_match && new_match
78
- entities << entity
79
79
  end
80
- end
80
+ end
81
81
  end
82
82
  end
83
83
  end
@@ -0,0 +1,19 @@
1
+ module Baku
2
+ module EventDispatcher
3
+ def add_event_listener(event_name, method)
4
+ @event_listeners ||= {}
5
+ @event_listeners[event_name] ||= []
6
+
7
+ @event_listeners[event_name] << method
8
+ end
9
+
10
+ def remove_event_listener(event_name, method)
11
+ @event_listeners[event_name].delete(method)
12
+ end
13
+
14
+ def dispatch_event(event_name, *args)
15
+ return unless @event_listeners && @event_listeners.has_key?(event_name)
16
+ @event_listeners[event_name].each { |f| f.call(*args) }
17
+ end
18
+ end
19
+ end
data/lib/baku/system.rb CHANGED
@@ -1,25 +1,30 @@
1
1
  module Baku
2
2
  class System
3
3
  attr_reader :components, :game_loop_step
4
- attr_accessor :world
4
+ attr_writer :entity_manager
5
5
 
6
6
  def initialize(components, game_loop_step)
7
7
  @components = components
8
8
  @game_loop_step = game_loop_step
9
9
  end
10
10
 
11
- def execute
12
- entities =
13
- @world.entity_manager.get_entities_for_system(self)
14
-
15
- entities.each do |entity|
11
+ def process_entities
12
+ if @entity_manager.nil?
13
+ raise StandardError.new("Must set :entity_manager property of System.")
14
+ end
15
+
16
+ @entity_manager.get_entities(component_mask).each do |entity|
16
17
  entity_components = @components.map { |c| entity.get_component(c) }
17
18
  process_entity(entity, *entity_components)
18
19
  end
19
20
  end
20
-
21
+
21
22
  def process_entity(entity)
22
23
  raise NotImplementedError
23
24
  end
25
+
26
+ def component_mask
27
+ @component_mask ||= ComponentMask.from_components(@components)
28
+ end
24
29
  end
25
30
  end
data/lib/baku/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Baku
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/baku/world.rb CHANGED
@@ -26,28 +26,34 @@ module Baku
26
26
  end
27
27
 
28
28
  system_list << system
29
- system.world = self
29
+
30
+ @entity_manager.register_component_mask(system.component_mask)
30
31
 
31
- @entity_manager.register_system(system)
32
+ system.entity_manager = @entity_manager
32
33
  end
33
34
 
34
35
  def create_entity(tags = [])
35
- entity = Entity.new(self, tags)
36
- @entity_manager.register_entity(entity)
36
+ entity = Entity.new(tags)
37
+ @entity_manager.add_entity(entity)
37
38
  entity
38
39
  end
39
40
 
40
- def get_entities_by_tag(tag)
41
- @entity_manager.get_entities_by_tag(tag)
41
+ def destroy_entity(entity)
42
+ @entity_manager.remove_entity(entity)
42
43
  end
43
44
 
44
45
  def update(delta_ms)
45
46
  @delta_ms = delta_ms
46
- @update_systems.each(&:execute)
47
+
48
+ @update_systems.each do |system|
49
+ system.process_entities
50
+ end
47
51
  end
48
52
 
49
53
  def draw
50
- @draw_systems.each(&:execute)
54
+ @draw_systems.each do |system|
55
+ system.process_entities
56
+ end
51
57
  end
52
58
  end
53
59
  end
data/lib/baku.rb CHANGED
@@ -1,8 +1,14 @@
1
1
  require "baku/version"
2
2
 
3
3
  module Baku
4
- require 'securerandom'
4
+ begin
5
+ require 'pry-byebug'
6
+ rescue LoadError
7
+ # development dependencies
8
+ end
9
+
5
10
  require 'set'
6
-
11
+
12
+ require_relative "baku/event_dispatcher.rb"
7
13
  Gem.find_files("baku/**/*.rb").each { |path| require path }
8
14
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: baku
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Tuttle
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-09-12 00:00:00.000000000 Z
11
+ date: 2017-09-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry-byebug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 3.5.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 3.5.0
55
69
  description: An Entity Component System framework for Ruby
56
70
  email:
57
71
  - jtuttle.develops@gmail.com
@@ -62,6 +76,7 @@ files:
62
76
  - ".gitignore"
63
77
  - ".rspec"
64
78
  - ".travis.yml"
79
+ - CHANGELOG.md
65
80
  - Gemfile
66
81
  - LICENSE.txt
67
82
  - README.md
@@ -71,8 +86,10 @@ files:
71
86
  - bin/setup
72
87
  - lib/baku.rb
73
88
  - lib/baku/component.rb
89
+ - lib/baku/component_mask.rb
74
90
  - lib/baku/entity.rb
75
91
  - lib/baku/entity_manager.rb
92
+ - lib/baku/event_dispatcher.rb
76
93
  - lib/baku/system.rb
77
94
  - lib/baku/version.rb
78
95
  - lib/baku/world.rb