or2d 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/or2d/animation.rb +60 -0
- data/lib/or2d/animations/composite_animations.rb +160 -0
- data/lib/or2d/animations/entity_animations.rb +221 -0
- data/lib/or2d/composite.rb +73 -0
- data/lib/or2d/composites/projectile.rb +97 -0
- data/lib/or2d/composites/sprite.rb +245 -0
- data/lib/or2d/composites/text.rb +167 -0
- data/lib/or2d/console.rb +199 -0
- data/lib/or2d/entity.rb +78 -0
- data/lib/or2d/instance.rb +186 -0
- data/lib/or2d/patches/music.rb +30 -0
- data/lib/or2d/patches/renderable.rb +5 -0
- data/lib/or2d/patches/sound.rb +18 -0
- data/lib/or2d/patches/sprite.rb +20 -0
- data/lib/or2d/patches/window.rb +9 -0
- data/lib/or2d/resource.rb +182 -0
- data/lib/or2d/scene.rb +160 -0
- data/lib/or2d/scenes/placeholder_scene.rb +70 -0
- data/lib/or2d/version.rb +50 -0
- data/lib/or2d.rb +94 -0
- metadata +146 -0
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
|