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