bevy 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Cargo.lock +4279 -0
- data/Cargo.toml +36 -0
- data/README.md +226 -0
- data/crates/bevy/Cargo.toml +52 -0
- data/crates/bevy/src/app.rs +43 -0
- data/crates/bevy/src/component.rs +111 -0
- data/crates/bevy/src/entity.rs +30 -0
- data/crates/bevy/src/error.rs +32 -0
- data/crates/bevy/src/event.rs +190 -0
- data/crates/bevy/src/input_bridge.rs +300 -0
- data/crates/bevy/src/lib.rs +42 -0
- data/crates/bevy/src/mesh_renderer.rs +328 -0
- data/crates/bevy/src/query.rs +53 -0
- data/crates/bevy/src/render_app.rs +689 -0
- data/crates/bevy/src/resource.rs +28 -0
- data/crates/bevy/src/schedule.rs +186 -0
- data/crates/bevy/src/sprite_renderer.rs +355 -0
- data/crates/bevy/src/system.rs +44 -0
- data/crates/bevy/src/text_renderer.rs +258 -0
- data/crates/bevy/src/types/color.rs +114 -0
- data/crates/bevy/src/types/dynamic.rs +131 -0
- data/crates/bevy/src/types/math.rs +260 -0
- data/crates/bevy/src/types/mod.rs +9 -0
- data/crates/bevy/src/types/transform.rs +166 -0
- data/crates/bevy/src/world.rs +163 -0
- data/crates/bevy_ruby_render/Cargo.toml +22 -0
- data/crates/bevy_ruby_render/src/asset.rs +360 -0
- data/crates/bevy_ruby_render/src/audio.rs +511 -0
- data/crates/bevy_ruby_render/src/camera.rs +365 -0
- data/crates/bevy_ruby_render/src/gamepad.rs +398 -0
- data/crates/bevy_ruby_render/src/lib.rs +26 -0
- data/crates/bevy_ruby_render/src/material.rs +310 -0
- data/crates/bevy_ruby_render/src/mesh.rs +491 -0
- data/crates/bevy_ruby_render/src/sprite.rs +289 -0
- data/ext/bevy/Cargo.toml +20 -0
- data/ext/bevy/extconf.rb +6 -0
- data/ext/bevy/src/conversions.rs +137 -0
- data/ext/bevy/src/lib.rs +29 -0
- data/ext/bevy/src/ruby_app.rs +65 -0
- data/ext/bevy/src/ruby_color.rs +149 -0
- data/ext/bevy/src/ruby_component.rs +189 -0
- data/ext/bevy/src/ruby_entity.rs +33 -0
- data/ext/bevy/src/ruby_math.rs +384 -0
- data/ext/bevy/src/ruby_query.rs +64 -0
- data/ext/bevy/src/ruby_render_app.rs +779 -0
- data/ext/bevy/src/ruby_system.rs +122 -0
- data/ext/bevy/src/ruby_world.rs +107 -0
- data/lib/bevy/animation.rb +597 -0
- data/lib/bevy/app.rb +675 -0
- data/lib/bevy/asset.rb +613 -0
- data/lib/bevy/audio.rb +545 -0
- data/lib/bevy/audio_effects.rb +224 -0
- data/lib/bevy/camera.rb +412 -0
- data/lib/bevy/component.rb +91 -0
- data/lib/bevy/diagnostics.rb +227 -0
- data/lib/bevy/ecs_advanced.rb +296 -0
- data/lib/bevy/event.rb +199 -0
- data/lib/bevy/gizmos.rb +158 -0
- data/lib/bevy/gltf.rb +227 -0
- data/lib/bevy/hierarchy.rb +444 -0
- data/lib/bevy/input.rb +514 -0
- data/lib/bevy/lighting.rb +369 -0
- data/lib/bevy/material.rb +248 -0
- data/lib/bevy/mesh.rb +257 -0
- data/lib/bevy/navigation.rb +344 -0
- data/lib/bevy/networking.rb +335 -0
- data/lib/bevy/particle.rb +337 -0
- data/lib/bevy/physics.rb +396 -0
- data/lib/bevy/plugins/default_plugins.rb +34 -0
- data/lib/bevy/plugins/input_plugin.rb +49 -0
- data/lib/bevy/reflect.rb +361 -0
- data/lib/bevy/render_graph.rb +210 -0
- data/lib/bevy/resource.rb +185 -0
- data/lib/bevy/scene.rb +254 -0
- data/lib/bevy/shader.rb +319 -0
- data/lib/bevy/shape.rb +195 -0
- data/lib/bevy/skeletal.rb +248 -0
- data/lib/bevy/sprite.rb +152 -0
- data/lib/bevy/sprite_sheet.rb +444 -0
- data/lib/bevy/state.rb +277 -0
- data/lib/bevy/system.rb +206 -0
- data/lib/bevy/text.rb +99 -0
- data/lib/bevy/text_advanced.rb +455 -0
- data/lib/bevy/timer.rb +147 -0
- data/lib/bevy/transform.rb +158 -0
- data/lib/bevy/ui.rb +454 -0
- data/lib/bevy/ui_advanced.rb +568 -0
- data/lib/bevy/version.rb +5 -0
- data/lib/bevy/visibility.rb +250 -0
- data/lib/bevy/window.rb +302 -0
- data/lib/bevy.rb +390 -0
- metadata +150 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bevy
|
|
4
|
+
class DiagnosticsStore
|
|
5
|
+
attr_reader :diagnostics
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@diagnostics = {}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def add(diagnostic)
|
|
12
|
+
@diagnostics[diagnostic.id] = diagnostic
|
|
13
|
+
self
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def get(id)
|
|
17
|
+
@diagnostics[id]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def remove(id)
|
|
21
|
+
@diagnostics.delete(id)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def get_measurement(id)
|
|
25
|
+
diagnostic = @diagnostics[id]
|
|
26
|
+
diagnostic&.average
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def type_name
|
|
30
|
+
'DiagnosticsStore'
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class Diagnostic
|
|
35
|
+
attr_reader :id, :name, :suffix, :history
|
|
36
|
+
|
|
37
|
+
def initialize(id:, name:, max_history: 120, suffix: nil)
|
|
38
|
+
@id = id
|
|
39
|
+
@name = name
|
|
40
|
+
@suffix = suffix || ''
|
|
41
|
+
@max_history = max_history
|
|
42
|
+
@history = []
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def add_measurement(value)
|
|
46
|
+
@history << { value: value.to_f, time: ::Time.now }
|
|
47
|
+
@history.shift while @history.size > @max_history
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def value
|
|
51
|
+
@history.last&.fetch(:value)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def average
|
|
55
|
+
return nil if @history.empty?
|
|
56
|
+
|
|
57
|
+
@history.sum { |m| m[:value] } / @history.size
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def smoothed
|
|
61
|
+
return nil if @history.size < 2
|
|
62
|
+
|
|
63
|
+
@history.last(10).sum { |m| m[:value] } / [10, @history.size].min
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def clear_history
|
|
67
|
+
@history = []
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def type_name
|
|
71
|
+
'Diagnostic'
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class FrameTimeDiagnostics
|
|
76
|
+
FPS_ID = 'fps'
|
|
77
|
+
FRAME_TIME_ID = 'frame_time'
|
|
78
|
+
FRAME_COUNT_ID = 'frame_count'
|
|
79
|
+
|
|
80
|
+
attr_reader :fps, :frame_time, :frame_count
|
|
81
|
+
|
|
82
|
+
def initialize
|
|
83
|
+
@fps = Diagnostic.new(id: FPS_ID, name: 'FPS', suffix: ' fps')
|
|
84
|
+
@frame_time = Diagnostic.new(id: FRAME_TIME_ID, name: 'Frame Time', suffix: ' ms')
|
|
85
|
+
@frame_count = 0
|
|
86
|
+
@last_time = ::Time.now
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def update
|
|
90
|
+
current_time = ::Time.now
|
|
91
|
+
delta = current_time - @last_time
|
|
92
|
+
@last_time = current_time
|
|
93
|
+
@frame_count += 1
|
|
94
|
+
|
|
95
|
+
@frame_time.add_measurement(delta * 1000.0)
|
|
96
|
+
@fps.add_measurement(1.0 / delta) if delta > 0
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def diagnostics
|
|
100
|
+
[@fps, @frame_time]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def type_name
|
|
104
|
+
'FrameTimeDiagnostics'
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
class EntityCountDiagnostics
|
|
109
|
+
ENTITY_COUNT_ID = 'entity_count'
|
|
110
|
+
|
|
111
|
+
attr_reader :entity_count
|
|
112
|
+
|
|
113
|
+
def initialize
|
|
114
|
+
@entity_count = Diagnostic.new(id: ENTITY_COUNT_ID, name: 'Entity Count')
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def update(world)
|
|
118
|
+
count = world.respond_to?(:entity_count) ? world.entity_count : 0
|
|
119
|
+
@entity_count.add_measurement(count)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def diagnostics
|
|
123
|
+
[@entity_count]
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def type_name
|
|
127
|
+
'EntityCountDiagnostics'
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
class SystemDiagnostics
|
|
132
|
+
attr_reader :system_timings
|
|
133
|
+
|
|
134
|
+
def initialize
|
|
135
|
+
@system_timings = {}
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def start_system(name)
|
|
139
|
+
@system_timings[name] ||= Diagnostic.new(id: "system_#{name}", name: name, suffix: ' ms')
|
|
140
|
+
@system_timings[name].instance_variable_set(:@start_time, ::Time.now)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def end_system(name)
|
|
144
|
+
diagnostic = @system_timings[name]
|
|
145
|
+
return unless diagnostic
|
|
146
|
+
|
|
147
|
+
start_time = diagnostic.instance_variable_get(:@start_time)
|
|
148
|
+
return unless start_time
|
|
149
|
+
|
|
150
|
+
elapsed = (::Time.now - start_time) * 1000.0
|
|
151
|
+
diagnostic.add_measurement(elapsed)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def diagnostics
|
|
155
|
+
@system_timings.values
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def type_name
|
|
159
|
+
'SystemDiagnostics'
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
class LogDiagnostics
|
|
164
|
+
attr_accessor :enabled, :wait_duration
|
|
165
|
+
|
|
166
|
+
def initialize(wait_duration: 1.0)
|
|
167
|
+
@enabled = true
|
|
168
|
+
@wait_duration = wait_duration.to_f
|
|
169
|
+
@last_log_time = ::Time.now
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def log(store)
|
|
173
|
+
return unless @enabled
|
|
174
|
+
|
|
175
|
+
current_time = ::Time.now
|
|
176
|
+
return unless current_time - @last_log_time >= @wait_duration
|
|
177
|
+
|
|
178
|
+
@last_log_time = current_time
|
|
179
|
+
store.diagnostics.each_value do |diagnostic|
|
|
180
|
+
puts format_diagnostic(diagnostic) if diagnostic.value
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def type_name
|
|
185
|
+
'LogDiagnostics'
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
private
|
|
189
|
+
|
|
190
|
+
def format_diagnostic(diagnostic)
|
|
191
|
+
value = diagnostic.smoothed || diagnostic.value
|
|
192
|
+
"#{diagnostic.name}: #{value.round(2)}#{diagnostic.suffix}"
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
class PerformanceMetrics
|
|
197
|
+
attr_reader :draw_calls, :triangles, :vertices, :textures_bound
|
|
198
|
+
|
|
199
|
+
def initialize
|
|
200
|
+
@draw_calls = 0
|
|
201
|
+
@triangles = 0
|
|
202
|
+
@vertices = 0
|
|
203
|
+
@textures_bound = 0
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def reset
|
|
207
|
+
@draw_calls = 0
|
|
208
|
+
@triangles = 0
|
|
209
|
+
@vertices = 0
|
|
210
|
+
@textures_bound = 0
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def add_draw_call(triangles: 0, vertices: 0)
|
|
214
|
+
@draw_calls += 1
|
|
215
|
+
@triangles += triangles
|
|
216
|
+
@vertices += vertices
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def bind_texture
|
|
220
|
+
@textures_bound += 1
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def type_name
|
|
224
|
+
'PerformanceMetrics'
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bevy
|
|
4
|
+
class Changed
|
|
5
|
+
attr_reader :component_type
|
|
6
|
+
|
|
7
|
+
def initialize(component_type)
|
|
8
|
+
@component_type = component_type
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def type_name
|
|
12
|
+
'Changed'
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class Added
|
|
17
|
+
attr_reader :component_type
|
|
18
|
+
|
|
19
|
+
def initialize(component_type)
|
|
20
|
+
@component_type = component_type
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def type_name
|
|
24
|
+
'Added'
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class With
|
|
29
|
+
attr_reader :component_types
|
|
30
|
+
|
|
31
|
+
def initialize(*component_types)
|
|
32
|
+
@component_types = component_types
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def type_name
|
|
36
|
+
'With'
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Without
|
|
41
|
+
attr_reader :component_types
|
|
42
|
+
|
|
43
|
+
def initialize(*component_types)
|
|
44
|
+
@component_types = component_types
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def type_name
|
|
48
|
+
'Without'
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
class Or
|
|
53
|
+
attr_reader :filters
|
|
54
|
+
|
|
55
|
+
def initialize(*filters)
|
|
56
|
+
@filters = filters
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def type_name
|
|
60
|
+
'Or'
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class ChangeTrackers
|
|
65
|
+
attr_reader :entity, :component_type
|
|
66
|
+
|
|
67
|
+
def initialize(entity:, component_type:)
|
|
68
|
+
@entity = entity
|
|
69
|
+
@component_type = component_type
|
|
70
|
+
@added_tick = 0
|
|
71
|
+
@changed_tick = 0
|
|
72
|
+
@last_run = 0
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def is_added?(current_tick)
|
|
76
|
+
@added_tick > @last_run && @added_tick <= current_tick
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def is_changed?(current_tick)
|
|
80
|
+
@changed_tick > @last_run && @changed_tick <= current_tick
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def set_added(tick)
|
|
84
|
+
@added_tick = tick
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def set_changed(tick)
|
|
88
|
+
@changed_tick = tick
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def update_last_run(tick)
|
|
92
|
+
@last_run = tick
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def type_name
|
|
96
|
+
'ChangeTrackers'
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
class ComponentTracker
|
|
101
|
+
def initialize
|
|
102
|
+
@trackers = {}
|
|
103
|
+
@current_tick = 0
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def tick
|
|
107
|
+
@current_tick += 1
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def current_tick
|
|
111
|
+
@current_tick
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def track_add(entity, component_type)
|
|
115
|
+
key = [entity, component_type]
|
|
116
|
+
@trackers[key] ||= ChangeTrackers.new(entity: entity, component_type: component_type)
|
|
117
|
+
@trackers[key].set_added(@current_tick)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def track_change(entity, component_type)
|
|
121
|
+
key = [entity, component_type]
|
|
122
|
+
@trackers[key] ||= ChangeTrackers.new(entity: entity, component_type: component_type)
|
|
123
|
+
@trackers[key].set_changed(@current_tick)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def added?(entity, component_type)
|
|
127
|
+
key = [entity, component_type]
|
|
128
|
+
tracker = @trackers[key]
|
|
129
|
+
tracker&.is_added?(@current_tick) || false
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def changed?(entity, component_type)
|
|
133
|
+
key = [entity, component_type]
|
|
134
|
+
tracker = @trackers[key]
|
|
135
|
+
tracker&.is_changed?(@current_tick) || false
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def type_name
|
|
139
|
+
'ComponentTracker'
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
class SystemSet
|
|
144
|
+
attr_reader :name, :systems
|
|
145
|
+
|
|
146
|
+
def initialize(name)
|
|
147
|
+
@name = name
|
|
148
|
+
@systems = []
|
|
149
|
+
@run_condition = nil
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def add_system(system)
|
|
153
|
+
@systems << system
|
|
154
|
+
self
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def run_if(&condition)
|
|
158
|
+
@run_condition = condition
|
|
159
|
+
self
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def should_run?(context)
|
|
163
|
+
return true unless @run_condition
|
|
164
|
+
|
|
165
|
+
@run_condition.call(context)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def type_name
|
|
169
|
+
'SystemSet'
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
class RunCondition
|
|
174
|
+
def self.resource_exists(resource_type)
|
|
175
|
+
->(ctx) { ctx.has_resource?(resource_type) }
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def self.resource_equals(resource_type, value)
|
|
179
|
+
->(ctx) { ctx.get_resource(resource_type) == value }
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def self.state_equals(state_type, value)
|
|
183
|
+
->(ctx) { ctx.get_state(state_type) == value }
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def self.in_state(state)
|
|
187
|
+
->(ctx) { ctx.current_state == state }
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def self.run_once
|
|
191
|
+
ran = false
|
|
192
|
+
lambda do |_ctx|
|
|
193
|
+
return false if ran
|
|
194
|
+
|
|
195
|
+
ran = true
|
|
196
|
+
true
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def self.not(condition)
|
|
201
|
+
->(ctx) { !condition.call(ctx) }
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def self.and(cond1, cond2)
|
|
205
|
+
->(ctx) { cond1.call(ctx) && cond2.call(ctx) }
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def self.or(cond1, cond2)
|
|
209
|
+
->(ctx) { cond1.call(ctx) || cond2.call(ctx) }
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
class Commands
|
|
214
|
+
def initialize(world)
|
|
215
|
+
@world = world
|
|
216
|
+
@command_queue = []
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def spawn(*components)
|
|
220
|
+
entity = @world.spawn_entity(*components)
|
|
221
|
+
EntityCommands.new(entity, @world)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def entity(entity)
|
|
225
|
+
EntityCommands.new(entity, @world)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def despawn(entity)
|
|
229
|
+
@command_queue << [:despawn, entity]
|
|
230
|
+
self
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def insert_resource(resource)
|
|
234
|
+
@command_queue << [:insert_resource, resource]
|
|
235
|
+
self
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def remove_resource(resource_type)
|
|
239
|
+
@command_queue << [:remove_resource, resource_type]
|
|
240
|
+
self
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def apply
|
|
244
|
+
@command_queue.each do |cmd, *args|
|
|
245
|
+
case cmd
|
|
246
|
+
when :despawn
|
|
247
|
+
@world.despawn(args[0])
|
|
248
|
+
when :insert_resource
|
|
249
|
+
@world.insert_resource(args[0])
|
|
250
|
+
when :remove_resource
|
|
251
|
+
@world.remove_resource(args[0])
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
@command_queue = []
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def type_name
|
|
258
|
+
'Commands'
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
class EntityCommands
|
|
263
|
+
def initialize(entity, world)
|
|
264
|
+
@entity = entity
|
|
265
|
+
@world = world
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def insert(*components)
|
|
269
|
+
components.each do |component|
|
|
270
|
+
@world.insert_component(@entity, component)
|
|
271
|
+
end
|
|
272
|
+
self
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def remove(component_type)
|
|
276
|
+
@world.remove_component(@entity, component_type)
|
|
277
|
+
self
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def despawn
|
|
281
|
+
@world.despawn(@entity)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def despawn_recursive
|
|
285
|
+
DespawnRecursive.despawn(@world, @entity)
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def id
|
|
289
|
+
@entity
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def type_name
|
|
293
|
+
'EntityCommands'
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
data/lib/bevy/event.rb
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bevy
|
|
4
|
+
class EventDSL
|
|
5
|
+
class << self
|
|
6
|
+
def attribute(name, type, default: nil)
|
|
7
|
+
@attributes ||= {}
|
|
8
|
+
@attributes[name] = { type: type, default: default }
|
|
9
|
+
|
|
10
|
+
define_method(name) { @data[name] }
|
|
11
|
+
define_method(:"#{name}=") { |value| @data[name] = value }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def attributes
|
|
15
|
+
@attributes ||= {}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def event_name
|
|
19
|
+
name || 'AnonymousEvent'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def inherited(subclass)
|
|
23
|
+
super
|
|
24
|
+
subclass.instance_variable_set(:@attributes, attributes.dup)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def initialize(**attrs)
|
|
29
|
+
@data = {}
|
|
30
|
+
self.class.attributes.each do |attr_name, config|
|
|
31
|
+
default = config[:default]
|
|
32
|
+
default_value = default.respond_to?(:call) ? default.call : default
|
|
33
|
+
@data[attr_name] = attrs.fetch(attr_name, default_value)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def to_h
|
|
38
|
+
@data.dup
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class Events
|
|
43
|
+
def initialize(event_class)
|
|
44
|
+
@event_class = event_class
|
|
45
|
+
@events = []
|
|
46
|
+
@events_last_frame = []
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def send(event)
|
|
50
|
+
validate_event!(event)
|
|
51
|
+
@events << event
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def read
|
|
55
|
+
@events_last_frame + @events
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def drain
|
|
59
|
+
result = @events.dup
|
|
60
|
+
@events.clear
|
|
61
|
+
result
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def clear
|
|
65
|
+
@events.clear
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def update
|
|
69
|
+
@events_last_frame = @events.dup
|
|
70
|
+
@events.clear
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def empty?
|
|
74
|
+
@events.empty? && @events_last_frame.empty?
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def len
|
|
78
|
+
@events.length + @events_last_frame.length
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def validate_event!(event)
|
|
84
|
+
return if event.is_a?(@event_class)
|
|
85
|
+
|
|
86
|
+
raise ArgumentError, "Expected #{@event_class}, got #{event.class}"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
class EventReader
|
|
91
|
+
def initialize(events)
|
|
92
|
+
@events = events
|
|
93
|
+
@cursor = 0
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def read
|
|
97
|
+
all_events = @events.read
|
|
98
|
+
new_events = all_events[@cursor..]
|
|
99
|
+
@cursor = all_events.length
|
|
100
|
+
new_events || []
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def is_empty?
|
|
104
|
+
@events.read.length <= @cursor
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def len
|
|
108
|
+
[@events.read.length - @cursor, 0].max
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def clear
|
|
112
|
+
@cursor = @events.read.length
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
class EventWriter
|
|
117
|
+
def initialize(events)
|
|
118
|
+
@events = events
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def send(event)
|
|
122
|
+
@events.send(event)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def send_batch(events)
|
|
126
|
+
events.each { |e| @events.send(e) }
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def send_default
|
|
130
|
+
@events.send(@events.instance_variable_get(:@event_class).new)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
class EventRegistry
|
|
135
|
+
def initialize
|
|
136
|
+
@events = {}
|
|
137
|
+
@readers = {}
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def register(event_class)
|
|
141
|
+
type_name = event_class_name(event_class)
|
|
142
|
+
@events[type_name] ||= Events.new(event_class)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def get_events(event_class)
|
|
146
|
+
type_name = event_class_name(event_class)
|
|
147
|
+
@events[type_name]
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def reader(event_class)
|
|
151
|
+
type_name = event_class_name(event_class)
|
|
152
|
+
events = @events[type_name]
|
|
153
|
+
return nil unless events
|
|
154
|
+
|
|
155
|
+
@readers[type_name] ||= EventReader.new(events)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def writer(event_class)
|
|
159
|
+
type_name = event_class_name(event_class)
|
|
160
|
+
events = @events[type_name]
|
|
161
|
+
return nil unless events
|
|
162
|
+
|
|
163
|
+
EventWriter.new(events)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def update_all
|
|
167
|
+
@events.each_value(&:update)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def clear_all
|
|
171
|
+
@events.each_value(&:clear)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
private
|
|
175
|
+
|
|
176
|
+
def event_class_name(event_class)
|
|
177
|
+
case event_class
|
|
178
|
+
when Class
|
|
179
|
+
event_class.respond_to?(:event_name) ? event_class.event_name : event_class.name
|
|
180
|
+
when String
|
|
181
|
+
event_class
|
|
182
|
+
else
|
|
183
|
+
raise ArgumentError, 'Expected Class or String'
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
class PickingEvent < EventDSL
|
|
189
|
+
attribute :kind, :string, default: ''
|
|
190
|
+
attribute :target_id, :integer, default: 0
|
|
191
|
+
attribute :pointer_id, :string, default: ''
|
|
192
|
+
attribute :button, :string, default: nil
|
|
193
|
+
attribute :position, :vec2, default: -> { Vec2.zero }
|
|
194
|
+
attribute :camera_id, :integer, default: nil
|
|
195
|
+
attribute :depth, :float, default: nil
|
|
196
|
+
attribute :hit_position, :vec3, default: nil
|
|
197
|
+
attribute :hit_normal, :vec3, default: nil
|
|
198
|
+
end
|
|
199
|
+
end
|