minigl 2.4.3 → 2.5.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 46eaeb260b03c9bcfbac716d7568b01c081d66760d71dc540ca9046050f7d89d
4
- data.tar.gz: 3de3cd79bd21324af047f1705d641374e92c986ec0f02badf0a5fd9206f228e8
3
+ metadata.gz: 6d76d1851d74339e98ce7cf452a95f001ff0dc5d757641dc22920769905aa0f7
4
+ data.tar.gz: a3ac0315dc9fb04cdafaac737e90678f58d7aea3b694d8662e2cd447f2eb4622
5
5
  SHA512:
6
- metadata.gz: 33675d555bd833c359f548bff9c16ef95da819ecc084ecbc657c60016d5e3d18255cd19da03252b212e53bb8600a5a0c4c9f4ab4893261377a7f4f20a03d991b
7
- data.tar.gz: 3f9ed35d874c81bca4544a42c7f6106c0f3b26ba69bf5bfcba59f201297588be32aa6a2f697f2ea4ffde846e8f46fd2d42c5bc68142ec925cf9f8cd6bc5542cf
6
+ metadata.gz: b2d9f56c53f76cf02461c8d76aca13836e9010e8265394e8fcc59a372e22ad145f5d8572beb33d4cf498632e425d9b0a0c905fe0f3ddd566aa31b841a96dc84e
7
+ data.tar.gz: ad58132b5c6ea3bd4b377648e3ed02421f32783ba8dadbb80cdb6edcbe12b09bea720a15fd64d41dfaa8e78aa1956490e376e62c4e5e3c3219d9e35cd9d6fc69
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,13 @@ 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.2
46
47
 
47
- * Fixed a collision checking bug in `Movement#move`.
48
+ * Added `source_offset_x` and `source_offset_y` initialization options and `move_to` method to `Particles`.
48
49
 
49
50
  ## Contributing
50
51
 
