class GameEcs::EntityStore
Attributes
entity_count[R]
id_to_comp[R]
Public Class Methods
new()
click to toggle source
# File lib/game_ecs/entity_store.rb, line 7 def initialize clear! end
Public Instance Methods
add_component(component:,id:)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 117 def add_component(component:,id:) if _iterating? _add_component_later component: component, id: id else _add_component component: component, id: id end end
add_entity(*components)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 141 def add_entity(*components) id = generate_id if _iterating? _add_entity_later(id:id, components: components) else _add_entity(id: id, components: components) end id end
clear!()
click to toggle source
# File lib/game_ecs/entity_store.rb, line 27 def clear! @comp_to_id = {} @id_to_comp = {} @cache = {} @entity_count = 0 @iterator_count = 0 @ents_to_add_later = [] @comps_to_add_later = [] @comps_to_remove_later = [] @ents_to_remove_later = [] clear_cache! end
clear_cache!()
click to toggle source
# File lib/game_ecs/entity_store.rb, line 41 def clear_cache! @cache = {} end
deep_clone()
click to toggle source
# File lib/game_ecs/entity_store.rb, line 11 def deep_clone # NOTE! does not work for Hashes with default procs if _iterating? raise "AHH! EM is still iterating!!" else _apply_updates clear_cache! em = Marshal.load( Marshal.dump(self) ) em end end
each_entity(*klasses, &blk)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 99 def each_entity(*klasses, &blk) ents = find(*klasses) if block_given? _iterating do ents.each &blk end end ents end
find_by_id(id, *klasses) { |rec| ... }
click to toggle source
# File lib/game_ecs/entity_store.rb, line 45 def find_by_id(id, *klasses) return nil unless @id_to_comp.key? id ent_record = @id_to_comp[id] components = ent_record.values_at(*klasses) rec = build_record(id, @id_to_comp[id], klasses) unless components.any?(&:nil?) if block_given? yield rec else rec end end
first(*klasses)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 95 def first(*klasses) find(*klasses).first end
musts(*klasses)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 57 def musts(*klasses) raise "specify at least one component" if klasses.empty? q = Q klasses.each{|k| q = q.must(k)} query(q) end
Also aliased as: find
num_entities()
click to toggle source
# File lib/game_ecs/entity_store.rb, line 23 def num_entities @id_to_comp.keys.size end
query(q)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 65 def query(q) # TODO cache results as q with content based cache # invalidate cache based on queried_comps cache_hit = @cache[q] return cache_hit if cache_hit queried_comps = q.components required_comps = q.required_components required_comps.each do |k| @comp_to_id[k] ||= Set.new end intersecting_ids = [] unless required_comps.empty? id_collection = @comp_to_id.values_at(*required_comps) intersecting_ids = id_collection.sort_by(&:size).inject &:& end recs = intersecting_ids. select{|eid| q.matches?(eid, @id_to_comp[eid]) }. map do |eid| build_record eid, @id_to_comp[eid], queried_comps end result = QueryResultSet.new(records: recs, ids: recs.map(&:id)) @cache[q] = result if q.cacheable? result end
remove_component(klass:, id:)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 109 def remove_component(klass:, id:) if _iterating? _remove_component_later klass: klass, id: id else _remove_component klass: klass, id: id end end
remove_entites(ids:)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 125 def remove_entites(ids:) if _iterating? _remove_entities_later(ids: ids) else _remove_entites(ids: ids) end end
remove_entity(id:)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 133 def remove_entity(id:) if _iterating? _remove_entity_later(id: id) else _remove_entity(id: id) end end
Private Instance Methods
_add_component(component:,id:)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 202 def _add_component(component:,id:) raise "Cannot add nil component" if component.nil? @comp_to_id[component.class] ||= Set.new @comp_to_id[component.class] << id @id_to_comp[id] ||= {} ent_record = @id_to_comp[id] klass = component.class raise "Cannot add component twice! #{component} -> #{id}" if ent_record.has_key? klass ent_record[klass] = component @cache.each do |q, results| # TODO make results a smart result set that knows about ids to avoid the linear scan # will musts vs maybes help here? comp_klasses = q.components if comp_klasses.include?(klass) if results.has_id?(id) results.add_component(id: id, component: component) else results << build_record(id, ent_record, comp_klasses) if q.matches?(id, ent_record) end end end nil end
_add_component_later(component:,id:)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 167 def _add_component_later(component:,id:) @comps_to_add_later << {component: component, id: id} end
_add_entity(id:, components:)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 273 def _add_entity(id:, components:) components.each do |comp| _add_component component: comp, id: id end id end
_add_entity_later(id:,components:)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 152 def _add_entity_later(id:,components:) @ents_to_add_later << {components: components, id: id} end
_apply_updates()
click to toggle source
# File lib/game_ecs/entity_store.rb, line 171 def _apply_updates _remove_entites ids: @ents_to_remove_later @ents_to_remove_later.clear @comps_to_remove_later.each do |opts| _remove_component klass: opts[:klass], id: opts[:id] end @comps_to_remove_later.clear @comps_to_add_later.each do |opts| _add_component component: opts[:component], id: opts[:id] end @comps_to_add_later.clear @ents_to_add_later.each do |opts| _add_entity id: opts[:id], components: opts[:components] end @ents_to_add_later.clear end
_iterating() { || ... }
click to toggle source
# File lib/game_ecs/entity_store.rb, line 191 def _iterating @iterator_count += 1 yield @iterator_count -= 1 _apply_updates unless _iterating? end
_iterating?()
click to toggle source
# File lib/game_ecs/entity_store.rb, line 198 def _iterating? @iterator_count > 0 end
_remove_component(klass:, id:)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 229 def _remove_component(klass:, id:) @comp_to_id[klass] ||= Set.new @comp_to_id[klass].delete id @id_to_comp[id] ||= {} @id_to_comp[id].delete klass @cache.each do |q, results| comp_klasses = q.components if comp_klasses.include?(klass) results.delete(id: id) unless q.matches?(id, @id_to_comp[id]) end end nil end
_remove_component_later(klass:,id:)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 164 def _remove_component_later(klass:,id:) @comps_to_remove_later << {klass: klass, id: id} end
_remove_entites(ids:)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 244 def _remove_entites(ids:) return if ids.empty? ids.each do |id| @id_to_comp.delete(id) end @comp_to_id.each do |_klass, ents| ents.delete_if{|ent_id| ids.include? ent_id} end @cache.each do |comp_klasses, results| results.delete ids: ids end end
_remove_entities_later(ids:)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 155 def _remove_entities_later(ids:) ids.each do |id| @ents_to_remove_later << id end end
_remove_entity(id:)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 260 def _remove_entity(id:) comp_map = @id_to_comp[id] if @id_to_comp.delete(id) ent_comps = comp_map.keys ent_comps.each do |klass| @comp_to_id[klass].delete id end @cache.each do |_query, results| results.delete id: id end end end
_remove_entity_later(id:)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 160 def _remove_entity_later(id:) @ents_to_remove_later << id end
build_record(*args)
click to toggle source
# File lib/game_ecs/entity_store.rb, line 286 def build_record(*args) EntityQueryResult.new(*args) end
generate_id()
click to toggle source
# File lib/game_ecs/entity_store.rb, line 280 def generate_id @entity_count += 1 @ent_counter ||= 0 @ent_counter += 1 end