minigl 2.4.3 → 2.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +8 -3
- data/lib/minigl/particles.rb +313 -0
- data/lib/minigl.rb +1 -0
- data/test/particles_game.rb +70 -0
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6078cf1c0aafd790460ec6b7723dff865c0a2e78a8359fd8734cd8a6a07aec60
|
4
|
+
data.tar.gz: 61be9170370e9b912a7c3a7087ca145de121f36b005abf62bfe193ca1dc29ec3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68d1060caaf63cfe944a924b692c38680833a15fa49412060295904e630d8b73e5b6342abf4febf1ff6d9324e88f61e0a34fa5c9014e70b376cafa10402e0df5
|
7
|
+
data.tar.gz: ff9db71adca879e4ec8b7044c1864477befb45eff207cdd0dc2b1ff3d97d4f6f3f0255e1d61e6705293d7ab3d490c484543f40cd861545cf1936b5b09d2da590
|
data/README.md
CHANGED
@@ -10,6 +10,7 @@ It provides the following features:
|
|
10
10
|
* UI (text, buttons, text fields, drop-down lists, progress bars)
|
11
11
|
* Basic physics and collision checking
|
12
12
|
* Animated objects
|
13
|
+
* Particle systems
|
13
14
|
|
14
15
|
More functionalities are coming. Feel free to contribute! You can send feedback
|
15
16
|
to victordavidsantos@gmail.com.
|
@@ -38,13 +39,17 @@ After installing the Gosu dependencies, you can just `gem install minigl`.
|
|
38
39
|
|
39
40
|
## Documentation
|
40
41
|
|
41
|
-
* The library is 100% RDoc-documented [here](
|
42
|
+
* The library is 100% RDoc-documented [here](https://www.rubydoc.info/gems/minigl).
|
42
43
|
* The [wiki](https://github.com/victords/minigl/wiki) is a work in progress with tutorials and examples.
|
43
44
|
* Test package and examples aren't complete!
|
44
45
|
|
45
|
-
## Version 2.
|
46
|
+
## Version 2.5.0
|
46
47
|
|
47
|
-
*
|
48
|
+
* Added `Particles` (particle system) class. Run `test/particles_game.rb` to check its capabilities!
|
49
|
+
|
50
|
+
### Version 2.5.1
|
51
|
+
|
52
|
+
Patch fixing the required Ruby version (>= 3.1).
|
48
53
|
|
49
54
|
## Contributing
|
50
55
|
|
@@ -0,0 +1,313 @@
|
|
1
|
+
module MiniGL
|
2
|
+
# A particle system.
|
3
|
+
class Particles
|
4
|
+
# Create a new particle system.
|
5
|
+
# Options:
|
6
|
+
# - x: x-coordinate of the origin of the particle system. If +source+ is
|
7
|
+
# set, it has precedence.
|
8
|
+
# - y: y-coordinate of the origin of the particle system. If +source+ is
|
9
|
+
# set, it has precedence.
|
10
|
+
# - source: if set, must be an object that responds to +x+ and +y+. The
|
11
|
+
# position of the particle system will be updated to this object's
|
12
|
+
# position on initialization and every time +update+ is called.
|
13
|
+
# - emission_interval (Integer|Range): interval in frames between each
|
14
|
+
# particle emission. It can be a fixed value or a range, in which case
|
15
|
+
# the interval will be a random value within that range (a new value
|
16
|
+
# before each emission). Default: 10.
|
17
|
+
# - emission_rate (Integer|Range): how many particles will be emitted at a
|
18
|
+
# time. It can be a fixed value or a range, in which case a random number
|
19
|
+
# of particles in that range will be emitted each time. Default: 1.
|
20
|
+
# - duration (Integer): how many frames each particle will live. Default: 30.
|
21
|
+
# - shape (Symbol|nil): one of +:square+, +:triangle_up+, or
|
22
|
+
# +:triangle_down+, to emit basic shapes (if the +img+ option is set, it
|
23
|
+
# has precedence). Shape particles don't support rotation. Either this or
|
24
|
+
# +img+ must be set.
|
25
|
+
# - img (Gosu::Image|nil): image of the particle, has precedence over
|
26
|
+
# +shape+. Either this or +shape+ must be set.
|
27
|
+
# - scale (Numeric): fixed scale of each particle, ignored if +scale_change+
|
28
|
+
# is set to a valid value. Default: 1.
|
29
|
+
# - scale_change (Symbol|nil): one of +:grow+, +:shrink+, or +:alternate+,
|
30
|
+
# indicates how the scale of the particle will change over time. +:grow+
|
31
|
+
# will cause the scale to change from +scale_min+ to +scale_max+;
|
32
|
+
# +:shrink+ will cause the scale to change from +scale_max+ to
|
33
|
+
# +scale_min+; +:alternate+ will cause the scale to first go from
|
34
|
+
# +scale_min+ to +scale_max+, in <code>scale_inflection * duration</code>
|
35
|
+
# frames, and then back to +scale_min+ in the remaining frames. All
|
36
|
+
# changes are linear over time.
|
37
|
+
# - scale_min (Numeric): minimum scale, to be used together with
|
38
|
+
# +scale_change+. Default: 0.
|
39
|
+
# - scale_max (Numeric): maximum scale, to be used together with
|
40
|
+
# +scale_change+. Default: 1.
|
41
|
+
# - scale_inflection (Numeric): should be a number between 0 and 1, to be
|
42
|
+
# used with +scale_change+ set to +:alternate+. Default: 0.5.
|
43
|
+
# - alpha (Numeric): fixed alpha of each particle, ignored if +alpha_change+
|
44
|
+
# is set to a valid value. Default: 255.
|
45
|
+
# - alpha_change, alpha_min, alpha_max, alpha_inflection: behave the same
|
46
|
+
# way as the corresponding properties for +scale+. Default +alpha_max+ is
|
47
|
+
# 255.
|
48
|
+
# - angle (Numeric|Range|nil): initial rotation angle of each particle in
|
49
|
+
# degrees. Can be a fixed value or a range, in which case the initial
|
50
|
+
# rotation will be a random value within that range. Default: nil (no
|
51
|
+
# rotation).
|
52
|
+
# - rotation(Numeric|nil): how much each particle will rotate each frame,
|
53
|
+
# in degrees. Default: nil (no rotation).
|
54
|
+
# - speed (Vector|Hash|nil): specifies how the particle will move each
|
55
|
+
# frame. It can be a +Vector+, in which case the particle will move a
|
56
|
+
# fixed amount (corresponding to the +x+ and +y+ values of the vector)
|
57
|
+
# or a +Hash+ with +:x+ and +:y+ keys, in this case the value can be
|
58
|
+
# fixed or a range, for random movement. Default: nil (no movement).
|
59
|
+
# - color (Integer): color to tint the particles, in the 0xRRGGBB format.
|
60
|
+
# Default: 0xffffff (white, no tinting).
|
61
|
+
# - round_position (Boolean): only draw particles in integer positions.
|
62
|
+
# Default: true.
|
63
|
+
def initialize(**options)
|
64
|
+
raise "Particles must have either a shape or an image!" if options[:shape].nil? && options[:img].nil?
|
65
|
+
|
66
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
67
|
+
@x = @options[:source]&.x || @options[:x] || 0
|
68
|
+
@y = @options[:source]&.y || @options[:y] || 0
|
69
|
+
|
70
|
+
@particles = []
|
71
|
+
@emitting = false
|
72
|
+
end
|
73
|
+
|
74
|
+
# Starts emitting particles. This returns +self+, so you can create, start,
|
75
|
+
# and assign a particle system to a variable like this:
|
76
|
+
# <code>@p_system = Particles.new(...).start</code>
|
77
|
+
def start
|
78
|
+
set_emission_time
|
79
|
+
@timer = @emission_time
|
80
|
+
@emitting = true
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
84
|
+
# Stops emitting new particles. The existing particles will still be kept
|
85
|
+
# alive until they hit +duration+ frames.
|
86
|
+
def stop
|
87
|
+
@emitting = false
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns a boolean indicating whether this particle system is currently
|
91
|
+
# emitting particles.
|
92
|
+
def emitting?
|
93
|
+
@emitting
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns the current particle count.
|
97
|
+
def count
|
98
|
+
@particles.size
|
99
|
+
end
|
100
|
+
|
101
|
+
# Updates the particle system. This should be called in the +update+ loop
|
102
|
+
# of the game.
|
103
|
+
def update
|
104
|
+
@particles.each do |particle|
|
105
|
+
particle.update
|
106
|
+
@particles.delete(particle) if particle.dead?
|
107
|
+
end
|
108
|
+
return unless @emitting
|
109
|
+
|
110
|
+
if @options[:source]
|
111
|
+
@x = @options[:source].x
|
112
|
+
@y = @options[:source].y
|
113
|
+
end
|
114
|
+
|
115
|
+
@timer += 1
|
116
|
+
if @timer >= @emission_time
|
117
|
+
count = @options[:emission_rate].is_a?(Range) ? rand(@options[:emission_rate]) : @options[:emission_rate]
|
118
|
+
count.times do
|
119
|
+
x = @options[:area] ? @x + rand * @options[:area].x : @x + @options[:spread] * (rand - 0.5)
|
120
|
+
y = @options[:area] ? @y + rand * @options[:area].y : @y + @options[:spread] * (rand - 0.5)
|
121
|
+
@particles << Particle.new(x:,
|
122
|
+
y:,
|
123
|
+
duration: @options[:duration],
|
124
|
+
shape: @options[:shape],
|
125
|
+
img: @options[:img],
|
126
|
+
**@options.slice(*PARTICLE_OPTIONS))
|
127
|
+
end
|
128
|
+
set_emission_time
|
129
|
+
@timer = 0
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Draws the particles.
|
134
|
+
# Parameters:
|
135
|
+
# - map (Map|nil): a map whose camera will be used to determine the
|
136
|
+
# position of particles in the screen.
|
137
|
+
# - z_index (Integer): z-index to draw the particles. Default: 0.
|
138
|
+
def draw(map = nil, z_index = 0)
|
139
|
+
@particles.each do |particle|
|
140
|
+
particle.draw(map, z_index)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
# :nodoc:
|
147
|
+
DEFAULT_OPTIONS = {
|
148
|
+
x: 0,
|
149
|
+
y: 0,
|
150
|
+
source: nil,
|
151
|
+
emission_interval: 10,
|
152
|
+
emission_rate: 1,
|
153
|
+
duration: 30,
|
154
|
+
shape: nil,
|
155
|
+
img: nil,
|
156
|
+
spread: 0,
|
157
|
+
scale: 1,
|
158
|
+
scale_change: nil,
|
159
|
+
scale_min: 0,
|
160
|
+
scale_max: 1,
|
161
|
+
scale_inflection: 0.5,
|
162
|
+
alpha: 255,
|
163
|
+
alpha_change: nil,
|
164
|
+
alpha_min: 0,
|
165
|
+
alpha_max: 255,
|
166
|
+
alpha_inflection: 0.5,
|
167
|
+
angle: nil,
|
168
|
+
rotation: nil,
|
169
|
+
speed: nil,
|
170
|
+
color: 0xffffff,
|
171
|
+
round_position: true,
|
172
|
+
}.freeze
|
173
|
+
|
174
|
+
# :nodoc:
|
175
|
+
PARTICLE_OPTIONS = %i[
|
176
|
+
scale
|
177
|
+
scale_change
|
178
|
+
scale_min
|
179
|
+
scale_max
|
180
|
+
scale_inflection
|
181
|
+
alpha
|
182
|
+
alpha_change
|
183
|
+
alpha_min
|
184
|
+
alpha_max
|
185
|
+
alpha_inflection
|
186
|
+
angle
|
187
|
+
rotation
|
188
|
+
speed
|
189
|
+
color
|
190
|
+
round_position
|
191
|
+
].freeze
|
192
|
+
|
193
|
+
def set_emission_time # :nodoc:
|
194
|
+
interval = @options[:emission_interval]
|
195
|
+
@emission_time = interval.is_a?(Range) ? rand(interval) : interval
|
196
|
+
end
|
197
|
+
|
198
|
+
class Particle # :nodoc:
|
199
|
+
def initialize(x:, y:, duration:, shape: nil, img: nil, **options)
|
200
|
+
@x = x
|
201
|
+
@y = y
|
202
|
+
@duration = duration
|
203
|
+
@shape = shape
|
204
|
+
@img = img
|
205
|
+
@options = DEFAULT_OPTIONS.slice(*PARTICLE_OPTIONS).merge(options)
|
206
|
+
@elapsed_time = 0
|
207
|
+
|
208
|
+
if @options[:angle].is_a?(Range)
|
209
|
+
@angle = rand(@options[:angle])
|
210
|
+
elsif @options[:angle].is_a?(Numeric)
|
211
|
+
@angle = @options[:angle]
|
212
|
+
end
|
213
|
+
|
214
|
+
if @options[:speed].is_a?(Hash)
|
215
|
+
speed_x = @options[:speed][:x].is_a?(Range) ? rand(@options[:speed][:x]) : (@options[:speed][:x] || 0)
|
216
|
+
speed_y = @options[:speed][:y].is_a?(Range) ? rand(@options[:speed][:y]) : (@options[:speed][:y] || 0)
|
217
|
+
@speed = Vector.new(speed_x, speed_y)
|
218
|
+
elsif @options[:speed].is_a?(Vector)
|
219
|
+
@speed = @options[:speed]
|
220
|
+
end
|
221
|
+
|
222
|
+
init_variable_property(:scale)
|
223
|
+
init_variable_property(:alpha)
|
224
|
+
end
|
225
|
+
|
226
|
+
def init_variable_property(name)
|
227
|
+
ivar_name = "@#{name}".to_sym
|
228
|
+
case @options["#{name}_change".to_sym]
|
229
|
+
when :grow, :alternate
|
230
|
+
instance_variable_set(ivar_name, @options["#{name}_min".to_sym])
|
231
|
+
when :shrink
|
232
|
+
instance_variable_set(ivar_name, @options["#{name}_max".to_sym])
|
233
|
+
else
|
234
|
+
instance_variable_set(ivar_name, @options[name.to_sym])
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def update_variable_property(name)
|
239
|
+
ivar_name = "@#{name}".to_sym
|
240
|
+
min = @options["#{name}_min".to_sym]
|
241
|
+
max = @options["#{name}_max".to_sym]
|
242
|
+
case @options["#{name}_change".to_sym]
|
243
|
+
when :grow
|
244
|
+
instance_variable_set(ivar_name, min + (@elapsed_time.to_f / @duration) * (max - min))
|
245
|
+
when :shrink
|
246
|
+
instance_variable_set(ivar_name, max - (@elapsed_time.to_f / @duration) * (max - min))
|
247
|
+
when :alternate
|
248
|
+
inflection_point = (@options["#{name}_inflection".to_sym] * @duration).round
|
249
|
+
if @elapsed_time >= inflection_point
|
250
|
+
instance_variable_set(ivar_name, min + (@duration - @elapsed_time).to_f / (@duration - inflection_point) * (max - min))
|
251
|
+
else
|
252
|
+
instance_variable_set(ivar_name, min + (@elapsed_time.to_f / inflection_point) * (max - min))
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def dead?
|
258
|
+
@elapsed_time >= @duration
|
259
|
+
end
|
260
|
+
|
261
|
+
def update
|
262
|
+
if @options[:rotation] && !@img.nil?
|
263
|
+
@angle = 0 if @angle.nil?
|
264
|
+
@angle += @options[:rotation]
|
265
|
+
@angle -= 360 if @angle >= 360
|
266
|
+
end
|
267
|
+
|
268
|
+
if @speed
|
269
|
+
@x += @speed.x
|
270
|
+
@y += @speed.y
|
271
|
+
end
|
272
|
+
|
273
|
+
update_variable_property(:scale) if @options[:scale_change]
|
274
|
+
if @options[:alpha_change]
|
275
|
+
update_variable_property(:alpha)
|
276
|
+
@alpha = @alpha.round
|
277
|
+
end
|
278
|
+
|
279
|
+
@elapsed_time += 1
|
280
|
+
end
|
281
|
+
|
282
|
+
def draw(map, z_index)
|
283
|
+
x = @x - (map&.cam&.x || 0)
|
284
|
+
y = @y - (map&.cam&.y || 0)
|
285
|
+
if @options[:round_position]
|
286
|
+
x = x.round
|
287
|
+
y = y.round
|
288
|
+
end
|
289
|
+
color = (@alpha << 24) | @options[:color]
|
290
|
+
if @img
|
291
|
+
if @angle
|
292
|
+
@img.draw_rot(x, y, z_index, @angle, 0.5, 0.5, @scale, @scale, color)
|
293
|
+
else
|
294
|
+
@img.draw(x - @img.width * @scale * 0.5, y - @img.height * @scale * 0.5, z_index, @scale, @scale, color)
|
295
|
+
end
|
296
|
+
else
|
297
|
+
case @shape
|
298
|
+
when :square
|
299
|
+
G.window.draw_rect(@x - @scale * 0.5, @y - @scale * 0.5, @scale, @scale, color, z_index)
|
300
|
+
when :triangle_up
|
301
|
+
G.window.draw_triangle(@x - @scale * 0.5, @y + @scale * 0.433, color,
|
302
|
+
@x + @scale * 0.5, @y + @scale * 0.433, color,
|
303
|
+
@x, @y - @scale * 0.433, color, z_index)
|
304
|
+
when :triangle_down
|
305
|
+
G.window.draw_triangle(@x - @scale * 0.5, @y - @scale * 0.433, color,
|
306
|
+
@x + @scale * 0.5, @y - @scale * 0.433, color,
|
307
|
+
@x, @y + @scale * 0.433, color, z_index)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
data/lib/minigl.rb
CHANGED
@@ -0,0 +1,70 @@
|
|
1
|
+
require_relative '../lib/minigl'
|
2
|
+
|
3
|
+
include MiniGL
|
4
|
+
|
5
|
+
class MyGame < GameWindow
|
6
|
+
class Source
|
7
|
+
attr_accessor :x, :y
|
8
|
+
|
9
|
+
def initialize(x, y)
|
10
|
+
@x = x
|
11
|
+
@y = y
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
super(800, 600, false)
|
17
|
+
@source = Source.new(100, 100)
|
18
|
+
@particles_systems = [
|
19
|
+
Particles.new(
|
20
|
+
source: @source,
|
21
|
+
img: Res.img(:square),
|
22
|
+
duration: 30,
|
23
|
+
spread: 50,
|
24
|
+
emission_rate: 5,
|
25
|
+
color: 0x00ffff,
|
26
|
+
scale_change: :grow,
|
27
|
+
alpha_change: :shrink,
|
28
|
+
speed: { x: -1..1, y: -2..2 },
|
29
|
+
rotation: 1,
|
30
|
+
angle: 0..89
|
31
|
+
),
|
32
|
+
Particles.new(
|
33
|
+
x: 400,
|
34
|
+
y: 100,
|
35
|
+
shape: :triangle_down,
|
36
|
+
spread: 50,
|
37
|
+
emission_interval: 8,
|
38
|
+
emission_rate: 1..3,
|
39
|
+
scale: 40,
|
40
|
+
color: 0xffff00,
|
41
|
+
alpha_change: :alternate,
|
42
|
+
),
|
43
|
+
]
|
44
|
+
@particles_systems.each(&:start)
|
45
|
+
end
|
46
|
+
|
47
|
+
def update
|
48
|
+
KB.update
|
49
|
+
@source.x -= 3 if KB.key_down?(Gosu::KB_LEFT)
|
50
|
+
@source.x += 3 if KB.key_down?(Gosu::KB_RIGHT)
|
51
|
+
@source.y -= 3 if KB.key_down?(Gosu::KB_UP)
|
52
|
+
@source.y += 3 if KB.key_down?(Gosu::KB_DOWN)
|
53
|
+
|
54
|
+
if KB.key_pressed?(Gosu::KB_SPACE)
|
55
|
+
if @particles_systems[1].emitting?
|
56
|
+
@particles_systems[1].stop
|
57
|
+
else
|
58
|
+
@particles_systems[1].start
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
@particles_systems.each(&:update)
|
63
|
+
end
|
64
|
+
|
65
|
+
def draw
|
66
|
+
@particles_systems.each(&:draw)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
MyGame.new.show
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: minigl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Victor David Santos
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-10-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: gosu
|
@@ -46,6 +46,7 @@ files:
|
|
46
46
|
- lib/minigl/localization.rb
|
47
47
|
- lib/minigl/map.rb
|
48
48
|
- lib/minigl/movement.rb
|
49
|
+
- lib/minigl/particles.rb
|
49
50
|
- lib/minigl/text.rb
|
50
51
|
- test/collision.rb
|
51
52
|
- test/data/font/font1.ttf
|
@@ -82,6 +83,7 @@ files:
|
|
82
83
|
- test/map_tests.rb
|
83
84
|
- test/mov_game.rb
|
84
85
|
- test/movement_tests.rb
|
86
|
+
- test/particles_game.rb
|
85
87
|
- test/res_tests.rb
|
86
88
|
- test/test.png
|
87
89
|
- test/vector_tests.rb
|
@@ -97,14 +99,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
97
99
|
requirements:
|
98
100
|
- - ">="
|
99
101
|
- !ruby/object:Gem::Version
|
100
|
-
version: '
|
102
|
+
version: '3.1'
|
101
103
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
104
|
requirements:
|
103
105
|
- - ">="
|
104
106
|
- !ruby/object:Gem::Version
|
105
107
|
version: '0'
|
106
108
|
requirements: []
|
107
|
-
rubygems_version: 3.
|
109
|
+
rubygems_version: 3.4.10
|
108
110
|
signing_key:
|
109
111
|
specification_version: 4
|
110
112
|
summary: MiniGL
|
@@ -117,6 +119,7 @@ test_files:
|
|
117
119
|
- test/map_tests.rb
|
118
120
|
- test/mov_game.rb
|
119
121
|
- test/movement_tests.rb
|
122
|
+
- test/particles_game.rb
|
120
123
|
- test/res_tests.rb
|
121
124
|
- test/vector_tests.rb
|
122
125
|
- test/test.png
|