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.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/Cargo.lock +4279 -0
  3. data/Cargo.toml +36 -0
  4. data/README.md +226 -0
  5. data/crates/bevy/Cargo.toml +52 -0
  6. data/crates/bevy/src/app.rs +43 -0
  7. data/crates/bevy/src/component.rs +111 -0
  8. data/crates/bevy/src/entity.rs +30 -0
  9. data/crates/bevy/src/error.rs +32 -0
  10. data/crates/bevy/src/event.rs +190 -0
  11. data/crates/bevy/src/input_bridge.rs +300 -0
  12. data/crates/bevy/src/lib.rs +42 -0
  13. data/crates/bevy/src/mesh_renderer.rs +328 -0
  14. data/crates/bevy/src/query.rs +53 -0
  15. data/crates/bevy/src/render_app.rs +689 -0
  16. data/crates/bevy/src/resource.rs +28 -0
  17. data/crates/bevy/src/schedule.rs +186 -0
  18. data/crates/bevy/src/sprite_renderer.rs +355 -0
  19. data/crates/bevy/src/system.rs +44 -0
  20. data/crates/bevy/src/text_renderer.rs +258 -0
  21. data/crates/bevy/src/types/color.rs +114 -0
  22. data/crates/bevy/src/types/dynamic.rs +131 -0
  23. data/crates/bevy/src/types/math.rs +260 -0
  24. data/crates/bevy/src/types/mod.rs +9 -0
  25. data/crates/bevy/src/types/transform.rs +166 -0
  26. data/crates/bevy/src/world.rs +163 -0
  27. data/crates/bevy_ruby_render/Cargo.toml +22 -0
  28. data/crates/bevy_ruby_render/src/asset.rs +360 -0
  29. data/crates/bevy_ruby_render/src/audio.rs +511 -0
  30. data/crates/bevy_ruby_render/src/camera.rs +365 -0
  31. data/crates/bevy_ruby_render/src/gamepad.rs +398 -0
  32. data/crates/bevy_ruby_render/src/lib.rs +26 -0
  33. data/crates/bevy_ruby_render/src/material.rs +310 -0
  34. data/crates/bevy_ruby_render/src/mesh.rs +491 -0
  35. data/crates/bevy_ruby_render/src/sprite.rs +289 -0
  36. data/ext/bevy/Cargo.toml +20 -0
  37. data/ext/bevy/extconf.rb +6 -0
  38. data/ext/bevy/src/conversions.rs +137 -0
  39. data/ext/bevy/src/lib.rs +29 -0
  40. data/ext/bevy/src/ruby_app.rs +65 -0
  41. data/ext/bevy/src/ruby_color.rs +149 -0
  42. data/ext/bevy/src/ruby_component.rs +189 -0
  43. data/ext/bevy/src/ruby_entity.rs +33 -0
  44. data/ext/bevy/src/ruby_math.rs +384 -0
  45. data/ext/bevy/src/ruby_query.rs +64 -0
  46. data/ext/bevy/src/ruby_render_app.rs +779 -0
  47. data/ext/bevy/src/ruby_system.rs +122 -0
  48. data/ext/bevy/src/ruby_world.rs +107 -0
  49. data/lib/bevy/animation.rb +597 -0
  50. data/lib/bevy/app.rb +675 -0
  51. data/lib/bevy/asset.rb +613 -0
  52. data/lib/bevy/audio.rb +545 -0
  53. data/lib/bevy/audio_effects.rb +224 -0
  54. data/lib/bevy/camera.rb +412 -0
  55. data/lib/bevy/component.rb +91 -0
  56. data/lib/bevy/diagnostics.rb +227 -0
  57. data/lib/bevy/ecs_advanced.rb +296 -0
  58. data/lib/bevy/event.rb +199 -0
  59. data/lib/bevy/gizmos.rb +158 -0
  60. data/lib/bevy/gltf.rb +227 -0
  61. data/lib/bevy/hierarchy.rb +444 -0
  62. data/lib/bevy/input.rb +514 -0
  63. data/lib/bevy/lighting.rb +369 -0
  64. data/lib/bevy/material.rb +248 -0
  65. data/lib/bevy/mesh.rb +257 -0
  66. data/lib/bevy/navigation.rb +344 -0
  67. data/lib/bevy/networking.rb +335 -0
  68. data/lib/bevy/particle.rb +337 -0
  69. data/lib/bevy/physics.rb +396 -0
  70. data/lib/bevy/plugins/default_plugins.rb +34 -0
  71. data/lib/bevy/plugins/input_plugin.rb +49 -0
  72. data/lib/bevy/reflect.rb +361 -0
  73. data/lib/bevy/render_graph.rb +210 -0
  74. data/lib/bevy/resource.rb +185 -0
  75. data/lib/bevy/scene.rb +254 -0
  76. data/lib/bevy/shader.rb +319 -0
  77. data/lib/bevy/shape.rb +195 -0
  78. data/lib/bevy/skeletal.rb +248 -0
  79. data/lib/bevy/sprite.rb +152 -0
  80. data/lib/bevy/sprite_sheet.rb +444 -0
  81. data/lib/bevy/state.rb +277 -0
  82. data/lib/bevy/system.rb +206 -0
  83. data/lib/bevy/text.rb +99 -0
  84. data/lib/bevy/text_advanced.rb +455 -0
  85. data/lib/bevy/timer.rb +147 -0
  86. data/lib/bevy/transform.rb +158 -0
  87. data/lib/bevy/ui.rb +454 -0
  88. data/lib/bevy/ui_advanced.rb +568 -0
  89. data/lib/bevy/version.rb +5 -0
  90. data/lib/bevy/visibility.rb +250 -0
  91. data/lib/bevy/window.rb +302 -0
  92. data/lib/bevy.rb +390 -0
  93. metadata +150 -0
