minigl 2.4.3 → 2.5.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 46eaeb260b03c9bcfbac716d7568b01c081d66760d71dc540ca9046050f7d89d
4
- data.tar.gz: 3de3cd79bd21324af047f1705d641374e92c986ec0f02badf0a5fd9206f228e8
3
+ metadata.gz: 6078cf1c0aafd790460ec6b7723dff865c0a2e78a8359fd8734cd8a6a07aec60
4
+ data.tar.gz: 61be9170370e9b912a7c3a7087ca145de121f36b005abf62bfe193ca1dc29ec3
5
5
  SHA512:
6
- metadata.gz: 33675d555bd833c359f548bff9c16ef95da819ecc084ecbc657c60016d5e3d18255cd19da03252b212e53bb8600a5a0c4c9f4ab4893261377a7f4f20a03d991b
7
- data.tar.gz: 3f9ed35d874c81bca4544a42c7f6106c0f3b26ba69bf5bfcba59f201297588be32aa6a2f697f2ea4ffde846e8f46fd2d42c5bc68142ec925cf9f8cd6bc5542cf
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](http://www.rubydoc.info/gems/minigl).
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.4.3
46
+ ## Version 2.5.0
46
47
 
47
- * Fixed a collision checking bug in `Movement#move`.
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
@@ -3,3 +3,4 @@ require_relative 'minigl/map'
3
3
  require_relative 'minigl/text'
4
4
  require_relative 'minigl/forms'
5
5
  require_relative 'minigl/localization'
6
+ require_relative 'minigl/particles'
@@ -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.3
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-03-30 00:00:00.000000000 Z
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: '2.0'
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.2.15
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