or2d 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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