or2d 0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 69d868c6eb2d8bf4781708c14470be61e2b51081c6e4769005af19dfffdbe82d
4
+ data.tar.gz: 3427253c8d35fd728edc4b7a5c9841d8b0f58ea13b9b45ac59eea36596625d1e
5
+ SHA512:
6
+ metadata.gz: 44d7d7cd85cf7896666e687600276d12f0e271919d84a796fec627db5e3184e7ffb9304bc7b69f0456a7df73e5a7e42660e3e8fde402ee972e2056a978b4246a
7
+ data.tar.gz: 83b07d35364f4fdeebef9a791cbe6d39b5ea36a956940c9291d00eb97537455fde9c3d2becb9b1fd17bb4714c774e9970e59ad14bd10e1458c449f4c3b9857a2
@@ -0,0 +1,60 @@
1
+ module OR2D
2
+ # The Animation class represents a single animation. It is used to animate entities in the game.
3
+ class Animation
4
+
5
+ # @!attribute [r] playing
6
+ # @return [Boolean] whether or not the animation is currently playing
7
+ attr_accessor :playing
8
+
9
+ # @!attribute [r] target
10
+ # @return [Integer] the ID of the entity to animate
11
+ attr_reader :target
12
+
13
+ # @!attribute [r] cache
14
+ # @return [Hash] the cache of the animation
15
+ attr_reader :cache
16
+
17
+ # Constructs a new animation.
18
+ # @param entity_id [Integer] the ID of the entity to animate
19
+ # @param _ [Proc] the block to execute
20
+ def initialize(entity_id, &_)
21
+ @target = entity_id
22
+ @playing = true
23
+ @cache = {}
24
+ @worker = Fiber.new do |animation|
25
+ loop do
26
+ break unless playing?
27
+
28
+ block_given? && playing? ? yield(animation) : complete
29
+ rescue StandardError => e
30
+ puts "An error occurred while animating entity #{entity_id}: #{e.message}"
31
+ puts e.backtrace if OR2D.debug?
32
+ ensure
33
+ Fiber.yield if playing?
34
+ end
35
+ end
36
+ end
37
+
38
+ # Advances a single frame of the animation.
39
+ def resume
40
+ @worker.resume(self) if playing?
41
+ end
42
+
43
+ # Completes the animation.
44
+ def complete
45
+ @playing = false
46
+ end
47
+
48
+ # Is the animation complete?
49
+ # @return [Boolean] whether or not the animation is complete
50
+ def complete?
51
+ !@playing || @playing == false || !@worker.alive?
52
+ end
53
+
54
+ # Is the animation playing?
55
+ # @return [Boolean] whether or not the animation is playing
56
+ def playing?
57
+ @playing == true && @worker.alive?
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,160 @@
1
+ module OR2D::Animations
2
+
3
+ # A module that provides functions for animating the layers of a Composite object.
4
+ module CompositeAnimations
5
+
6
+ # Fade the a single layer of a Composite object, or all layers if no target is specified.
7
+ # @param speed [Float] the speed at which to fade
8
+ # @param target [Symbol, Integer] the index of the layer to fade or :all to fade all layers
9
+ def fade(speed: nil, target: :all)
10
+ if target == :all
11
+ @layers.each_key do |layer|
12
+ OR2D.game.entities[layer].fade(speed: speed)
13
+ end
14
+ else
15
+ OR2D.game.entities[@layers.keys[target]].fade(speed: speed)
16
+ end
17
+ end
18
+
19
+ # Fade in a single layer of a Composite object, or all layers if no target is specified.
20
+ # @param speed [Float] the speed at which to fade in
21
+ # @param target [Symbol, Integer] the index of the layer to fade in or :all to fade in all layers
22
+ def fade_in(speed: nil, target: :all)
23
+ if target == :all
24
+ @layers.each_key do |layer|
25
+ OR2D.game.entities[layer].fade_in(speed: speed)
26
+ end
27
+ else
28
+ OR2D.game.entities[@layers.keys[target]].fade_in(speed: speed)
29
+ end
30
+ end
31
+
32
+ # Fade out a single layer of a Composite object, or all layers if no target is specified.
33
+ # @param speed [Float] the speed at which to fade out
34
+ # @param target [Symbol, Integer] the index of the layer to fade out or :all to fade out all layers
35
+ def fade_out(speed: nil, target: :all)
36
+ if target == :all
37
+ @layers.each_key do |layer|
38
+ OR2D.game.entities[layer].fade_out(speed: speed)
39
+ end
40
+ else
41
+ OR2D.game.entities[@layers.keys[target]].fade_out(speed: speed)
42
+ end
43
+ end
44
+
45
+ # Rotate a single layer of a Composite object, or all layers if no target is specified.
46
+ # @param speed [Float] the speed at which to rotate
47
+ # @param angle [Float] the angle at which to rotate
48
+ # @param clockwise [Boolean] whether to rotate clockwise or counter-clockwise
49
+ # @param target [Symbol, Integer] the index of the layer to rotate or :all to rotate all layers
50
+ def rotation(speed: 0.1, angle: 360, clockwise: true, target: :all)
51
+ if target == :all
52
+ @layers.each_key do |layer|
53
+ OR2D.game.entities[layer].rotation(speed: speed, angle: angle, clockwise: clockwise)
54
+ end
55
+ else
56
+ OR2D.game.entities[@layers.keys[target]].rotation(speed: speed, angle: angle, clockwise: clockwise)
57
+ end
58
+ end
59
+
60
+ # Grow a single layer of a Composite object, or all layers if no target is specified.
61
+ # @param factor [Float] the factor by which to grow
62
+ # @param delay [Integer] the delay before growing
63
+ # @param target [Symbol, Integer] the index of the layer to grow or :all to grow all layers
64
+ def grow(factor: 1.0, delay: 100, target: :all)
65
+ if target == :all
66
+ @layers.each_key do |layer|
67
+ OR2D.game.entities[layer].grow(factor: factor, delay: delay)
68
+ end
69
+ else
70
+ OR2D.game.entities[@layers.keys[target]].grow(factor: factor, delay: delay)
71
+ end
72
+ end
73
+
74
+ # Grow the text of a single layer of a Composite object, or all layers if no target is specified.
75
+ # @param factor [Float] the factor by which to grow the text
76
+ # @param delay [Integer] the delay before growing the text
77
+ # @param target [Symbol, Integer] the index of the layer to grow the text or :all to grow the text of all layers
78
+ def grow_text_composite(factor: nil, delay: nil, target: :all)
79
+ if target == :all
80
+ @layers.each_key do |layer|
81
+ OR2D.game.entities[layer].grow_text(factor: factor, delay: delay)
82
+ end
83
+ else
84
+ OR2D.game.entities[@layers.keys[target]].grow_text(factor: factor, delay: delay)
85
+ end
86
+ end
87
+
88
+ # Grow the entity of a single layer of a Composite object, or all layers if no target is specified.
89
+ # @param factor [Float] the factor by which to grow the entity
90
+ # @param delay [Integer] the delay before growing the entity
91
+ # @param target [Symbol, Integer] the index of the layer to grow the entity or :all to grow the entity of all layers
92
+ def grow_entity(factor: nil, delay: nil, target: :all)
93
+ if target == :all
94
+ @layers.each_key do |layer|
95
+ OR2D.game.entities[layer].grow_entity(factor: factor, delay: delay)
96
+ end
97
+ else
98
+ OR2D.game.entities[@layers.keys[target]].grow_entity(factor: factor, delay: delay)
99
+ end
100
+ end
101
+
102
+ # Shrink a single layer of a Composite object, or all layers if no target is specified.
103
+ # @param factor [Float] the factor by which to shrink
104
+ # @param delay [Integer] the delay before shrinking
105
+ # @param target [Symbol, Integer] the index of the layer to shrink or :all to shrink all layers
106
+ def shrink(factor: 1.0, delay: 100, target: :all)
107
+ if target == :all
108
+ @layers.each_key do |layer|
109
+ OR2D.game.entities[layer].shrink(factor: factor, delay: delay)
110
+ end
111
+ else
112
+ OR2D.game.entities[@layers.keys[target]].shrink(factor: factor, delay: delay)
113
+ end
114
+ end
115
+
116
+ # Shrink the text of a single layer of a Composite object, or all layers if no target is specified.
117
+ # @param factor [Float] the factor by which to shrink the text
118
+ # @param delay [Integer] the delay before shrinking the text
119
+ # @param target [Symbol, Integer] the index of the layer to shrink the text or :all to shrink the text of all layers
120
+ def shrink_text(factor: nil, delay: nil, target: :all)
121
+ if target == :all
122
+ @layers.each_key do |layer|
123
+ OR2D.game.entities[layer].shrink_text(factor: factor, delay: delay)
124
+ end
125
+ else
126
+ OR2D.game.entities[@layers.keys[target]].shrink_text(factor: factor, delay: delay)
127
+ end
128
+ end
129
+
130
+ # Shrinks the entity of a single layer of a Composite object, or all layers if no target is specified.
131
+ # @param factor [Float] the factor by which to shrink the entity
132
+ # @param delay [Integer] the delay before shrinking the entity
133
+ # @param target [Symbol, Integer] the index of the layer to shrink the entity or :all to shrink the entity of all layers
134
+ def shrink_entity(factor: nil, delay: nil, target: nil)
135
+ if target == :all
136
+ @layers.each_key do |layer|
137
+ OR2D.game.entities[layer].shrink_entity(factor: factor, delay: delay)
138
+ end
139
+ else
140
+ OR2D.game.entities[@layers.keys[target]].shrink_entity(factor: factor, delay: delay)
141
+ end
142
+ end
143
+
144
+ # Shake a single layer of a Composite object, or all layers if no target is specified.
145
+ # @param duration [Integer] the duration of the shake
146
+ # @param magnitude [Integer] the magnitude of the shake
147
+ # @param target [Symbol, Integer] the index of the layer to shake or :all to shake all layers
148
+ # @example Shake all layers of a Composite object
149
+ def shake(target: :all, duration: 100, magnitude: 10)
150
+ if target == :all
151
+ @layers.each_key do |layer|
152
+ OR2D.game.entities[layer].shake(duration: duration, magnitude: magnitude)
153
+ end
154
+ else
155
+ OR2D.game.entities[@layers.keys[target]].shake(duration: duration, magnitude: magnitude)
156
+ end
157
+ end
158
+ end
159
+
160
+ end
@@ -0,0 +1,221 @@
1
+ module OR2D::Animations
2
+ # A module that contains methods for animating entities.
3
+ module EntityAnimations
4
+ # Fades an Animatable object in and out.
5
+ # @param speed [Float] the speed at which to fade the object in and out
6
+ # @param entity [OR2D::Entity] the entity to fade in and out
7
+ def fade(speed: 0.1, entity: self)
8
+ if entity.resource.color.opacity.zero? || entity.resource.color.opacity.negative?
9
+ fade_in(entity: entity, speed: speed)
10
+ else
11
+ fade_out(entity: entity, speed: speed)
12
+ end
13
+ end
14
+
15
+ # Fades an Animatable object out.
16
+ # @param speed [Float] the speed at which to fade the object out
17
+ # @param entity [OR2D::Entity] the entity to fade out
18
+ def fade_out(speed: 0.1, entity: self)
19
+ OR2D.game.animate(entity.id) do |animation|
20
+ if entity.resource.color.opacity.zero? || entity.resource.color.opacity.negative?
21
+ animation.complete
22
+ else
23
+ entity.resource.color.opacity -= speed
24
+ end
25
+ end
26
+ end
27
+
28
+ # Fades an Animatable object in.
29
+ # @param speed [Float] the speed at which to fade the object in
30
+ # @param entity [OR2D::Entity] the entity to fade in
31
+ def fade_in(speed: 0.1, entity: self)
32
+ OR2D.game.animate(entity.id) do |animation|
33
+ if entity.resource.color.opacity >= 1.0
34
+ animation.complete
35
+ else
36
+ entity.resource.color.opacity += speed
37
+ end
38
+ end
39
+ end
40
+
41
+ # Rotates an Animatable object.
42
+ # @param speed [Float] the speed at which to rotate the object
43
+ # @param angle [Integer] the angle at which to stop rotating the object
44
+ # @param clockwise [Boolean] whether or not to rotate the object clockwise
45
+ # @param entity [OR2D::Entity] the entity to rotate
46
+ def rotation(speed: 1.0, angle: 360, clockwise: true, entity: self)
47
+ OR2D.game.animate(entity.id) do |animation|
48
+ if clockwise
49
+ entity.resource.rotate += speed
50
+ else
51
+ entity.resource.rotate -= speed
52
+ end
53
+
54
+ case angle
55
+ when Range then animation.complete if angle.include?(entity.resource.rotate)
56
+ when Integer then animation.complete if (entity.resource.rotate % angle).zero?
57
+ else rotation(speed: speed, clockwise: clockwise, entity: entity)
58
+ end
59
+ end
60
+ end
61
+
62
+ # Grows an Animatable object.
63
+ # @param factor [Float] the factor at which to grow the object
64
+ # @param entity [OR2D::Entity] the entity to grow
65
+ # @param delay [Integer] the delay at which to grow the object
66
+ # @note Using this animation multiple times in conjunction with multiple `shrink` animations can end up preventing animations from completing, leaving the target Entity in a state where it is not animatable via growth or shrinkage.
67
+ def grow(factor: 1.0, delay: 100, entity: self)
68
+ if entity.resource.is_a?(Ruby2D::Text)
69
+ grow_text(factor: factor, delay: delay, entity: entity)
70
+ else
71
+ grow_entity(factor: factor, delay: delay, entity: entity)
72
+ end
73
+ end
74
+
75
+ # Grows an Animatable Entity object. This is accomplished by changing the width and height of the Entity.
76
+ # @param factor [Float] the factor at which to grow the object
77
+ # @param entity [OR2D::Entity] the entity to grow
78
+ # @param delay [Integer] the delay at which to grow the object
79
+ # @note Using this animation multiple times in conjunction with multiple `shrink` animations can end up preventing animations from completing, leaving the target Entity in a state where it is not animatable via growth or shrinkage.
80
+ def grow_entity(factor: 1.0, delay: 100, entity: self)
81
+ OR2D.game.animate(entity.id) do |animation|
82
+ animation.cache[:delay] ||= delay
83
+ animation.cache[:end_width] ||= entity.resource.width + (entity.resource.width * factor)
84
+ animation.cache[:end_height] ||= entity.resource.height + (entity.resource.height * factor)
85
+ animation.cache[:increment_width] ||= ((animation.cache[:end_width] - entity.resource.width) / animation.cache[:delay]).round(3)
86
+ animation.cache[:increment_height] ||= ((animation.cache[:end_height] - entity.resource.height) / animation.cache[:delay]).round(3)
87
+
88
+ if animation.cache[:delay].positive?
89
+ if entity.resource.width >= animation.cache[:end_width] &&
90
+ entity.resource.height >= animation.cache[:end_height]
91
+ animation.complete
92
+ else
93
+ entity.resource.width += animation.cache[:increment_width] if entity.resource.width != animation.cache[:end_width]
94
+ entity.resource.height += animation.cache[:increment_height] if entity.resource.height != animation.cache[:end_height]
95
+ end
96
+ else
97
+ entity.resource.width = animation.cache[:end_width]
98
+ entity.resource.height = animation.cache[:end_height]
99
+ animation.complete
100
+ end
101
+ end
102
+ end
103
+
104
+ # Grows an Animatable Text object. This is accomplished by changing the size of the Text.
105
+ # @param factor [Float] the factor at which to grow the object
106
+ # @param entity [OR2D::Entity] the entity to grow
107
+ # @param delay [Integer] the delay at which to grow the object
108
+ # @note Using this animation multiple times in conjunction with multiple `shrink_text` animations can end up preventing animations from completing, leaving the target Entity in a state where it is not animatable via growth or shrinkage.
109
+ def grow_text(factor: 1.0, delay: 100, entity: self)
110
+ OR2D.game.animate(entity.id) do |animation|
111
+ animation.cache[:end_size] ||= entity.resource.size + (entity.resource.size * factor)
112
+ animation.cache[:delay] ||= delay
113
+ animation.cache[:increment] ||= ((animation.cache[:end_size] - entity.resource.size) / animation.cache[:delay]).round(3)
114
+
115
+
116
+ if animation.cache[:delay].positive?
117
+ if entity.resource.size >= animation.cache[:end_size]
118
+ animation.complete
119
+ else
120
+ entity.resource.size += animation.cache[:increment]
121
+ end
122
+ else
123
+ entity.resource.size = animation.cache[:end_size]
124
+ animation.complete
125
+ end
126
+ end
127
+ end
128
+
129
+ # Shrinks an Animatable object.
130
+ # @param factor [Float] the factor at which to shrink the object
131
+ # @param entity [OR2D::Entity] the entity to shrink
132
+ # @param delay [Integer] the delay at which to shrink the object
133
+ # @note Using this animation multiple times in conjunction with multiple `grow` animations can end up preventing animations from completing, leaving the target Entity in a state where it is not animatable via growth or shrinkage.
134
+ def shrink(factor: 1.0, delay: 100, entity: self)
135
+ if entity.resource.is_a?(Ruby2D::Text)
136
+ shrink_text(factor: factor, delay: delay, entity: entity)
137
+ else
138
+ shrink_entity(factor: factor, delay: delay, entity: entity)
139
+ end
140
+ end
141
+
142
+ # Shrinks an Animatable Entity object. This is accomplished by changing the width and height of the Entity.
143
+ # @note Using this animation multiple times in conjunction with multiple `grow_entity` animations can end up preventing animations from completing, leaving the target Entity in a state where it is not animatable via growth or shrinkage.
144
+ # @param factor [Float] the factor at which to shrink the object
145
+ # @param entity [OR2D::Entity] the entity to shrink
146
+ def shrink_entity(factor: 1.0, delay: 100, entity: self)
147
+ OR2D.game.animate(entity.id) do |animation|
148
+ animation.cache[:delay] ||= delay
149
+ animation.cache[:end_width] ||= entity.resource.width - (entity.resource.width * factor)
150
+ animation.cache[:end_height] ||= entity.resource.height - (entity.resource.height * factor)
151
+ animation.cache[:increment_width] ||= ((entity.resource.width - animation.cache[:end_width]) / delay).round(3)
152
+ animation.cache[:increment_height] ||= ((entity.resource.height - animation.cache[:end_height]) / delay).round(3)
153
+
154
+
155
+ if animation.cache[:delay].positive?
156
+ if entity.resource.width <= animation.cache[:end_width] &&
157
+ entity.resource.height <= animation.cache[:end_height]
158
+ animation.complete
159
+ else
160
+ entity.resource.width -= animation.cache[:increment_width] if entity.resource.width != animation.cache[:end_width]
161
+ entity.resource.height -= animation.cache[:increment_height] if entity.resource.height != animation.cache[:end_height]
162
+ end
163
+ else
164
+ entity.resource.width = animation.cache[:end_width]
165
+ entity.resource.height = animation.cache[:end_height]
166
+ animation.complete
167
+ end
168
+ end
169
+ end
170
+
171
+
172
+ # Shrinks an Animatable shape object. This is accomplished by changing the size of the text.
173
+ # @note Using this animation multiple times in conjunction with multiple `grow_text` animations can end up preventing animations from completing, leaving the target Entity in a state where it is not animatable via growth or shrinkage.
174
+ # @param factor [Float] the factor at which to shrink the object
175
+ # @param entity [OR2D::Entity] the entity to shrink
176
+ def shrink_text(factor: 1.0, delay: 100, entity: self)
177
+ OR2D.game.animate(entity.id) do |animation|
178
+ animation.cache[:end_size] ||= entity.resource.size - (entity.resource.size * factor)
179
+ animation.cache[:delay] ||= delay
180
+ animation.cache[:increment] ||= ((entity.resource.size - animation.cache[:end_size]) / animation.cache[:delay]).round(3)
181
+
182
+ if animation.cache[:delay].positive?
183
+ if entity.resource.size <= animation.cache[:end_size]
184
+ animation.complete
185
+ else
186
+ entity.resource.size -= animation.cache[:increment]
187
+ animation.cache[:delay] -= 1
188
+ end
189
+ else
190
+ entity.resource.size = animation.cache[:end_size]
191
+ animation.complete
192
+ end
193
+ end
194
+ end
195
+
196
+ # Shakes an Animatable object. This is accomplished by changing the x and y coordinates of the object.
197
+ # @param duration [Integer] the duration of the shake
198
+ # @param magnitude [Integer] the magnitude of the shake
199
+ # @param entity [OR2D::Entity] the entity to shake
200
+ def shake(duration: 100, magnitude: 10, entity: self)
201
+ OR2D.game.animate(entity.id) do |animation|
202
+ animation.cache[:duration] ||= duration
203
+ animation.cache[:magnitude] ||= magnitude
204
+ animation.cache[:x] ||= entity.x
205
+ animation.cache[:y] ||= entity.y
206
+ animation.cache[:increment] ||= ((animation.cache[:magnitude] / animation.cache[:duration]) * 0.001).round(3)
207
+ jitter = rand(-animation.cache[:magnitude]..animation.cache[:magnitude])
208
+ if animation.cache[:duration].positive?
209
+ entity.x = animation.cache[:x] + animation.cache[:magnitude] + jitter
210
+ entity.y = animation.cache[:y] + animation.cache[:magnitude] + jitter
211
+ animation.cache[:duration] -= 1
212
+ animation.cache[:magnitude] -= animation.cache[:increment]
213
+ else
214
+ entity.x = animation.cache[:x]
215
+ entity.y = animation.cache[:y]
216
+ animation.complete
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,73 @@
1
+ module OR2D
2
+ # A Composite object behaves like a collection of Entity objects.
3
+ class Composite
4
+
5
+ # @!attribute [r] layers
6
+ # @return [Hash] the layers of the Composite object
7
+ attr_reader :layers
8
+
9
+ # @!attribute [r] id
10
+ # @return [String] the identifier of the Composite object
11
+ attr_reader :id
12
+
13
+ # Constructs a new Composite object.
14
+ def initialize(id = "Composite_#{SecureRandom.uuid}")
15
+ @id = id
16
+ @layers = {}
17
+ end
18
+
19
+ # Add a layer to the Composite object.
20
+ # @param type [Symbol] the type of layer to add
21
+ # @param options [Hash] the options used to construct the layer
22
+ # @param entity [OR2D::Entity] an optional Entity object to add if the layer type is :entity
23
+ def add_layer(type, options, entity: nil)
24
+ entity = if type == :entity
25
+ entity
26
+ else
27
+ OR2D::Entity.new(type, options)
28
+ end
29
+
30
+ @layers[entity.id] = { show: options[:show].nil? ? true : options[:show] }
31
+ entity.id
32
+ end
33
+
34
+ # Remove a layer from the Composite object.
35
+ # @param id [String] the id of the Entity object to remove
36
+ def remove_layer(id)
37
+ @layers.delete(id)
38
+ OR2D.game.entities[id]&.destroy
39
+ OR2D.game.remove_entity(id)
40
+ end
41
+
42
+ # Show the Composite object.
43
+ def show
44
+ @layers.each do |id, properties|
45
+ OR2D.game.entities[id].show if properties[:show]
46
+ end
47
+ end
48
+
49
+ # Hide the Composite object.
50
+ def hide
51
+ @layers.each_key do |id|
52
+ OR2D.game.entities[id].hide
53
+ end
54
+ end
55
+
56
+ # Toggle the visibility of a specific layer.
57
+ # @param id [String] the id of the layer to toggle
58
+ def toggle(id)
59
+ @layers[id][:show] = !@layers[id][:show]
60
+ @layers[id][:show] ? OR2D.game.entities[id].show : OR2D.game.entities[id].hide
61
+ end
62
+
63
+ # Destroys the Composite object and all of its Entity objects.
64
+ def destroy
65
+ @layers.each_key do |id|
66
+ next if OR2D.game.entities[id].nil?
67
+
68
+ OR2D.game.entities[id].destroy
69
+ OR2D.game.remove_entity(id)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,97 @@
1
+ module OR2D::Composites
2
+ # The Composite Projectile class is used to create a composite projectile object.
3
+ class Projectile < OR2D::Composite
4
+
5
+ # Construct a new Projectile object.
6
+ # @param options [Hash] the options used to construct the Projectile object
7
+ # @option options [Hash] :asset the options used to construct the Asset object
8
+ # @option options [Float] :angle the angle of the Projectile object
9
+ # @option options [Hash] :direction the direction of the Projectile object
10
+ # @option options [Float] :radius the radius of the Projectile object
11
+ # @option options [Float] :velocity the velocity of the Projectile object
12
+ # @option options [Float] :time_step the time step of the Projectile object
13
+ # @option options [Float] :stepping the time step of the Projectile object
14
+ # @option options [Array] :gravity_points the gravity points of the Projectile object
15
+ def initialize(options)
16
+ super(options[:id] || "ProjectileComposite_#{SecureRandom.uuid}")
17
+ @asset_id = add_layer(options[:asset][:type], options[:asset][:options].merge(show: true)) if options.key?(:asset)
18
+ @angle = options[:angle] || 45
19
+ @direction = { x: Math.cos(@angle * Math::PI / 180),
20
+ y: Math.sin(@angle * Math::PI / 180) }
21
+ @radius = options[:radius] || 360
22
+ @velocity = options[:velocity] || 15
23
+ @velocity_y = @velocity * @direction[:y]
24
+ @time_step = options[:time_step] || options[:stepping] || 0.1
25
+ @elapsed = 0
26
+ @points = { line: {}, gravity: options[:gravity_points] || [] }
27
+ @gravity = @points[:gravity].first[:value] || 0
28
+ end
29
+
30
+ # Update the Projectile object.
31
+ def update
32
+ @elapsed += @time_step
33
+ update_gravity
34
+
35
+ @layers.each_key do |id|
36
+ OR2D.game.entities[id].x += Math.cos(@angle * Math::PI / 180) * @velocity * @direction[:x] * @time_step
37
+ OR2D.game.entities[id].y -= Math.sin(@angle * Math::PI / 180) * @velocity * @direction[:y] * @time_step - 0.5 * @gravity * @time_step**2
38
+ @velocity_y -= @gravity * @time_step
39
+ @points[:line][id] ||= []
40
+ @points[:line][id] << [OR2D.game.entities[id].x, OR2D.game.entities[id].y]
41
+ end
42
+ end
43
+
44
+ # Generate a random set of gravity points.
45
+ # @param stepping [Integer] the number of gravity points to generate
46
+ # @param gravity_minimum [Float] the minimum gravity value
47
+ # @param gravity_maximum [Float] the maximum gravity value
48
+ # @param interval [Float] the interval between gravity points
49
+ # @return [Array] the generated gravity points
50
+ def self.gravity_points(stepping: 3, gravity_minimum: 5, gravity_maximum: 100, interval: 0.5)
51
+ gravity_points = []
52
+ stepping.times do |i|
53
+ gravity_points << { time: i * interval, value: gravity_minimum + rand * (gravity_maximum - gravity_minimum) }
54
+ end
55
+
56
+ gravity_points
57
+ end
58
+
59
+ # Convert radians to degrees.
60
+ # @param radians [Float] the radians to convert
61
+ # @return [Float] the converted radians
62
+ def self.rad_to_deg(radians)
63
+ radians * 360.0 / Math::PI
64
+ end
65
+
66
+ # Destroy the Projectile object.
67
+ def destroy
68
+ @layers.each_key do |id|
69
+ OR2D.game.entities[id].destroy
70
+ OR2D.game.remove_entity(id)
71
+ end
72
+ end
73
+
74
+ # Is the Projectile object out of bounds?
75
+ # @return [Boolean] whether the Projectile object is out of bounds
76
+ def out_of_bounds?
77
+ if @layers.any? do |id, _|
78
+ OR2D.game.entities[id].x.negative? ||
79
+ OR2D.game.entities[id].x > OR2D.game.window.get(:width) ||
80
+ OR2D.game.entities[id].y.negative? ||
81
+ OR2D.game.entities[id].y > OR2D.game.window.get(:height)
82
+ end
83
+ true
84
+ else
85
+ false
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def update_gravity
92
+ @points[:gravity].each do |point|
93
+ @gravity = point[:value] if @elapsed >= point[:time]
94
+ end
95
+ end
96
+ end
97
+ end