@@ -0,0 +1,326 @@
1
+ module MiniGL
2
+ # A particle system.
3
+ class Particles
4
+ # Create a new particle system.
5
+ # Options:
6
+ # - x (Numeric): x-coordinate of the origin of the particle system. If
7
+ # +source+ is set, it has precedence.
8
+ # - y (Numeric): y-coordinate of the origin of the particle system. If
9
+ # +source+ is 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 <code>(source.x
12
+ # + source_offset_x, source.y + source_offset_y)</code> on initialization
13
+ # and every time +update+ is called.
14
+ # - source_offset_x (Numeric): horizontal offset relative to the +source+
15
+ # where the particle system will be positioned. Default: 0.
16
+ # - source_offset_y (Numeric): vertical offset relative to the +source+
17
+ # where the particle system will be positioned. Default: 0.
18
+ # - emission_interval (Integer|Range): interval in frames between each
19
+ # particle emission. It can be a fixed value or a range, in which case
20
+ # the interval will be a random value within that range (a new value
21
+ # before each emission). Default: 10.
22
+ # - emission_rate (Integer|Range): how many particles will be emitted at a
23
+ # time. It can be a fixed value or a range, in which case a random number
24
+ # of particles in that range will be emitted each time. Default: 1.
25
+ # - duration (Integer): how many frames each particle will live. Default: 30.
26
+ # - shape (Symbol|nil): one of +:square+, +:triangle_up+, or
27
+ # +:triangle_down+, to emit basic shapes (if the +img+ option is set, it
28
+ # has precedence). Shape particles don't support rotation. Either this or
29
+ # +img+ must be set.
30
+ # - img (Gosu::Image|nil): image of the particle, has precedence over
31
+ # +shape+. Either this or +shape+ must be set.
32
+ # - scale (Numeric): fixed scale of each particle, ignored if +scale_change+
33
+ # is set to a valid value. Default: 1.
34
+ # - scale_change (Symbol|nil): one of +:grow+, +:shrink+, or +:alternate+,
35
+ # indicates how the scale of the particle will change over time. +:grow+
36
+ # will cause the scale to change from +scale_min+ to +scale_max+;
37
+ # +:shrink+ will cause the scale to change from +scale_max+ to
38
+ # +scale_min+; +:alternate+ will cause the scale to first go from
39
+ # +scale_min+ to +scale_max+, in <code>scale_inflection * duration</code>
40
+ # frames, and then back to +scale_min+ in the remaining frames. All
41
+ # changes are linear over time.
42
+ # - scale_min (Numeric): minimum scale, to be used together with
43
+ # +scale_change+. Default: 0.
44
+ # - scale_max (Numeric): maximum scale, to be used together with
45
+ # +scale_change+. Default: 1.
46
+ # - scale_inflection (Numeric): should be a number between 0 and 1, to be
47
+ # used with +scale_change+ set to +:alternate+. Default: 0.5.
48
+ # - alpha (Numeric): fixed alpha of each particle, ignored if +alpha_change+
49
+ # is set to a valid value. Default: 255.
50
+ # - alpha_change, alpha_min, alpha_max, alpha_inflection: behave the same
51
+ # way as the corresponding properties for +scale+. Default +alpha_max+ is
52
+ # 255.
53
+ # - angle (Numeric|Range|nil): initial rotation angle of each particle in
54
+ # degrees. Can be a fixed value or a range, in which case the initial
55
+ # rotation will be a random value within that range. Default: nil (no
56
+ # rotation).
57
+ # - rotation(Numeric|nil): how much each particle will rotate each frame,
58
+ # in degrees. Default: nil (no rotation).
59
+ # - speed (Vector|Hash|nil): specifies how the particle will move each
60
+ # frame. It can be a +Vector+, in which case the particle will move a
61
+ # fixed amount (corresponding to the +x+ and +y+ values of the vector)
62
+ # or a +Hash+ with +:x+ and +:y+ keys, in this case the value can be
63
+ # fixed or a range, for random movement. Default: nil (no movement).
64
+ # - color (Integer): color to tint the particles, in the 0xRRGGBB format.
65
+ # Default: 0xffffff (white, no tinting).
66
+ # - round_position (Boolean): only draw particles in integer positions.
67
+ # Default: true.
68
+ def initialize(**options)
69
+ raise "Particles must have either a shape or an image!" if options[:shape].nil? && options[:img].nil?
70
+
71
+ @options = DEFAULT_OPTIONS.merge(options)
72
+ @x = (@options[:source]&.x || @options[:x]) + @options[:source_offset_x]
73
+ @y = (@options[:source]&.y || @options[:y]) + @options[:source_offset_y]
74
+
75
+ @particles = []
76
+ @emitting = false
77
+ end
78
+
79
+ # Starts emitting particles. This returns +self+, so you can create, start,
80
+ # and assign a particle system to a variable like this:
81
+ # <code>@p_system = Particles.new(...).start</code>
82
+ def start
83
+ set_emission_time
84
+ @timer = @emission_time
85
+ @emitting = true
86
+ self
87
+ end
88
+
89
+ # Stops emitting new particles. The existing particles will still be kept
90
+ # alive until they hit +duration+ frames.
91
+ def stop
92
+ @emitting = false
93
+ end
94
+
95
+ # Changes particle system origin to <code>(x, y)</code>.
96
+ def move_to(x, y)
97
+ @x = x
98
+ @y = y
99
+ end
100
+
101
+ # Returns a boolean indicating whether this particle system is currently
102
+ # emitting particles.
103
+ def emitting?
104
+ @emitting
105
+ end
106
+
107
+ # Returns the current particle count.
108
+ def count
109
+ @particles.size
110
+ end
111
+
112
+ # Updates the particle system. This should be called in the +update+ loop
113
+ # of the game.
114
+ def update
115
+ @particles.each do |particle|
116
+ particle.update
117
+ @particles.delete(particle) if particle.dead?
118
+ end
119
+ return unless @emitting
120
+
121
+ if @options[:source]
122
+ @x = @options[:source].x + @options[:source_offset_x]
123
+ @y = @options[:source].y + @options[:source_offset_y]
124
+ end
125
+
126
+ @timer += 1
127
+ if @timer >= @emission_time
128
+ count = @options[:emission_rate].is_a?(Range) ? rand(@options[:emission_rate]) : @options[:emission_rate]
129
+ count.times do
130
+ x = @options[:area] ? @x + rand * @options[:area].x : @x + @options[:spread] * (rand - 0.5)
131
+ y = @options[:area] ? @y + rand * @options[:area].y : @y + @options[:spread] * (rand - 0.5)
132
+ @particles << Particle.new(x:,
133
+ y:,
134
+ duration: @options[:duration],
135
+ shape: @options[:shape],
136
+ img: @options[:img],
137
+ **@options.slice(*PARTICLE_OPTIONS))
138
+ end
139
+ set_emission_time
140
+ @timer = 0
141
+ end
142
+ end
143
+
144
+ # Draws the particles.
145
+ # Parameters:
146
+ # - map (Map|nil): a map whose camera will be used to determine the
147
+ # position of particles in the screen.
148
+ # - z_index (Integer): z-index to draw the particles. Default: 0.
149
+ def draw(map = nil, z_index = 0)
150
+ @particles.each do |particle|
151
+ particle.draw(map, z_index)
152
+ end
153
+ end
154
+
155
+ private
156
+
157
+ # :nodoc:
158
+ DEFAULT_OPTIONS = {
159
+ x: 0,
160
+ y: 0,
161
+ source: nil,
162
+ source_offset_x: 0,
163
+ source_offset_y: 0,
164
+ emission_interval: 10,
165
+ emission_rate: 1,
166
+ duration: 30,
167
+ shape: nil,
168
+ img: nil,
169
+ spread: 0,
170
+ scale: 1,
171
+ scale_change: nil,
172
+ scale_min: 0,
173
+ scale_max: 1,
174
+ scale_inflection: 0.5,
175
+ alpha: 255,
176
+ alpha_change: nil,
177
+ alpha_min: 0,
178
+ alpha_max: 255,
179
+ alpha_inflection: 0.5,
180
+ angle: nil,
181
+ rotation: nil,
182
+ speed: nil,
183
+ color: 0xffffff,
184
+ round_position: true,
185
+ }.freeze
186
+
187
+ # :nodoc:
188
+ PARTICLE_OPTIONS = %i[
189
+ scale
190
+ scale_change
191
+ scale_min
192
+ scale_max
193
+ scale_inflection
194
+ alpha
195
+ alpha_change
196
+ alpha_min
197
+ alpha_max
198
+ alpha_inflection
199
+ angle
200
+ rotation
201
+ speed
202
+ color
203
+ round_position
204
+ ].freeze
205
+
206
+ def set_emission_time # :nodoc:
207
+ interval = @options[:emission_interval]
208
+ @emission_time = interval.is_a?(Range) ? rand(interval) : interval
209
+ end
210
+
211
+ class Particle # :nodoc:
212
+ def initialize(x:, y:, duration:, shape: nil, img: nil, **options)
213
+ @x = x
214
+ @y = y
215
+ @duration = duration
216
+ @shape = shape
217
+ @img = img
218
+ @options = DEFAULT_OPTIONS.slice(*PARTICLE_OPTIONS).merge(options)
219
+ @elapsed_time = 0
220
+
221
+ if @options[:angle].is_a?(Range)
222
+ @angle = rand(@options[:angle])
223
+ elsif @options[:angle].is_a?(Numeric)
224
+ @angle = @options[:angle]
225
+ end
226
+
227
+ if @options[:speed].is_a?(Hash)
228
+ speed_x = @options[:speed][:x].is_a?(Range) ? rand(@options[:speed][:x]) : (@options[:speed][:x] || 0)
229
+ speed_y = @options[:speed][:y].is_a?(Range) ? rand(@options[:speed][:y]) : (@options[:speed][:y] || 0)
230
+ @speed = Vector.new(speed_x, speed_y)
231
+ elsif @options[:speed].is_a?(Vector)
232
+ @speed = @options[:speed]
233
+ end
234
+
235
+ init_variable_property(:scale)
236
+ init_variable_property(:alpha)
237
+ end
238
+
239
+ def init_variable_property(name)
240
+ ivar_name = "@#{name}".to_sym
241
+ case @options["#{name}_change".to_sym]
242
+ when :grow, :alternate
243
+ instance_variable_set(ivar_name, @options["#{name}_min".to_sym])
244
+ when :shrink
245
+ instance_variable_set(ivar_name, @options["#{name}_max".to_sym])
246
+ else
247
+ instance_variable_set(ivar_name, @options[name.to_sym])
248
+ end
249
+ end
250
+
251
+ def update_variable_property(name)
252
+ ivar_name = "@#{name}".to_sym
253
+ min = @options["#{name}_min".to_sym]
254
+ max = @options["#{name}_max".to_sym]
255
+ case @options["#{name}_change".to_sym]
256
+ when :grow
257
+ instance_variable_set(ivar_name, min + (@elapsed_time.to_f / @duration) * (max - min))
258
+ when :shrink
259
+ instance_variable_set(ivar_name, max - (@elapsed_time.to_f / @duration) * (max - min))
260
+ when :alternate
261
+ inflection_point = (@options["#{name}_inflection".to_sym] * @duration).round
262
+ if @elapsed_time >= inflection_point
263
+ instance_variable_set(ivar_name, min + (@duration - @elapsed_time).to_f / (@duration - inflection_point) * (max - min))
264
+ else
265
+ instance_variable_set(ivar_name, min + (@elapsed_time.to_f / inflection_point) * (max - min))
266
+ end
267
+ end
268
+ end
269
+
270
+ def dead?
271
+ @elapsed_time >= @duration
272
+ end
273
+
274
+ def update
275
+ if @options[:rotation] && !@img.nil?
276
+ @angle = 0 if @angle.nil?
277
+ @angle += @options[:rotation]
278
+ @angle -= 360 if @angle >= 360
279
+ end
280
+
281
+ if @speed
282
+ @x += @speed.x
283
+ @y += @speed.y
284
+ end
285
+
286
+ update_variable_property(:scale) if @options[:scale_change]
287
+ if @options[:alpha_change]
288
+ update_variable_property(:alpha)
289
+ @alpha = @alpha.round
290
+ end
291
+
292
+ @elapsed_time += 1
293
+ end
294
+
295
+ def draw(map, z_index)
296
+ x = @x - (map&.cam&.x || 0)
297
+ y = @y - (map&.cam&.y || 0)
298
+ if @options[:round_position]
299
+ x = x.round
300
+ y = y.round
301
+ end
302
+ color = (@alpha << 24) | @options[:color]
303
+ if @img
304
+ if @angle
305
+ @img.draw_rot(x, y, z_index, @angle, 0.5, 0.5, @scale, @scale, color)
306
+ else
307
+ @img.draw(x - @img.width * @scale * 0.5, y - @img.height * @scale * 0.5, z_index, @scale, @scale, color)
308
+ end
309
+ else
310
+ case @shape
311
+ when :square
312
+ G.window.draw_rect(@x - @scale * 0.5, @y - @scale * 0.5, @scale, @scale, color, z_index)
313
+ when :triangle_up
314
+ G.window.draw_triangle(@x - @scale * 0.5, @y + @scale * 0.433, color,
315
+ @x + @scale * 0.5, @y + @scale * 0.433, color,
316
+ @x, @y - @scale * 0.433, color, z_index)
317
+ when :triangle_down
318
+ G.window.draw_triangle(@x - @scale * 0.5, @y - @scale * 0.433, color,
319
+ @x + @scale * 0.5, @y - @scale * 0.433, color,
320
+ @x, @y + @scale * 0.433, color, z_index)
321
+ end
322
+ end
323
+ end
324
+ end
325
+ end
326
+ 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,68 @@
1
+ require_relative '../lib/minigl'
2
+
3
+ include MiniGL
4
+
5
+ class MyGame < GameWindow
6
+ def initialize
7
+ super(800, 600, false)
8
+ @source = Sprite.new(100, 100, :btn)
9
+ @particles_systems = [
10
+ Particles.new(
11
+ source: @source,
12
+ source_offset_x: 50,
13
+ source_offset_y: 120,
14
+ img: Res.img(:square),
15
+ duration: 30,
16
+ spread: 50,
17
+ emission_rate: 5,
18
+ color: 0x00ffff,
19
+ scale_change: :grow,
20
+ alpha_change: :shrink,
21
+ speed: { x: -1..1, y: -2..2 },
22
+ rotation: 1,
23
+ angle: 0..89
24
+ ),
25
+ Particles.new(
26
+ x: 400,
27
+ y: 100,
28
+ shape: :triangle_down,
29
+ spread: 50,
30
+ emission_interval: 8,
31
+ emission_rate: 1..3,
32
+ scale: 40,
33
+ color: 0xffff00,
34
+ alpha_change: :alternate,
35
+ ),
36
+ ]
37
+ @particles_systems.each(&:start)
38
+ end
39
+
40
+ def update
41
+ KB.update
42
+ @source.x -= 3 if KB.key_down?(Gosu::KB_LEFT)
43
+ @source.x += 3 if KB.key_down?(Gosu::KB_RIGHT)
44
+ @source.y -= 3 if KB.key_down?(Gosu::KB_UP)
45
+ @source.y += 3 if KB.key_down?(Gosu::KB_DOWN)
46
+
47
+ if KB.key_pressed?(Gosu::KB_SPACE)
48
+ if @particles_systems[1].emitting?
49
+ @particles_systems[1].stop
50
+ else
51
+ @particles_systems[1].start
52
+ end
53
+ end
54
+
55
+ if KB.key_pressed?(Gosu::KB_Q)
56
+ @particles_systems[1].move_to(50, 500)
57
+ end
58
+
59
+ @particles_systems.each(&:update)
60
+ end
61
+
62
+ def draw
63
+ @source.draw
64
+ @particles_systems.each(&:draw)
65
+ end
66
+ end
67
+
68
+ 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.2
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