chingu 0.9rc7 → 0.9rc8
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.
- data/examples/example15_trait_timer2.rb +1 -1
- data/examples/example7_gfx_helpers.rb +23 -11
- data/examples/game1.rb +299 -15
- data/examples/high_score_list.yml +22 -22
- data/examples/media/city3.png +0 -0
- data/examples/media/enemy_plane.png +0 -0
- data/lib/chingu.rb +1 -1
- data/lib/chingu/animation.rb +58 -38
- data/lib/chingu/game_state_manager.rb +11 -41
- data/lib/chingu/game_states/edit.rb +5 -2
- data/lib/chingu/game_states/enter_name.rb +3 -3
- data/lib/chingu/game_states/network_client.rb +88 -67
- data/lib/chingu/game_states/network_server.rb +66 -103
- data/lib/chingu/game_states/network_state.rb +71 -0
- data/lib/chingu/gosu_ext/sample.rb +79 -0
- data/lib/chingu/gosu_ext/song.rb +96 -0
- data/lib/chingu/helpers/game_object.rb +5 -1
- data/lib/chingu/helpers/game_state.rb +2 -0
- data/lib/chingu/simple_menu.rb +2 -2
- data/lib/chingu/text.rb +25 -10
- data/lib/chingu/traits/sprite.rb +3 -3
- data/lib/chingu/viewport.rb +18 -14
- data/lib/chingu/window.rb +54 -0
- data/spec/chingu/animation_spec.rb +41 -3
- data/spec/chingu/game_state_manager_spec.rb +50 -3
- data/spec/chingu/network_spec.rb +144 -11
- metadata +20 -15
@@ -9,11 +9,25 @@ include Chingu
|
|
9
9
|
# GFXHelpers example - demonstrating Chingus GFX
|
10
10
|
#
|
11
11
|
class Game < Chingu::Window
|
12
|
+
@@number_of_game_states = 7
|
12
13
|
def initialize
|
13
14
|
super(640,400)
|
14
15
|
self.input = {:space => :next_effect, :esc => :exit}
|
15
16
|
self.caption = "Example of Chingus GFX Helpers"
|
16
|
-
|
17
|
+
load_game_states
|
18
|
+
end
|
19
|
+
|
20
|
+
def next_effect
|
21
|
+
@@number_of_game_states -= 1
|
22
|
+
if @@number_of_game_states == 0
|
23
|
+
load_game_states
|
24
|
+
@@number_of_game_states = 7
|
25
|
+
else
|
26
|
+
pop_game_state
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def load_game_states
|
17
31
|
push_game_state(Fill)
|
18
32
|
push_game_state(FillRect)
|
19
33
|
push_game_state(FillGradient)
|
@@ -22,15 +36,13 @@ class Game < Chingu::Window
|
|
22
36
|
push_game_state(DrawCircle)
|
23
37
|
push_game_state(Particles)
|
24
38
|
end
|
25
|
-
|
26
|
-
def next_effect
|
27
|
-
pop_game_state
|
28
|
-
end
|
29
39
|
end
|
30
40
|
|
41
|
+
|
42
|
+
|
31
43
|
class DrawCircle < Chingu::GameState
|
32
44
|
def draw
|
33
|
-
$window.caption = "circles and arcs (space to continue)"
|
45
|
+
$window.caption = "circles and arcs (press space to continue)"
|
34
46
|
draw_circle(0, 0, 300, Color::RED)
|
35
47
|
fill_circle($window.width, $window.height, 200, Color::RED)
|
36
48
|
|
@@ -44,14 +56,14 @@ end
|
|
44
56
|
|
45
57
|
class Fill < Chingu::GameState
|
46
58
|
def draw
|
47
|
-
$window.caption = "fill (space to continue)"
|
59
|
+
$window.caption = "fill (press space to continue)"
|
48
60
|
fill(Color::RED)
|
49
61
|
end
|
50
62
|
end
|
51
63
|
|
52
64
|
class FillRect < Chingu::GameState
|
53
65
|
def draw
|
54
|
-
$window.caption = "fill_rect (space to continue)"
|
66
|
+
$window.caption = "fill_rect (press space to continue)"
|
55
67
|
fill_rect([10,10,100,100], Color::WHITE)
|
56
68
|
end
|
57
69
|
end
|
@@ -63,7 +75,7 @@ class FillGradient < Chingu::GameState
|
|
63
75
|
end
|
64
76
|
|
65
77
|
def draw
|
66
|
-
$window.caption = "fill_gradient (space to continue)"
|
78
|
+
$window.caption = "fill_gradient (press space to continue)"
|
67
79
|
fill_gradient(:from => @pinkish, :to => @blueish, :orientation => :vertical)
|
68
80
|
end
|
69
81
|
end
|
@@ -74,7 +86,7 @@ class FillGradientMultipleColors < Chingu::GameState
|
|
74
86
|
end
|
75
87
|
|
76
88
|
def draw
|
77
|
-
$window.caption = "fill_gradient with more than two colors (space to continue)"
|
89
|
+
$window.caption = "fill_gradient with more than two colors (press space to continue)"
|
78
90
|
fill_gradient(:colors => @colors, :orientation => :horizontal)
|
79
91
|
end
|
80
92
|
end
|
@@ -86,7 +98,7 @@ class FillGradientRect < Chingu::GameState
|
|
86
98
|
end
|
87
99
|
|
88
100
|
def draw
|
89
|
-
$window.caption = "fill_gradient with :rect-option (space to continue)"
|
101
|
+
$window.caption = "fill_gradient with :rect-option (press space to continue)"
|
90
102
|
fill_gradient(:from => @color1, :to => @color2, :rect => [100,100,200,200], :orientation => :horizontal)
|
91
103
|
end
|
92
104
|
end
|
data/examples/game1.rb
CHANGED
@@ -8,12 +8,20 @@ require 'rubygems' rescue nil
|
|
8
8
|
$LOAD_PATH.unshift File.join(File.expand_path(__FILE__), "..", "..", "lib")
|
9
9
|
require 'chingu'
|
10
10
|
require 'texplay' # adds Image#get_pixel
|
11
|
+
require 'optparse'
|
11
12
|
|
12
13
|
include Gosu
|
13
14
|
include Chingu
|
14
15
|
|
16
|
+
|
15
17
|
class Game < Chingu::Window
|
16
18
|
def initialize
|
19
|
+
$debug = false
|
20
|
+
ARGV.each do |a|
|
21
|
+
if a == "-debug"
|
22
|
+
$debug = true
|
23
|
+
end
|
24
|
+
end
|
17
25
|
super(1000,400,false)
|
18
26
|
self.input = { :escape => :exit }
|
19
27
|
retrofy
|
@@ -30,14 +38,18 @@ class Level < Chingu::GameState
|
|
30
38
|
|
31
39
|
def initialize(options = {})
|
32
40
|
super
|
33
|
-
|
34
|
-
@parallax = Parallax.create(:rotation_center => :top_left, :zorder =>
|
35
|
-
@parallax << { :image => "city2.png", :damping => 2, :zorder =>
|
36
|
-
@parallax << { :image => "city1.png", :damping => 1, :zorder =>
|
37
|
-
|
41
|
+
|
42
|
+
@parallax = Parallax.create(:rotation_center => :top_left, :zorder => 0)
|
43
|
+
@parallax << { :image => "city2.png", :damping => 2, :zorder => 0}
|
44
|
+
@parallax << { :image => "city1.png", :damping => 1, :zorder => 1000}
|
45
|
+
|
46
|
+
@player = Player.create(:x => 30, :y => 10, :zorder=> 500)
|
38
47
|
|
39
48
|
@bg1 = Color.new(0xFFCE28FF)
|
40
49
|
@bg2 = Color.new(0xFF013E87)
|
50
|
+
@text = Text.create("Score:", :size => 20, :x => 30, :y => 370, :zorder => 1001) #initialize score string
|
51
|
+
@text1 = Text.create("0", :size => 20, :x => 90, :y => 370, :zorder => 1001) #initialize score value to 0
|
52
|
+
|
41
53
|
end
|
42
54
|
|
43
55
|
#
|
@@ -53,7 +65,7 @@ class Level < Chingu::GameState
|
|
53
65
|
@player.y = 100
|
54
66
|
|
55
67
|
@parallax.camera_x = 0
|
56
|
-
@total_game_ticks = 100000
|
68
|
+
@total_game_ticks = 10000#100000
|
57
69
|
@timer = 100
|
58
70
|
@total_ticks = 0
|
59
71
|
end
|
@@ -94,19 +106,183 @@ class Level < Chingu::GameState
|
|
94
106
|
bullet.die
|
95
107
|
if enemy.hit_by(bullet)
|
96
108
|
@player.score += 20
|
109
|
+
@text1.destroy! #destroy old score
|
110
|
+
@text1 = Text.create(@player.score, :size => 20, :x => 90, :y => 370, :zorder => 1001 ) #update the new score
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
Bullet.each_bounding_circle_collision(EnemyPlane) do |bullet, enemy|
|
116
|
+
bullet.die
|
117
|
+
if enemy.hit_by(bullet)
|
118
|
+
@player.score += 20
|
119
|
+
@text1.destroy! #destroy old score
|
120
|
+
@text1 = Text.create(@player.score, :size => 20, :x => 90, :y => 370, :zorder => 1001 ) #update the new score
|
121
|
+
|
97
122
|
end
|
98
123
|
end
|
124
|
+
|
125
|
+
# clean up objects
|
126
|
+
game_objects.destroy_if { |object| object.outside_window? || object.color.alpha == 0 }
|
99
127
|
|
100
128
|
@timer = @timer * 0.9999
|
101
129
|
@total_ticks += 1
|
102
130
|
if @total_ticks > @timer
|
103
|
-
Enemy.create(:x => $window.width, :y => rand(300))
|
131
|
+
Enemy.create(:x => $window.width, :y => rand(300), :zorder => (rand(20) * 100))
|
104
132
|
@total_ticks = 0
|
105
133
|
end
|
106
|
-
|
107
|
-
|
134
|
+
|
135
|
+
if @player.score > 100
|
136
|
+
switch_game_state(Level2)
|
137
|
+
end
|
138
|
+
|
139
|
+
if($debug)
|
140
|
+
$window.caption = "City Battle! - Player x/y: #{@player.x}/#{@player.y} - Score: #{@player.score} - FPS: #{$window.fps} - game objects: #{game_objects.size}"
|
141
|
+
else
|
142
|
+
$window.caption = "City Battle! - Score: #{@player.score} - FPS: #{$window.fps} - game objects: #{game_objects.size}"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
def draw
|
146
|
+
fill_gradient(:from => @bg2, :to => @bg1)
|
147
|
+
@parallax.draw
|
148
|
+
super
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
|
153
|
+
#
|
154
|
+
# GAME STATE: LEVEL
|
155
|
+
#
|
156
|
+
class Level2 < Chingu::GameState
|
157
|
+
has_trait :timer
|
158
|
+
|
159
|
+
def initialize(options = {})
|
160
|
+
super
|
161
|
+
|
162
|
+
@parallax = Parallax.create(:rotation_center => :top_left, :zorder => 0)
|
163
|
+
@parallax << { :image => "city1.png", :damping => 2, :zorder => 0}
|
164
|
+
@parallax << { :image => "city3.png", :damping => 1, :zorder => 1000}
|
165
|
+
|
166
|
+
@player = Player.create(:x => 30, :y => 10, :zorder=> 500)
|
167
|
+
|
168
|
+
@bg1 = Color.new(0xFFCE28FF)
|
169
|
+
@bg2 = Color.new(0xFF013E87)
|
170
|
+
@text = Text.create("Score:", :size => 20, :x => 30, :y => 370, :zorder => 1001) #initialize score string
|
171
|
+
@text1 = Text.create("0", :size => 20, :x => 90, :y => 370, :zorder => 1001) #initialize score value to 0
|
172
|
+
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
#
|
177
|
+
# This is called each time this GameState is switched/pushed/poped to.
|
178
|
+
#
|
179
|
+
def setup
|
180
|
+
# Remove all lingering game objects
|
181
|
+
Enemy.destroy_all
|
182
|
+
Bullet.destroy_all
|
183
|
+
|
184
|
+
@player.score = 0
|
185
|
+
@player.x = 10
|
186
|
+
@player.y = 100
|
187
|
+
|
188
|
+
@parallax.camera_x = 0
|
189
|
+
@total_game_ticks = 10000#100000
|
190
|
+
@timer = 100
|
191
|
+
@total_ticks = 0
|
192
|
+
end
|
193
|
+
|
194
|
+
#
|
195
|
+
# The foremost layer in our parallax scroller is the collidable terrain
|
196
|
+
#
|
197
|
+
def solid_pixel_at?(x, y)
|
198
|
+
begin
|
199
|
+
@parallax.layers.last.get_pixel(x, y)[3] != 0
|
200
|
+
rescue
|
201
|
+
puts "Error in get_pixel(#{x}, #{y})"
|
202
|
+
end
|
108
203
|
end
|
109
204
|
|
205
|
+
def update
|
206
|
+
super
|
207
|
+
|
208
|
+
# Move the level forward by increasing the parallax-scrollers camera x-coordinate
|
209
|
+
@parallax.camera_x += 1.5
|
210
|
+
|
211
|
+
# Remove all objects outside screen
|
212
|
+
game_objects.destroy_if { |game_object| game_object.respond_to?("outside_window?") && game_object.outside_window? }
|
213
|
+
|
214
|
+
# Collide bullets with terrain
|
215
|
+
Bullet.select { |o| solid_pixel_at?(o.x, o.y)}.each { |o| o.die }
|
216
|
+
|
217
|
+
# Collide player with terrain
|
218
|
+
push_game_state(GameOver) if solid_pixel_at?(@player.x, @player.y)
|
219
|
+
|
220
|
+
# Collide player with enemies and enemy bullets
|
221
|
+
@player.each_bounding_circle_collision(Enemy) do |player, enemy|
|
222
|
+
enemy.die
|
223
|
+
push_game_state(GameOver)
|
224
|
+
end
|
225
|
+
|
226
|
+
# Collide player with enemies and enemy bullets
|
227
|
+
@player.each_bounding_circle_collision(EnemyPlane) do |player, enemy|
|
228
|
+
enemy.die
|
229
|
+
push_game_state(GameOver)
|
230
|
+
end
|
231
|
+
|
232
|
+
Bullet.each_bounding_circle_collision(Enemy) do |bullet, enemy|
|
233
|
+
bullet.die
|
234
|
+
if enemy.hit_by(bullet)
|
235
|
+
@player.score += 20
|
236
|
+
@text1.destroy! #destroy old score
|
237
|
+
@text1 = Text.create(@player.score, :size => 20, :x => 90, :y => 370, :zorder => 1001 ) #update the new score
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
Bullet.each_bounding_circle_collision(EnemyPlane) do |bullet, enemy|
|
242
|
+
bullet.die
|
243
|
+
if enemy.hit_by(bullet)
|
244
|
+
@player.score += 20
|
245
|
+
@text1.destroy! #destroy old score
|
246
|
+
@text1 = Text.create(@player.score, :size => 20, :x => 90, :y => 370, :zorder => 1001 ) #update the new score
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
Bullet.each_bounding_circle_collision(EnemyPlane) do |bullet, enemy|
|
251
|
+
bullet.die
|
252
|
+
if enemy.hit_by(bullet)
|
253
|
+
@player.score += 20
|
254
|
+
@text1.destroy! #destroy old score
|
255
|
+
@text1 = Text.create(@player.score, :size => 20, :x => 90, :y => 370, :zorder => 1001 ) #update the new score
|
256
|
+
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
|
261
|
+
@timer = @timer * 0.9999
|
262
|
+
@total_ticks += 1
|
263
|
+
if @total_ticks > @timer
|
264
|
+
@select=rand(2)
|
265
|
+
if @select==1
|
266
|
+
Enemy.create(:x => $window.width, :y => rand(300), :zorder => (rand(20) * 100))
|
267
|
+
else
|
268
|
+
EnemyPlane.create(:x => $window.width, :y => rand(300), :zorder => (rand(20) * 100))
|
269
|
+
end
|
270
|
+
@total_ticks = 0
|
271
|
+
end
|
272
|
+
|
273
|
+
if @player.score > 100
|
274
|
+
switch_game_state(Done)
|
275
|
+
end
|
276
|
+
|
277
|
+
# clean up objects
|
278
|
+
game_objects.destroy_if { |object| object.outside_window? || object.color.alpha == 0 }
|
279
|
+
|
280
|
+
if($debug)
|
281
|
+
$window.caption = "City Battle! - Player x/y: #{@player.x}/#{@player.y} - Score: #{@player.score} - FPS: #{$window.fps} - game objects: #{game_objects.size}"
|
282
|
+
else
|
283
|
+
$window.caption = "City Battle! - Score: #{@player.score} - FPS: #{$window.fps} - game objects: #{game_objects.size}"
|
284
|
+
end
|
285
|
+
end
|
110
286
|
def draw
|
111
287
|
fill_gradient(:from => @bg2, :to => @bg1)
|
112
288
|
@parallax.draw
|
@@ -280,6 +456,8 @@ class Enemy < GameObject
|
|
280
456
|
@black = Color.new(0xFF000000)
|
281
457
|
@status == :default
|
282
458
|
|
459
|
+
@fireball_animation = Chingu::Animation.new(:file => media_path("fireball.png"), :size => [32,32])
|
460
|
+
|
283
461
|
#
|
284
462
|
# Cache explosion and shrapnel images (created with texplay, not recomended doing over and over each time)
|
285
463
|
#
|
@@ -323,7 +501,19 @@ class Enemy < GameObject
|
|
323
501
|
# Create some shrapnel-objects
|
324
502
|
#
|
325
503
|
Sound["explosion.wav"].play(0.3)
|
326
|
-
|
504
|
+
|
505
|
+
#Explosion.create(:x => @x, :y => @y, :image => @@explosion_image )
|
506
|
+
5.times{ Chingu::Particle.create( :x => @x,
|
507
|
+
:y => @y,
|
508
|
+
:animation => @fireball_animation,
|
509
|
+
:scale_rate => +0.05,
|
510
|
+
:fade_rate => -10,
|
511
|
+
:rotation_rate => +1,
|
512
|
+
:mode => :default
|
513
|
+
)
|
514
|
+
@x -= @velocity
|
515
|
+
}
|
516
|
+
|
327
517
|
5.times { Shrapnel.create(:x => @x, :y => @y, :image => @@shrapnel_image)}
|
328
518
|
|
329
519
|
#
|
@@ -343,6 +533,103 @@ class Enemy < GameObject
|
|
343
533
|
end
|
344
534
|
|
345
535
|
|
536
|
+
#
|
537
|
+
# OUR ENEMY Plane
|
538
|
+
#
|
539
|
+
class EnemyPlane < GameObject
|
540
|
+
has_traits :collision_detection, :timer
|
541
|
+
attr_reader :radius
|
542
|
+
|
543
|
+
def setup
|
544
|
+
@velocity = options[:velocity] || 2
|
545
|
+
@health = options[:health] || 100
|
546
|
+
|
547
|
+
@anim = Animation.new(:file => "media/enemy_plane.png", :size => [32,20], :delay => 100)
|
548
|
+
@image = @anim.first
|
549
|
+
|
550
|
+
@radius = 5
|
551
|
+
@black = Color.new(0xFF000000)
|
552
|
+
@status == :default
|
553
|
+
|
554
|
+
@fireball_animation = Chingu::Animation.new(:file => media_path("fireball.png"), :size => [32,32])
|
555
|
+
|
556
|
+
#
|
557
|
+
# Cache explosion and shrapnel images (created with texplay, not recomended doing over and over each time)
|
558
|
+
#
|
559
|
+
@@shrapnel_image ||= Shrapnel.create_image_for(self)
|
560
|
+
@@explosion_image ||= Explosion.create_image_for(self)
|
561
|
+
end
|
562
|
+
|
563
|
+
def hit_by(object)
|
564
|
+
return if @status == :dying
|
565
|
+
|
566
|
+
#
|
567
|
+
# During 20 millisecons, use Gosus :additive draw-mode, which here results in a white sprite
|
568
|
+
# Classic "hit by a bullet"-effect
|
569
|
+
#
|
570
|
+
during(20) { @mode = :additive; }.then { @mode = :default }
|
571
|
+
|
572
|
+
@health -= 20
|
573
|
+
|
574
|
+
if @health <= 0
|
575
|
+
die
|
576
|
+
return true
|
577
|
+
else
|
578
|
+
return false
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
def fire
|
583
|
+
EnemyBullet.create(:x => self.x, :y => self.y)
|
584
|
+
end
|
585
|
+
|
586
|
+
def die
|
587
|
+
#
|
588
|
+
# Make sure die() is only called once
|
589
|
+
#
|
590
|
+
return if @status == :dying
|
591
|
+
@status = :dying
|
592
|
+
|
593
|
+
#
|
594
|
+
# Play our explosion-sound file
|
595
|
+
# Create an explosion-object
|
596
|
+
# Create some shrapnel-objects
|
597
|
+
#
|
598
|
+
Sound["explosion.wav"].play(0.3)
|
599
|
+
|
600
|
+
#Explosion.create(:x => @x, :y => @y, :image => @@explosion_image )
|
601
|
+
5.times{ Chingu::Particle.create( :x => @x,
|
602
|
+
:y => @y,
|
603
|
+
:animation => @fireball_animation,
|
604
|
+
:scale_rate => +0.05,
|
605
|
+
:fade_rate => -10,
|
606
|
+
:rotation_rate => +1,
|
607
|
+
:mode => :default
|
608
|
+
)
|
609
|
+
@x -= @velocity
|
610
|
+
}
|
611
|
+
|
612
|
+
5.times { Shrapnel.create(:x => @x, :y => @y, :image => @@shrapnel_image)}
|
613
|
+
|
614
|
+
#
|
615
|
+
# During 200 ms, fade and scale image, then destroy it
|
616
|
+
#
|
617
|
+
@color = @black
|
618
|
+
@color.alpha = 50
|
619
|
+
during(200) { @factor_x += 0.5; @factor_y += 0.5; @x -= 1; @color.alpha -= 1}.then { self.destroy }
|
620
|
+
end
|
621
|
+
|
622
|
+
def update
|
623
|
+
return if @status == :dying
|
624
|
+
|
625
|
+
@image = @anim.next
|
626
|
+
@x -= @velocity
|
627
|
+
end
|
628
|
+
end
|
629
|
+
|
630
|
+
|
631
|
+
|
632
|
+
|
346
633
|
#
|
347
634
|
# GAME STATE: GAME OVER
|
348
635
|
#
|
@@ -368,17 +655,14 @@ end
|
|
368
655
|
# GAME STATE: GAME OVER
|
369
656
|
#
|
370
657
|
class Done < Chingu::GameState
|
371
|
-
|
372
|
-
@score = options[:score]
|
373
|
-
end
|
374
|
-
|
658
|
+
|
375
659
|
def setup
|
376
660
|
@text = Text.create("You made it! Score #{@score} (ESC to quit, RETURN to try again!)", :size => 40, :x => 30, :y => 100)
|
377
661
|
self.input = { :esc => :exit, :return => :try_again}
|
378
662
|
end
|
379
663
|
|
380
664
|
def try_again
|
381
|
-
|
665
|
+
switch_game_state(Level)
|
382
666
|
end
|
383
667
|
end
|
384
668
|
|