The System (behavior) class
- DEFAULT_ASPECT_HASH
A Hash that auto-vivifies only its :default key
- aspect_entities R
The Hash of Sets of entity IDs which match the System’s aspects, keyed by aspect name.
- world R
The World which the System belongs to
aspect( name, *required, all_of: nil, one_of: nil, none_of: nil )
Add the specified component_types to the Aspect of this System as being required in any entities it processes.
55 def self::aspect( name, *required, all_of: nil, one_of: nil, none_of: nil )
56 aspect = Chione::Aspect.new
57
58 all_of = required + Array( all_of )
59
60 aspect = aspect.with_all_of( all_of )
61 aspect = aspect.with_one_of( one_of ) if one_of
62 aspect = aspect.with_none_of( none_of ) if none_of
63
64 self.aspects[ name ] = aspect
65 end
Declare a block that is called once every tick for each entity that matches the given aspect.
97 def self::every_tick( &block )
98 return self.on( 'timing' ) do |event_name, payload|
99 self.instance_exec( *payload, &block )
100 end
101 end
Add some per-subclass data structures to inheriting +subclass+es.
105 def self::inherited( subclass )
106 super
107 subclass.instance_variable_set( :@aspects, DEFAULT_ASPECT_HASH.clone )
108 subclass.instance_variable_set( :@event_handlers, self.event_handlers&.dup || [] )
109 subclass.instance_variable_set( :@injected_systems, self.injected_systems&.dup || {} )
110 end
Dependency-injection: declare one or more systems that should be passed to #start by the running World.
70 def self::inject( *systems )
71 systems.each do |system_type|
72 system_type = system_type.to_sym
73 system_class = Chione::System.get_subclass( system_type )
74 attr_accessor( "#{system_type}_system" )
75 self.injected_systems[ system_type ] = system_class
76 end
77 end
Create a new Chione::System for the specified world.
114 def initialize( world, * )
115 self.log.debug "Setting up %p" % [ self.class ]
116 @world = world
117 @aspect_entities = self.class.aspects.each_with_object( {} ) do |(aspect_name, aspect), hash|
118 matching_set = world.entities_with( aspect )
119 self.log.debug "Initial Set with the %s aspect: %p" % [ aspect_name, matching_set]
120 hash[ aspect_name ] = matching_set
121 end
122 end
Declare a block that is called once whenever an event matching event_name is broadcast to the World.
82 def self::on( event_name, &block )
83 raise LocalJumpError, "no block given" unless block
84 raise ArgumentError, "callback has wrong arity" unless block.arity >= 2 || block.arity < 0
85
86 method_name = "on_%s_event" % [ event_name.tr('/', '_') ]
87 self.log.debug "Making handler method #%s for %s events out of %p" %
88 [ method_name, event_name, block ]
89 define_method( method_name, &block )
90
91 self.event_handlers << [ event_name, method_name ]
92 end
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 singleton_attr_reader :aspects
entities( aspect_name=:default )
Return an Enumerator that yields the entities which match the given aspect_name.
167 def entities( aspect_name=:default )
168 return self.aspect_entities[ aspect_name ].to_enum( :each )
169 end
entity_components_updated( entity_id, components_hash )
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 def entity_components_updated( entity_id, components_hash )
177 self.class.aspects.each do |aspect_name, aspect|
178 entity_ids = self.aspect_entities[ aspect_name ]
179
180 if aspect.matches?( components_hash )
181 self.inserted( aspect_name, entity_id, components_hash ) if
182 entity_ids.add?( entity_id )
183 else
184 self.removed( aspect_name, entity_id, components_hash ) if
185 entity_ids.delete?( entity_id )
186 end
187 end
188 end
Event handler tuples (event name, callback) that should be registered when the System is started.
46 singleton_attr_reader :event_handlers
Systems to be injected by the world when this System is started.
50 singleton_attr_reader :injected_systems
inserted( aspect_name, entity_id, components )
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 def inserted( aspect_name, entity_id, components )
197 self.log.debug "Entity %s now matches the %s aspect." % [ entity_id, aspect_name ]
198 end
removed( aspect_name, entity_id, components )
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 def removed( aspect_name, entity_id, components )
207 self.log.debug "Entity %s no longer matches the %s aspect." % [ entity_id, aspect_name ]
208 end
start( **injected_systems )
Start the system.
139 def start( **injected_systems )
140 self.log.info "Starting the %p system; %d injected systems, %d event handlers to register" %
141 [ self.class, injected_systems.length, self.class.event_handlers.length ]
142
143 injected_systems.each do |name, other_system|
144 self.public_send( "#{name}_system=", other_system )
145 end
146
147 self.class.event_handlers.each do |event_name, method_name|
148 callback = self.method( method_name )
149 self.log.info "Registering %p as a callback for '%s' events." % [ callback, event_name ]
150 self.world.subscribe( event_name, callback )
151 end
152 end
Stop the system.
156 def stop
157 self.log.info "Stopping the %p system" % [ self.class ]
158 self.class.event_handlers.each do |_, method_name|
159 callback = self.method( method_name )
160 self.log.info "Unregistering subscription for %p." % [ callback ]
161 self.world.unsubscribe( callback )
162 end
163 end
Protected Instance Methods
Return the detail part of the inspection string.
216 def inspect_details
217 return "for %p:%#016x" % [ self.world.class, self.world.object_id * 2 ]
218 end