@@ -0,0 +1,597 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bevy
4
+ module Easing
5
+ def self.linear(t)
6
+ t
7
+ end
8
+
9
+ def self.ease_in_quad(t)
10
+ t * t
11
+ end
12
+
13
+ def self.ease_out_quad(t)
14
+ 1.0 - (1.0 - t) * (1.0 - t)
15
+ end
16
+
17
+ def self.ease_in_out_quad(t)
18
+ t < 0.5 ? 2.0 * t * t : 1.0 - ((-2.0 * t + 2.0)**2) / 2.0
19
+ end
20
+
21
+ def self.ease_in_cubic(t)
22
+ t * t * t
23
+ end
24
+
25
+ def self.ease_out_cubic(t)
26
+ 1.0 - ((1.0 - t)**3)
27
+ end
28
+
29
+ def self.ease_in_out_cubic(t)
30
+ t < 0.5 ? 4.0 * t * t * t : 1.0 - ((-2.0 * t + 2.0)**3) / 2.0
31
+ end
32
+
33
+ def self.ease_in_sine(t)
34
+ 1.0 - Math.cos((t * Math::PI) / 2.0)
35
+ end
36
+
37
+ def self.ease_out_sine(t)
38
+ Math.sin((t * Math::PI) / 2.0)
39
+ end
40
+
41
+ def self.ease_in_out_sine(t)
42
+ -(Math.cos(Math::PI * t) - 1.0) / 2.0
43
+ end
44
+
45
+ def self.ease_in_expo(t)
46
+ t == 0.0 ? 0.0 : 2.0**(10.0 * t - 10.0)
47
+ end
48
+
49
+ def self.ease_out_expo(t)
50
+ t == 1.0 ? 1.0 : 1.0 - 2.0**(-10.0 * t)
51
+ end
52
+
53
+ def self.ease_in_out_expo(t)
54
+ return 0.0 if t == 0.0
55
+ return 1.0 if t == 1.0
56
+
57
+ if t < 0.5
58
+ 2.0**(20.0 * t - 10.0) / 2.0
59
+ else
60
+ (2.0 - 2.0**(-20.0 * t + 10.0)) / 2.0
61
+ end
62
+ end
63
+
64
+ def self.ease_in_back(t)
65
+ c1 = 1.70158
66
+ c3 = c1 + 1.0
67
+ c3 * t * t * t - c1 * t * t
68
+ end
69
+
70
+ def self.ease_out_back(t)
71
+ c1 = 1.70158
72
+ c3 = c1 + 1.0
73
+ 1.0 + c3 * ((t - 1.0)**3) + c1 * ((t - 1.0)**2)
74
+ end
75
+
76
+ def self.ease_in_out_back(t)
77
+ c1 = 1.70158
78
+ c2 = c1 * 1.525
79
+ if t < 0.5
80
+ ((2.0 * t)**2 * ((c2 + 1.0) * 2.0 * t - c2)) / 2.0
81
+ else
82
+ ((2.0 * t - 2.0)**2 * ((c2 + 1.0) * (t * 2.0 - 2.0) + c2) + 2.0) / 2.0
83
+ end
84
+ end
85
+
86
+ def self.ease_in_elastic(t)
87
+ return 0.0 if t == 0.0
88
+ return 1.0 if t == 1.0
89
+
90
+ c4 = (2.0 * Math::PI) / 3.0
91
+ -(2.0**(10.0 * t - 10.0)) * Math.sin((t * 10.0 - 10.75) * c4)
92
+ end
93
+
94
+ def self.ease_out_elastic(t)
95
+ return 0.0 if t == 0.0
96
+ return 1.0 if t == 1.0
97
+
98
+ c4 = (2.0 * Math::PI) / 3.0
99
+ 2.0**(-10.0 * t) * Math.sin((t * 10.0 - 0.75) * c4) + 1.0
100
+ end
101
+
102
+ def self.ease_in_bounce(t)
103
+ 1.0 - ease_out_bounce(1.0 - t)
104
+ end
105
+
106
+ def self.ease_out_bounce(t)
107
+ n1 = 7.5625
108
+ d1 = 2.75
109
+
110
+ if t < 1.0 / d1
111
+ n1 * t * t
112
+ elsif t < 2.0 / d1
113
+ t -= 1.5 / d1
114
+ n1 * t * t + 0.75
115
+ elsif t < 2.5 / d1
116
+ t -= 2.25 / d1
117
+ n1 * t * t + 0.9375
118
+ else
119
+ t -= 2.625 / d1
120
+ n1 * t * t + 0.984375
121
+ end
122
+ end
123
+
124
+ def self.ease_in_out_bounce(t)
125
+ if t < 0.5
126
+ (1.0 - ease_out_bounce(1.0 - 2.0 * t)) / 2.0
127
+ else
128
+ (1.0 + ease_out_bounce(2.0 * t - 1.0)) / 2.0
129
+ end
130
+ end
131
+
132
+ def self.apply(easing, t)
133
+ case easing
134
+ when :linear then linear(t)
135
+ when :ease_in_quad then ease_in_quad(t)
136
+ when :ease_out_quad then ease_out_quad(t)
137
+ when :ease_in_out_quad then ease_in_out_quad(t)
138
+ when :ease_in_cubic then ease_in_cubic(t)
139
+ when :ease_out_cubic then ease_out_cubic(t)
140
+ when :ease_in_out_cubic then ease_in_out_cubic(t)
141
+ when :ease_in_sine then ease_in_sine(t)
142
+ when :ease_out_sine then ease_out_sine(t)
143
+ when :ease_in_out_sine then ease_in_out_sine(t)
144
+ when :ease_in_expo then ease_in_expo(t)
145
+ when :ease_out_expo then ease_out_expo(t)
146
+ when :ease_in_out_expo then ease_in_out_expo(t)
147
+ when :ease_in_back then ease_in_back(t)
148
+ when :ease_out_back then ease_out_back(t)
149
+ when :ease_in_out_back then ease_in_out_back(t)
150
+ when :ease_in_elastic then ease_in_elastic(t)
151
+ when :ease_out_elastic then ease_out_elastic(t)
152
+ when :ease_in_bounce then ease_in_bounce(t)
153
+ when :ease_out_bounce then ease_out_bounce(t)
154
+ when :ease_in_out_bounce then ease_in_out_bounce(t)
155
+ when Proc then easing.call(t)
156
+ else linear(t)
157
+ end
158
+ end
159
+ end
160
+
161
+ class Keyframe
162
+ attr_reader :time, :value, :easing
163
+
164
+ def initialize(time, value, easing: :linear)
165
+ @time = time.to_f
166
+ @value = value
167
+ @easing = easing
168
+ end
169
+
170
+ def interpolate_to(other, t)
171
+ eased_t = Easing.apply(@easing, t)
172
+
173
+ case @value
174
+ when Numeric
175
+ @value + (other.value - @value) * eased_t
176
+ when Vec2
177
+ Vec2.new(
178
+ @value.x + (other.value.x - @value.x) * eased_t,
179
+ @value.y + (other.value.y - @value.y) * eased_t
180
+ )
181
+ when Vec3
182
+ Vec3.new(
183
+ @value.x + (other.value.x - @value.x) * eased_t,
184
+ @value.y + (other.value.y - @value.y) * eased_t,
185
+ @value.z + (other.value.z - @value.z) * eased_t
186
+ )
187
+ when Color
188
+ Color.rgba(
189
+ @value.r + (other.value.r - @value.r) * eased_t,
190
+ @value.g + (other.value.g - @value.g) * eased_t,
191
+ @value.b + (other.value.b - @value.b) * eased_t,
192
+ @value.a + (other.value.a - @value.a) * eased_t
193
+ )
194
+ when Array
195
+ @value.zip(other.value).map do |a, b|
196
+ a + (b - a) * eased_t
197
+ end
198
+ when Hash
199
+ @value.keys.each_with_object({}) do |key, result|
200
+ a = @value[key]
201
+ b = other.value[key]
202
+ result[key] = a + (b - a) * eased_t
203
+ end
204
+ else
205
+ t < 1.0 ? @value : other.value
206
+ end
207
+ end
208
+ end
209
+
210
+ class AnimationTrack
211
+ attr_reader :property, :keyframes
212
+
213
+ def initialize(property)
214
+ @property = property
215
+ @keyframes = []
216
+ end
217
+
218
+ def add_keyframe(time, value, easing: :linear)
219
+ @keyframes << Keyframe.new(time, value, easing: easing)
220
+ @keyframes.sort_by!(&:time)
221
+ self
222
+ end
223
+
224
+ def duration
225
+ return 0.0 if @keyframes.empty?
226
+
227
+ @keyframes.last.time
228
+ end
229
+
230
+ def sample(time)
231
+ return nil if @keyframes.empty?
232
+ return @keyframes.first.value if time <= @keyframes.first.time
233
+ return @keyframes.last.value if time >= @keyframes.last.time
234
+
235
+ prev_keyframe = nil
236
+ next_keyframe = nil
237
+
238
+ @keyframes.each_cons(2) do |a, b|
239
+ if time >= a.time && time < b.time
240
+ prev_keyframe = a
241
+ next_keyframe = b
242
+ break
243
+ end
244
+ end
245
+
246
+ return @keyframes.last.value unless prev_keyframe && next_keyframe
247
+
248
+ local_t = (time - prev_keyframe.time) / (next_keyframe.time - prev_keyframe.time)
249
+ prev_keyframe.interpolate_to(next_keyframe, local_t)
250
+ end
251
+ end
252
+
253
+ class AnimationClip
254
+ attr_reader :name, :tracks, :repeat_mode
255
+
256
+ def initialize(name, repeat_mode: :once)
257
+ @name = name
258
+ @tracks = {}
259
+ @repeat_mode = repeat_mode
260
+ end
261
+
262
+ def add_track(property)
263
+ @tracks[property] = AnimationTrack.new(property)
264
+ @tracks[property]
265
+ end
266
+
267
+ def get_track(property)
268
+ @tracks[property]
269
+ end
270
+
271
+ def duration
272
+ @tracks.values.map(&:duration).max || 0.0
273
+ end
274
+
275
+ def sample(time)
276
+ @tracks.transform_values { |track| track.sample(time) }
277
+ end
278
+
279
+ def sample_property(property, time)
280
+ track = @tracks[property]
281
+ track&.sample(time)
282
+ end
283
+
284
+ def type_name
285
+ 'AnimationClip'
286
+ end
287
+ end
288
+
289
+ class AnimationPlayer
290
+ attr_reader :current_clip, :elapsed, :speed, :paused
291
+
292
+ def initialize
293
+ @clips = {}
294
+ @current_clip = nil
295
+ @elapsed = 0.0
296
+ @speed = 1.0
297
+ @paused = false
298
+ @on_finish_callbacks = []
299
+ @on_loop_callbacks = []
300
+ end
301
+
302
+ def add_clip(clip)
303
+ @clips[clip.name] = clip
304
+ self
305
+ end
306
+
307
+ def play(clip_name, restart: false)
308
+ if @current_clip != clip_name || restart
309
+ @current_clip = clip_name
310
+ @elapsed = 0.0
311
+ end
312
+ @paused = false
313
+ self
314
+ end
315
+
316
+ def pause
317
+ @paused = true
318
+ self
319
+ end
320
+
321
+ def resume
322
+ @paused = false
323
+ self
324
+ end
325
+
326
+ def stop
327
+ @current_clip = nil
328
+ @elapsed = 0.0
329
+ @paused = false
330
+ self
331
+ end
332
+
333
+ def set_speed(speed)
334
+ @speed = speed
335
+ self
336
+ end
337
+
338
+ def seek(time)
339
+ @elapsed = time.clamp(0.0, duration)
340
+ self
341
+ end
342
+
343
+ def on_finish(&block)
344
+ @on_finish_callbacks << block
345
+ self
346
+ end
347
+
348
+ def on_loop(&block)
349
+ @on_loop_callbacks << block
350
+ self
351
+ end
352
+
353
+ def update(delta)
354
+ return unless @current_clip && !@paused
355
+
356
+ clip = @clips[@current_clip]
357
+ return unless clip
358
+
359
+ @elapsed += delta * @speed
360
+ clip_duration = clip.duration
361
+
362
+ return if clip_duration <= 0.0
363
+
364
+ case clip.repeat_mode
365
+ when :once
366
+ if @elapsed >= clip_duration
367
+ @elapsed = clip_duration
368
+ @on_finish_callbacks.each(&:call)
369
+ @current_clip = nil
370
+ end
371
+ when :loop
372
+ while @elapsed >= clip_duration
373
+ @elapsed -= clip_duration
374
+ @on_loop_callbacks.each(&:call)
375
+ end
376
+ when :ping_pong
377
+ cycle = (@elapsed / clip_duration).to_i
378
+ local_t = @elapsed % clip_duration
379
+ @elapsed = cycle.odd? ? clip_duration - local_t : local_t
380
+ end
381
+ end
382
+
383
+ def sample
384
+ return {} unless @current_clip
385
+
386
+ clip = @clips[@current_clip]
387
+ return {} unless clip
388
+
389
+ clip.sample(@elapsed)
390
+ end
391
+
392
+ def sample_property(property)
393
+ return nil unless @current_clip
394
+
395
+ clip = @clips[@current_clip]
396
+ return nil unless clip
397
+
398
+ clip.sample_property(property, @elapsed)
399
+ end
400
+
401
+ def duration
402
+ return 0.0 unless @current_clip
403
+
404
+ @clips[@current_clip]&.duration || 0.0
405
+ end
406
+
407
+ def progress
408
+ d = duration
409
+ return 0.0 if d <= 0.0
410
+
411
+ @elapsed / d
412
+ end
413
+
414
+ def playing?
415
+ @current_clip && !@paused
416
+ end
417
+
418
+ def finished?
419
+ @current_clip.nil? && @elapsed > 0.0
420
+ end
421
+
422
+ def type_name
423
+ 'AnimationPlayer'
424
+ end
425
+ end
426
+
427
+ class Tween
428
+ attr_reader :from, :to, :duration, :elapsed, :easing, :repeat_mode
429
+
430
+ def initialize(from:, to:, duration:, easing: :linear, repeat_mode: :once, delay: 0.0)
431
+ @from = from
432
+ @to = to
433
+ @duration = duration.to_f
434
+ @easing = easing
435
+ @repeat_mode = repeat_mode
436
+ @delay = delay.to_f
437
+ @elapsed = 0.0
438
+ @started = false
439
+ @finished = false
440
+ @on_update_callbacks = []
441
+ @on_complete_callbacks = []
442
+ end
443
+
444
+ def update(delta)
445
+ return if @finished
446
+
447
+ if !@started
448
+ @delay -= delta
449
+ if @delay <= 0.0
450
+ @started = true
451
+ @elapsed = -@delay
452
+ else
453
+ return
454
+ end
455
+ else
456
+ @elapsed += delta
457
+ end
458
+
459
+ if @elapsed >= @duration
460
+ case @repeat_mode
461
+ when :once
462
+ @elapsed = @duration
463
+ @finished = true
464
+ @on_complete_callbacks.each(&:call)
465
+ when :loop
466
+ @elapsed = @elapsed % @duration
467
+ when :ping_pong
468
+ cycle = (@elapsed / @duration).to_i
469
+ local_t = @elapsed % @duration
470
+ @elapsed = cycle.odd? ? @duration - local_t : local_t
471
+ end
472
+ end
473
+
474
+ @on_update_callbacks.each { |cb| cb.call(current_value) }
475
+ end
476
+
477
+ def current_value
478
+ t = progress
479
+ Keyframe.new(0, @from, easing: @easing).interpolate_to(Keyframe.new(1, @to), t)
480
+ end
481
+
482
+ def progress
483
+ return 0.0 if @duration <= 0.0
484
+
485
+ (@elapsed / @duration).clamp(0.0, 1.0)
486
+ end
487
+
488
+ def finished?
489
+ @finished
490
+ end
491
+
492
+ def started?
493
+ @started
494
+ end
495
+
496
+ def reset
497
+ @elapsed = 0.0
498
+ @started = false
499
+ @finished = false
500
+ self
501
+ end
502
+
503
+ def on_update(&block)
504
+ @on_update_callbacks << block
505
+ self
506
+ end
507
+
508
+ def on_complete(&block)
509
+ @on_complete_callbacks << block
510
+ self
511
+ end
512
+
513
+ def type_name
514
+ 'Tween'
515
+ end
516
+ end
517
+
518
+ class TweenSequence
519
+ def initialize
520
+ @tweens = []
521
+ @current_index = 0
522
+ @finished = false
523
+ end
524
+
525
+ def add(tween)
526
+ @tweens << tween
527
+ self
528
+ end
529
+
530
+ def update(delta)
531
+ return if @finished || @tweens.empty?
532
+
533
+ current_tween = @tweens[@current_index]
534
+ current_tween.update(delta)
535
+
536
+ if current_tween.finished?
537
+ @current_index += 1
538
+ if @current_index >= @tweens.size
539
+ @finished = true
540
+ end
541
+ end
542
+ end
543
+
544
+ def current_value
545
+ return nil if @tweens.empty?
546
+
547
+ @tweens[@current_index]&.current_value
548
+ end
549
+
550
+ def finished?
551
+ @finished
552
+ end
553
+
554
+ def reset
555
+ @tweens.each(&:reset)
556
+ @current_index = 0
557
+ @finished = false
558
+ self
559
+ end
560
+
561
+ def type_name
562
+ 'TweenSequence'
563
+ end
564
+ end
565
+
566
+ class TweenGroup
567
+ def initialize
568
+ @tweens = []
569
+ end
570
+
571
+ def add(tween)
572
+ @tweens << tween
573
+ self
574
+ end
575
+
576
+ def update(delta)
577
+ @tweens.each { |t| t.update(delta) }
578
+ end
579
+
580
+ def values
581
+ @tweens.map(&:current_value)
582
+ end
583
+
584
+ def finished?
585
+ @tweens.all?(&:finished?)
586
+ end
587
+
588
+ def reset
589
+ @tweens.each(&:reset)
590
+ self
591
+ end
592
+
593
+ def type_name
594
+ 'TweenGroup'
595
+ end
596
+ end
597
+ end