Class: Chione::System
- Inherits:
-
Object
- Object
- Chione::System
- Extended by:
- MethodUtilities, Loggability, Pluggability
- Includes:
- Inspection
- Defined in:
- lib/chione/system.rb
Overview
The System (behavior) class
Direct Known Subclasses
Constant Summary collapse
- DEFAULT_ASPECT_HASH =
A Hash that auto-vivifies only its :default key
Hash.new do |h,k| if k == :default h[k] = Chione::Aspect.new else nil end end
Instance Attribute Summary collapse
-
#aspect_entities ⇒ Object
readonly
The Hash of Sets of entity IDs which match the System’s aspects, keyed by aspect name.
-
#world ⇒ Object
readonly
The World which the System belongs to.
Class Method Summary collapse
-
.aspect(name, *required, all_of: nil, one_of: nil, none_of: nil) ⇒ Object
Add the specified
component_typesto the Aspect of this System as being required in any entities it processes. -
.every_tick(&block) ⇒ Object
Declare a block that is called once every tick for each entity that matches the given
aspect. -
.inherited(subclass) ⇒ Object
Add some per-subclass data structures to inheriting subclasses.
-
.inject(*systems) ⇒ Object
Dependency-injection: declare one or more
systemsthat should be passed to #start by the running World. -
.on(event_name, &block) ⇒ Object
Declare a block that is called once whenever an event matching
event_nameis broadcast to the World.
Instance Method Summary collapse
-
#aspects ⇒ Object
The Hash of Chione::Aspects that describe entities this system is interested in, keyed by name (a Symbol).
-
#entities(aspect_name = :default) ⇒ Object
Return an Enumerator that yields the entities which match the given
aspect_name. -
#entity_components_updated(entity_id, components_hash) ⇒ Object
Entity callback – called whenever an entity has a component added to it or removed from it.
-
#event_handlers ⇒ Object
Event handler tuples (event name, callback) that should be registered when the System is started.
-
#initialize(world) ⇒ System
constructor
Create a new Chione::System for the specified
world. -
#injected_systems ⇒ Object
Systems to be injected by the world when this System is started.
-
#inserted(aspect_name, entity_id, components) ⇒ Object
Entity callback – called whenever an entity has a component added to it that makes it start matching an aspect of the receiving System.
-
#removed(aspect_name, entity_id, components) ⇒ Object
Entity callback – called whenever an entity has a component removed from it that makes it stop matching an aspect of the receiving System.
-
#start(**injected_systems) ⇒ Object
Start the system.
-
#stop ⇒ Object
Stop the system.
Methods included from MethodUtilities
attr_predicate, attr_predicate_accessor, singleton_attr_accessor, singleton_attr_reader, singleton_attr_writer, singleton_method_alias, singleton_predicate_accessor, singleton_predicate_reader
Methods included from Inspection
Constructor Details
#initialize(world) ⇒ System
Create a new Chione::System for the specified world.
114 115 116 117 118 119 120 121 122 |
# File 'lib/chione/system.rb', line 114 def initialize( world, * ) self.log.debug "Setting up %p" % [ self.class ] @world = world @aspect_entities = self.class.aspects.each_with_object( {} ) do |(aspect_name, aspect), hash| matching_set = world.entities_with( aspect ) self.log.debug "Initial Set with the %s aspect: %p" % [ aspect_name, matching_set] hash[ aspect_name ] = matching_set end end |
Instance Attribute Details
#aspect_entities ⇒ Object (readonly)
The Hash of Sets of entity IDs which match the System’s aspects, keyed by aspect name.
135 136 137 |
# File 'lib/chione/system.rb', line 135 def aspect_entities @aspect_entities end |
#world ⇒ Object (readonly)
The World which the System belongs to
131 132 133 |
# File 'lib/chione/system.rb', line 131 def world @world end |
Class Method Details
.aspect(name, *required, all_of: nil, one_of: nil, none_of: nil) ⇒ Object
Add the specified component_types to the Aspect of this System as being required in any entities it processes.
55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/chione/system.rb', line 55 def self::aspect( name, *required, all_of: nil, one_of: nil, none_of: nil ) aspect = Chione::Aspect.new all_of = required + Array( all_of ) aspect = aspect.with_all_of( all_of ) aspect = aspect.with_one_of( one_of ) if one_of aspect = aspect.with_none_of( none_of ) if none_of self.aspects[ name ] = aspect end |
.every_tick(&block) ⇒ Object
Declare a block that is called once every tick for each entity that matches the given aspect.
97 98 99 100 101 |
# File 'lib/chione/system.rb', line 97 def self::every_tick( &block ) return self.on( 'timing' ) do |event_name, payload| self.instance_exec( *payload, &block ) end end |
.inherited(subclass) ⇒ Object
Add some per-subclass data structures to inheriting subclasses.
105 106 107 108 109 110 |
# File 'lib/chione/system.rb', line 105 def self::inherited( subclass ) super subclass.instance_variable_set( :@aspects, DEFAULT_ASPECT_HASH.clone ) subclass.instance_variable_set( :@event_handlers, self.event_handlers&.dup || [] ) subclass.instance_variable_set( :@injected_systems, self.injected_systems&.dup || {} ) end |
.inject(*systems) ⇒ Object
Dependency-injection: declare one or more systems that should be passed to #start by the running World.
70 71 72 73 74 75 76 77 |
# File 'lib/chione/system.rb', line 70 def self::inject( *systems ) systems.each do |system_type| system_type = system_type.to_sym system_class = Chione::System.get_subclass( system_type ) attr_accessor( "#{system_type}_system" ) self.injected_systems[ system_type ] = system_class end end |
.on(event_name, &block) ⇒ Object
Declare a block that is called once whenever an event matching event_name is broadcast to the World.
82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/chione/system.rb', line 82 def self::on( event_name, &block ) raise LocalJumpError, "no block given" unless block raise ArgumentError, "callback has wrong arity" unless block.arity >= 2 || block.arity < 0 method_name = "on_%s_event" % [ event_name.tr('/', '_') ] self.log.debug "Making handler method #%s for %s events out of %p" % [ method_name, event_name, block ] define_method( method_name, &block ) self.event_handlers << [ event_name, method_name ] end |
Instance Method Details
#aspects ⇒ Object
The Hash of Chione::Aspects that describe entities this system is interested in, keyed by name (a Symbol). A System which declares no aspects will have a :default Aspect which matches all entities.
41 |
# File 'lib/chione/system.rb', line 41 singleton_attr_reader :aspects |
#entities(aspect_name = :default) ⇒ Object
Return an Enumerator that yields the entities which match the given aspect_name.
167 168 169 |
# File 'lib/chione/system.rb', line 167 def entities( aspect_name=:default ) return self.aspect_entities[ aspect_name ].to_enum( :each ) end |
#entity_components_updated(entity_id, components_hash) ⇒ Object
Entity callback – called whenever an entity has a component added to it or removed from it. Calls the appropriate callback (#inserted or #removed) if the component change caused it to belong to or stop belonging to one of the system’s aspects.
176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/chione/system.rb', line 176 def entity_components_updated( entity_id, components_hash ) self.class.aspects.each do |aspect_name, aspect| entity_ids = self.aspect_entities[ aspect_name ] if aspect.matches?( components_hash ) self.inserted( aspect_name, entity_id, components_hash ) if entity_ids.add?( entity_id ) else self.removed( aspect_name, entity_id, components_hash ) if entity_ids.delete?( entity_id ) end end end |
#event_handlers ⇒ Object
Event handler tuples (event name, callback) that should be registered when the System is started.
46 |
# File 'lib/chione/system.rb', line 46 singleton_attr_reader :event_handlers |
#injected_systems ⇒ Object
Systems to be injected by the world when this System is started.
50 |
# File 'lib/chione/system.rb', line 50 singleton_attr_reader :injected_systems |
#inserted(aspect_name, entity_id, components) ⇒ Object
Entity callback – called whenever an entity has a component added to it that makes it start matching an aspect of the receiving System. The aspect_name is the name of the Aspect it now matches, and the components are a Hash of the entity’s components keyed by Class. By default this is a no-op.
196 197 198 |
# File 'lib/chione/system.rb', line 196 def inserted( aspect_name, entity_id, components ) self.log.debug "Entity %s now matches the %s aspect." % [ entity_id, aspect_name ] end |
#removed(aspect_name, entity_id, components) ⇒ Object
Entity callback – called whenever an entity has a component removed from it that makes it stop matching an aspect of the receiving System. The aspect_name is the name of the Aspect it no longer matches, and the components are a Hash of the entity’s components keyed by Class. By default this is a no-op.
206 207 208 |
# File 'lib/chione/system.rb', line 206 def removed( aspect_name, entity_id, components ) self.log.debug "Entity %s no longer matches the %s aspect." % [ entity_id, aspect_name ] end |
#start(**injected_systems) ⇒ Object
Start the system.
139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/chione/system.rb', line 139 def start( **injected_systems ) self.log.info "Starting the %p system; %d injected systems, %d event handlers to register" % [ self.class, injected_systems.length, self.class.event_handlers.length ] injected_systems.each do |name, other_system| self.public_send( "#{name}_system=", other_system ) end self.class.event_handlers.each do |event_name, method_name| callback = self.method( method_name ) self.log.info "Registering %p as a callback for '%s' events." % [ callback, event_name ] self.world.subscribe( event_name, callback ) end end |
#stop ⇒ Object
Stop the system.
156 157 158 159 160 161 162 163 |
# File 'lib/chione/system.rb', line 156 def stop self.log.info "Stopping the %p system" % [ self.class ] self.class.event_handlers.each do |_, method_name| callback = self.method( method_name ) self.log.info "Unregistering subscription for %p." % [ callback ] self.world.unsubscribe( callback ) end end |