chingu 0.9rc7 → 0.9rc8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|