chingu 0.5.5.3
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/History.txt +21 -0
- data/LICENSE +504 -0
- data/Manifest.txt +72 -0
- data/README.rdoc +588 -0
- data/Rakefile +19 -0
- data/benchmarks/README.txt +1 -0
- data/benchmarks/benchmark.rb +6 -0
- data/benchmarks/benchmark3.rb +23 -0
- data/benchmarks/benchmark4.rb +71 -0
- data/benchmarks/benchmark5.rb +91 -0
- data/benchmarks/benchmark6.rb +23 -0
- data/benchmarks/meta_benchmark.rb +67 -0
- data/benchmarks/meta_benchmark2.rb +39 -0
- data/chingu.gemspec +34 -0
- data/examples/example1.rb +37 -0
- data/examples/example10.rb +75 -0
- data/examples/example11.rb +51 -0
- data/examples/example12.rb +67 -0
- data/examples/example2.rb +115 -0
- data/examples/example3.rb +40 -0
- data/examples/example4.rb +175 -0
- data/examples/example5.rb +107 -0
- data/examples/example6.rb +57 -0
- data/examples/example7.rb +133 -0
- data/examples/example8.rb +109 -0
- data/examples/example9.rb +106 -0
- data/examples/media/Parallax-scroll-example-layer-0.png +0 -0
- data/examples/media/Parallax-scroll-example-layer-1.png +0 -0
- data/examples/media/Parallax-scroll-example-layer-2.png +0 -0
- data/examples/media/Parallax-scroll-example-layer-3.png +0 -0
- data/examples/media/background1.png +0 -0
- data/examples/media/fire_bullet.png +0 -0
- data/examples/media/fireball.png +0 -0
- data/examples/media/particle.png +0 -0
- data/examples/media/ruby.png +0 -0
- data/examples/media/spaceship.png +0 -0
- data/examples/media/stickfigure.bmp +0 -0
- data/examples/media/stickfigure.png +0 -0
- data/examples/media/video_games.png +0 -0
- data/lib/chingu.rb +32 -0
- data/lib/chingu/actor.rb +17 -0
- data/lib/chingu/animation.rb +142 -0
- data/lib/chingu/assets.rb +64 -0
- data/lib/chingu/basic_game_object.rb +132 -0
- data/lib/chingu/core_extensions.rb +53 -0
- data/lib/chingu/effects.rb +36 -0
- data/lib/chingu/fpscounter.rb +62 -0
- data/lib/chingu/game_object.rb +127 -0
- data/lib/chingu/game_object_list.rb +91 -0
- data/lib/chingu/game_state.rb +137 -0
- data/lib/chingu/game_state_manager.rb +284 -0
- data/lib/chingu/game_states/debug.rb +65 -0
- data/lib/chingu/game_states/fade_to.rb +91 -0
- data/lib/chingu/game_states/pause.rb +57 -0
- data/lib/chingu/gfx_helpers.rb +89 -0
- data/lib/chingu/helpers.rb +166 -0
- data/lib/chingu/inflector.rb +34 -0
- data/lib/chingu/input.rb +100 -0
- data/lib/chingu/named_resource.rb +254 -0
- data/lib/chingu/parallax.rb +83 -0
- data/lib/chingu/particle.rb +21 -0
- data/lib/chingu/rect.rb +612 -0
- data/lib/chingu/require_all.rb +133 -0
- data/lib/chingu/text.rb +56 -0
- data/lib/chingu/traits/collision_detection.rb +172 -0
- data/lib/chingu/traits/effect.rb +113 -0
- data/lib/chingu/traits/input.rb +38 -0
- data/lib/chingu/traits/retrofy.rb +53 -0
- data/lib/chingu/traits/rotation_center.rb +84 -0
- data/lib/chingu/traits/timer.rb +90 -0
- data/lib/chingu/traits/velocity.rb +67 -0
- data/lib/chingu/window.rb +170 -0
- metadata +162 -0
- metadata.gz.sig +1 -0
data/Manifest.txt
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
History.txt
|
2
|
+
LICENSE
|
3
|
+
Manifest.txt
|
4
|
+
README.rdoc
|
5
|
+
Rakefile
|
6
|
+
benchmarks/README.txt
|
7
|
+
benchmarks/benchmark.rb
|
8
|
+
benchmarks/benchmark3.rb
|
9
|
+
benchmarks/benchmark4.rb
|
10
|
+
benchmarks/benchmark5.rb
|
11
|
+
benchmarks/benchmark6.rb
|
12
|
+
benchmarks/meta_benchmark.rb
|
13
|
+
benchmarks/meta_benchmark2.rb
|
14
|
+
chingu.gemspec
|
15
|
+
examples/example1.rb
|
16
|
+
examples/example10.rb
|
17
|
+
examples/example11.rb
|
18
|
+
examples/example12.rb
|
19
|
+
examples/example2.rb
|
20
|
+
examples/example3.rb
|
21
|
+
examples/example4.rb
|
22
|
+
examples/example5.rb
|
23
|
+
examples/example6.rb
|
24
|
+
examples/example7.rb
|
25
|
+
examples/example8.rb
|
26
|
+
examples/example9.rb
|
27
|
+
examples/media/Parallax-scroll-example-layer-0.png
|
28
|
+
examples/media/Parallax-scroll-example-layer-1.png
|
29
|
+
examples/media/Parallax-scroll-example-layer-2.png
|
30
|
+
examples/media/Parallax-scroll-example-layer-3.png
|
31
|
+
examples/media/background1.png
|
32
|
+
examples/media/fire_bullet.png
|
33
|
+
examples/media/fireball.png
|
34
|
+
examples/media/particle.png
|
35
|
+
examples/media/ruby.png
|
36
|
+
examples/media/spaceship.png
|
37
|
+
examples/media/stickfigure.bmp
|
38
|
+
examples/media/stickfigure.png
|
39
|
+
examples/media/video_games.png
|
40
|
+
lib/chingu.rb
|
41
|
+
lib/chingu/actor.rb
|
42
|
+
lib/chingu/animation.rb
|
43
|
+
lib/chingu/assets.rb
|
44
|
+
lib/chingu/basic_game_object.rb
|
45
|
+
lib/chingu/core_extensions.rb
|
46
|
+
lib/chingu/effects.rb
|
47
|
+
lib/chingu/fpscounter.rb
|
48
|
+
lib/chingu/game_object.rb
|
49
|
+
lib/chingu/game_object_list.rb
|
50
|
+
lib/chingu/game_state.rb
|
51
|
+
lib/chingu/game_state_manager.rb
|
52
|
+
lib/chingu/game_states/debug.rb
|
53
|
+
lib/chingu/game_states/fade_to.rb
|
54
|
+
lib/chingu/game_states/pause.rb
|
55
|
+
lib/chingu/gfx_helpers.rb
|
56
|
+
lib/chingu/helpers.rb
|
57
|
+
lib/chingu/inflector.rb
|
58
|
+
lib/chingu/input.rb
|
59
|
+
lib/chingu/named_resource.rb
|
60
|
+
lib/chingu/parallax.rb
|
61
|
+
lib/chingu/particle.rb
|
62
|
+
lib/chingu/rect.rb
|
63
|
+
lib/chingu/require_all.rb
|
64
|
+
lib/chingu/text.rb
|
65
|
+
lib/chingu/traits/collision_detection.rb
|
66
|
+
lib/chingu/traits/effect.rb
|
67
|
+
lib/chingu/traits/input.rb
|
68
|
+
lib/chingu/traits/retrofy.rb
|
69
|
+
lib/chingu/traits/rotation_center.rb
|
70
|
+
lib/chingu/traits/timer.rb
|
71
|
+
lib/chingu/traits/velocity.rb
|
72
|
+
lib/chingu/window.rb
|
data/README.rdoc
ADDED
@@ -0,0 +1,588 @@
|
|
1
|
+
= CHINGU
|
2
|
+
http://github.com/ippa/chingu/tree/master
|
3
|
+
|
4
|
+
DOCUMENTATION: http://rdoc.info/projects/ippa/chingu
|
5
|
+
|
6
|
+
Ruby 1.9.1 is recommended. Also works with 1.8.7+.
|
7
|
+
|
8
|
+
This is still a rather young project, core classes and naming can still change.
|
9
|
+
|
10
|
+
== INSTALL
|
11
|
+
gem sources -a http://gems.github.com
|
12
|
+
sudo gem install ippa-chingu
|
13
|
+
|
14
|
+
|
15
|
+
== DESCRIPTION
|
16
|
+
Game framework built on top of the OpenGL accelerated game lib Gosu.
|
17
|
+
It adds simple yet powerful game states, prettier input handling, deployment safe asset-handling, a basic re-usable game object and automation of common task.
|
18
|
+
|
19
|
+
|
20
|
+
== THE STORY
|
21
|
+
The last years I've dabbled around a lot with game development.
|
22
|
+
I've developed games in both Rubygame and Gosu. I've looked at gamebox.
|
23
|
+
Rubygame is a very capable framework with a lot of functionality (collision detection, very good event system etc). Gosu is way more minimalistic but also faster with OpenGL -acceleration. Gosu isn't likely to get much more complex since it does what it should do very well and fast.
|
24
|
+
|
25
|
+
After 10+ game prototypes and some finished smaller games I started to see patterns each time I started a new game. Making classes with x/y/image/other-parameters that I called update/draw on in the main loop.
|
26
|
+
This became the basic Chingu::GameObject which encapsulates Gosus "Image.draw_rot" and enables automatic updating/drawing through "game_objects".
|
27
|
+
|
28
|
+
There was always a huge big chunk of checking keyboard-events in the main loop.
|
29
|
+
Borrowing ideas from Rubygame this has now become @player.keyboard(:left => :move_left, :space => :fire ... etc.
|
30
|
+
|
31
|
+
|
32
|
+
== CORE OVERVIEW
|
33
|
+
Chingu consists of the following core classes / concepts:
|
34
|
+
|
35
|
+
=== Chingu::Window
|
36
|
+
The main window, use it at you use Gosu::Window now. Calcs the framerate, takes care of states,
|
37
|
+
handles chingu-formated input, updates and draws BasicGameObject / GameObjects automaticly.
|
38
|
+
Available thoughout your source as $window (Yes, that's the only global Chingu has).
|
39
|
+
|
40
|
+
=== Chingu::GameObject
|
41
|
+
Use this for all your in game objects. The player, the enemies, the bullets, the powerups, the loot laying around.
|
42
|
+
It's very reusable and doesn't contain any game-logic (that's up to you!). Only stuff to put it on screen a certain way.
|
43
|
+
If you do GameObject.create() instead of new() Chingu will keep save the object in the "game_object"-list for automatic updates/draws.
|
44
|
+
GameObjects also have the nicer Chingu input-mapping: @player.input = { :left => :move_left, :right => :move_right, :space => :fire}
|
45
|
+
Has either Chingu::Window or a Chingu::GameState as "parent".
|
46
|
+
|
47
|
+
=== Chingu::BasicGameObject
|
48
|
+
For those who think GameObject is a too little fat, there's BasicGameObject (GameObject inherits from BasicGameObject).
|
49
|
+
BasicGameObject is just an empty frame (no x,y,image accessors or draw-logic) for you to build on.
|
50
|
+
It _can_ be extended with Chingus trait-system though. The new() vs create() behaivor of GameObject comes from BasicGameObject.
|
51
|
+
BasicGameObject#parent points to either $window or a game state and is automaticly set on creation time.
|
52
|
+
|
53
|
+
=== Chingu::GameStateManager
|
54
|
+
Keeps track of the game states. Implements a stack-based system with push_game_state and pop_game_state.
|
55
|
+
|
56
|
+
=== Chingu::GameState
|
57
|
+
A "standalone game loop" that can be activated and deactivated to control game flow.
|
58
|
+
A game state is very much like a main gosu window. You define update() and draw() in a gamestate.
|
59
|
+
It comes with 2 extras that main window doesn't have. #setup (called when activated) and #finalize (called when deactivated)
|
60
|
+
|
61
|
+
If using game states, the flow of draw/update/button_up/button_down is:
|
62
|
+
Chingu::Window --> Chingu::GameStateManager --> Chingu::GameState.
|
63
|
+
For example, inside game state Menu you call push_game_state(Level). When Level exists, it will go back to Menu.
|
64
|
+
|
65
|
+
=== Traits
|
66
|
+
Traits are extensions (or plugins if you so will) to BasicGameObjects.
|
67
|
+
The aim is so encapsulate common behaivor into modules for easy inclusion in your game classes.
|
68
|
+
Making a trait is easy, just an ordinary module with the methods setup_trait(), update_trait() and/or draw_trait().
|
69
|
+
It currently has to be namespaced to Chingu::Traits for "has_trait" to work inside GameObject-classes.
|
70
|
+
|
71
|
+
== OTHER CLASSES / HELPERS
|
72
|
+
|
73
|
+
=== Chingu::Text
|
74
|
+
Makes use of Gosu::Font more rubyish and powerful.
|
75
|
+
In it's core, another Chingu::GameObject + Gosu::Font.
|
76
|
+
|
77
|
+
=== Chingu::Animation
|
78
|
+
Load and interact with tile-based animations. loop, bounce and access invidual frame(s) easily.
|
79
|
+
An "@image = @animation.next!" in your Player#update is usually enough to get you started!
|
80
|
+
|
81
|
+
=== Chingu::Parallax
|
82
|
+
A class for easy paralaxxscrolling. See example3.rb for more.
|
83
|
+
|
84
|
+
=== Various Helpers
|
85
|
+
Both $window and game states gets some new graphical helpers, currently only 3, but quite useful:
|
86
|
+
|
87
|
+
fill() # Fills whole window with color 'color'.
|
88
|
+
fill_rect() # Fills a given Rect 'rect' with Color 'color'
|
89
|
+
fill_gradient() # Fills window or a given rect with a gradient between two colors.
|
90
|
+
|
91
|
+
If you base your models on GameObject (or BasicGameObject) you get:
|
92
|
+
Enemy.all # Returns an Array of all Enemy-instances
|
93
|
+
Enemy.size # Returns the amount of Enemy-instances
|
94
|
+
Enemy.destroy_all # Destroys all Enemy-instances
|
95
|
+
Enemy.destroy_if(&block) # Destroy all objects for which &block returns true
|
96
|
+
|
97
|
+
|
98
|
+
== BASICS / EXAMPLES
|
99
|
+
|
100
|
+
=== Chingu::Window
|
101
|
+
With Gosu the main window inherits from Gosu::Window. In Chingu we use Chingu::Window. It's a basic Gosu::Window with extra cheese on top of it. keyboard handling, automatic update/draw calls to all gameobjects, fps counting etc.
|
102
|
+
|
103
|
+
You're probably familiar with this very common Gosu pattern:
|
104
|
+
|
105
|
+
ROOT_PATH = File.dirname(File.expand_path(__FILE__))
|
106
|
+
class Game < Gosu::Window
|
107
|
+
def initialize
|
108
|
+
@player = Player.new
|
109
|
+
end
|
110
|
+
|
111
|
+
def update
|
112
|
+
if button_down? Button::KbLeft
|
113
|
+
@player.left
|
114
|
+
elsif button_down? Button::KbRight
|
115
|
+
@player.right
|
116
|
+
end
|
117
|
+
|
118
|
+
@player.update
|
119
|
+
end
|
120
|
+
|
121
|
+
def draw
|
122
|
+
@player.draw
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
class Player
|
127
|
+
attr_accessor :x,:y,:image
|
128
|
+
def initialize(options)
|
129
|
+
@x = options[:x]
|
130
|
+
@y = options[:y]
|
131
|
+
@image = Image.new(File.join(ROOT_PATH, "media", "player.png"))
|
132
|
+
end
|
133
|
+
|
134
|
+
def move_left
|
135
|
+
@x -= 1
|
136
|
+
end
|
137
|
+
|
138
|
+
def move_right
|
139
|
+
@x += 1
|
140
|
+
end
|
141
|
+
|
142
|
+
def draw
|
143
|
+
@image.draw(@x,@y,100)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
Game.new.show # Start the Game update/draw loop!
|
148
|
+
|
149
|
+
|
150
|
+
Chingu doesn't change any fundamental concept of Gosu, but it will make the above code cleaner:
|
151
|
+
|
152
|
+
#
|
153
|
+
# We use Chingu::Window instead of Gosu::Window
|
154
|
+
#
|
155
|
+
class Game < Chingu::Window
|
156
|
+
def initialize
|
157
|
+
super # This is always needed if you want to take advantage of what chingu offers
|
158
|
+
#
|
159
|
+
# Player will automaticly be updated and drawn since it's a Chingu::GameObject
|
160
|
+
# You'll need your own Game#update/#draw after a while, but just put #super there and Chingu can do its thing.
|
161
|
+
#
|
162
|
+
@player = Player.create
|
163
|
+
@player.input = {:left => :move_left, :right => :move_right}
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
#
|
168
|
+
# If we create classes from Chingu::GameObject we get stuff for free.
|
169
|
+
# The accessors: image,x,y,zorder,angle,factor_x,factor_y,center_x,center_y,mode,update,draw
|
170
|
+
# You might recognize those from #draw_rot - http://www.libgosu.org/rdoc/classes/Gosu/Image.html#M000023
|
171
|
+
# And in it's core, that's what Chingu::GameObject is, an encapsulation of draw_rot with some extra cheese.
|
172
|
+
# For example, we get automatic calls to draw/update with Chingu::GameObject, which usually is what you want.
|
173
|
+
# You could stop this by doing: @player = Player.new(:draw => false, :update => false)
|
174
|
+
#
|
175
|
+
class Player < Chingu::GameObject
|
176
|
+
def initialize(options)
|
177
|
+
super(options.merge(:image => Image["player.png"])
|
178
|
+
end
|
179
|
+
|
180
|
+
def move_left
|
181
|
+
@x -= 1
|
182
|
+
end
|
183
|
+
|
184
|
+
def move_right
|
185
|
+
@x += 1
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
Game.new.show # Start the Game update/draw loop!
|
190
|
+
|
191
|
+
Roughly 50 lines became 26 more powerful lines. (you can do @player.angle = 100 for example)
|
192
|
+
|
193
|
+
If you've worked with Gosu for a while you're probably tired of passing around the window-parameter.
|
194
|
+
Chingu solves this (as has many other developers) with a global variable $window. Yes, globals are bad, but in this case it kinda makes sense. It's used under the hood in various places.
|
195
|
+
|
196
|
+
=== Chingu::GameObject
|
197
|
+
This is our basic "game unit"-class, meaning most in game objects (players, enemies, bullets etc) should be inherited from Chingu::GameObject.
|
198
|
+
The basic ideas behind it are:
|
199
|
+
|
200
|
+
* Encapsulate only the very common basics that Most in game objects need
|
201
|
+
* Keep naming close to Gosu, but add smart convenient methods / shortcuts and a more rubyish feeling
|
202
|
+
* No game logic allowed in GameObject, since that's not likely to be useful for others.
|
203
|
+
|
204
|
+
I've chose to base it around Image#draw_rot. So basically all the arguments that you pass to draw_rot can be passed to GameObject#new when creating a new object, an example using almost all arguments would be:
|
205
|
+
|
206
|
+
#
|
207
|
+
# You probably recognize the arguments from http://www.libgosu.org/rdoc/classes/Gosu/Image.html#M000023
|
208
|
+
#
|
209
|
+
@player = Player.new(:image => Image["player.png"], :x=>100, :y=>100, :zorder=>100, :angle=>45, :factor_x=>10, :factor_y=>10, :center_x=>0, :center_y=>0)
|
210
|
+
|
211
|
+
#
|
212
|
+
# A shortcut for the above line would be
|
213
|
+
#
|
214
|
+
@player = Player.new(:image => Image["player.png"], :x=>100, :y=>100, :zorder=>100, :angle=>45, :factor=>10, :center=>0)
|
215
|
+
|
216
|
+
#
|
217
|
+
# I've tried doing sensible defaults:
|
218
|
+
# x/y = [middle of the screen] for super quick display where it should be easy in sight)
|
219
|
+
# angle = 0 (no angle by default)
|
220
|
+
# center_x/center_y = 0.5 (basically the center of the image will be drawn at x/y)
|
221
|
+
# factor_x/factor_y = 1 (no zoom by default)
|
222
|
+
#
|
223
|
+
@player = Player.new
|
224
|
+
|
225
|
+
#
|
226
|
+
# By default Chingu::Window calls update & draw on all GameObjects in it's own update/draw.
|
227
|
+
# If this is not what you want, use :draw and :update
|
228
|
+
#
|
229
|
+
@player = Player.new(:draw => false, :update => false)
|
230
|
+
|
231
|
+
=== Input
|
232
|
+
One of the core things I wanted a more natural way of inputhandling.
|
233
|
+
You can define input -> actions on Chingu::Window, Chingu::GameState and Chingu::GameObject.
|
234
|
+
Like this:
|
235
|
+
|
236
|
+
#
|
237
|
+
# When left arrow is pressed, call @player.turn_left ... and so on.
|
238
|
+
#
|
239
|
+
@player.input = { :left => :turn_left, :right => :turn_right, :left => :halt_left, :right => :halt_right }
|
240
|
+
|
241
|
+
|
242
|
+
#
|
243
|
+
# In Gosu the equivalent would be:
|
244
|
+
#
|
245
|
+
def button_down(id)
|
246
|
+
@player.turn_left if id == Button::KbLeft
|
247
|
+
@player.turn_right if id == Button::KbRight
|
248
|
+
end
|
249
|
+
|
250
|
+
def button_up(id)
|
251
|
+
@player.halt_left if id == Button::KbLeft
|
252
|
+
@player.halt_right if id == Button::KbRight
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
Another more complex example:
|
257
|
+
|
258
|
+
#
|
259
|
+
# So what happens here?
|
260
|
+
#
|
261
|
+
# Pressing P would create an game state out of class Pause, cache it and activate it.
|
262
|
+
# Pressing ESC would call Play#close
|
263
|
+
# Holding down LEFT would call Play#move_left on every game iteration
|
264
|
+
# Holding down RIGHT would call Play#move_right on every game iteration
|
265
|
+
# Releasing SPACE would call Play#fire
|
266
|
+
#
|
267
|
+
|
268
|
+
class Play < Chingu::GameState
|
269
|
+
def initialize
|
270
|
+
self.input = { :p => Pause,
|
271
|
+
:escape => :close,
|
272
|
+
:holding_left => :move_left,
|
273
|
+
:holding_right => :move_right,
|
274
|
+
:released_space => :fire }
|
275
|
+
end
|
276
|
+
end
|
277
|
+
class Pause < Chingu::GameState
|
278
|
+
# pause logic here
|
279
|
+
end
|
280
|
+
|
281
|
+
In Gosu the above code would include code in button_up(), button_down() and a check for button_down?() in update().
|
282
|
+
|
283
|
+
Every symbol can be prefixed by either "released_" or "holding_" while no prefix at all defaults to pressed once.
|
284
|
+
|
285
|
+
So, why not :up_space or :release_space instead of :released_space?
|
286
|
+
+:up_space+ doesn't sound like english, :release_space sounds more like a command then an event.
|
287
|
+
|
288
|
+
|
289
|
+
Or +:hold_left+ or :down_left instead of :holding_left?
|
290
|
+
:holding_left sounds like something that's happening over a period of time, not a single trigger, which corresponds well to how it works.
|
291
|
+
|
292
|
+
And with the default :space => :something you would imagine that :something is called once. You press :space once, :something is executed once.
|
293
|
+
|
294
|
+
|
295
|
+
=== GameState / GameStateManager
|
296
|
+
Chingu incorporates a basic push/pop game state system (as discussed here: http://www.gamedev.net/community/forums/topic.asp?topic_id=477320).
|
297
|
+
|
298
|
+
Game states is a way of organizing your intros, menus, levels.
|
299
|
+
|
300
|
+
Game states aren't complicated. In Chingu a GameState is a class that behaves mostly like your default Gosu::Window (or in our case Chingu::Window) game loop.
|
301
|
+
|
302
|
+
|
303
|
+
# A simple GameState-example
|
304
|
+
class Intro < Chingu::GameState
|
305
|
+
|
306
|
+
def update
|
307
|
+
# game logic here
|
308
|
+
end
|
309
|
+
|
310
|
+
def draw
|
311
|
+
# screen manipulation here
|
312
|
+
end
|
313
|
+
|
314
|
+
# Called when we enter the game state
|
315
|
+
def setup
|
316
|
+
@player.angle = 0 # point player upwards
|
317
|
+
end
|
318
|
+
|
319
|
+
# Called when we leave the game state
|
320
|
+
def finalize
|
321
|
+
push_game_state(Menu) # switch to game state "Menu"
|
322
|
+
end
|
323
|
+
|
324
|
+
end
|
325
|
+
|
326
|
+
Looks familiar yet?
|
327
|
+
You can activate the above game state in 2 ways
|
328
|
+
|
329
|
+
class Game < Chingu::Window
|
330
|
+
def initialize
|
331
|
+
#
|
332
|
+
# 1) Create a new Intro-object and activate it (pushing to the top).
|
333
|
+
# This version makes more sense if you want to pass parameters to the gamestate, for example:
|
334
|
+
# push_game_state(Level.new(:level_nr => 10))
|
335
|
+
#
|
336
|
+
push_game_state(Intro.new)
|
337
|
+
|
338
|
+
#
|
339
|
+
# 2) This leaves the actual object-creation to the game state manager.
|
340
|
+
# Intro#initialize() is called, then Intro#setup()
|
341
|
+
#
|
342
|
+
push_game_state(Intro)
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
Another example:
|
347
|
+
|
348
|
+
class Game < Chingu::Window
|
349
|
+
def initialize
|
350
|
+
#
|
351
|
+
# We start by pushing Menu to the game state stack, making it active as the only state on stack.
|
352
|
+
# :setup => :false which will skip setup() from being called (standard when switching to a new state)
|
353
|
+
#
|
354
|
+
push_game_state(Menu, :setup => false)
|
355
|
+
|
356
|
+
#
|
357
|
+
# We push another game state to the stack, Play. We now have 2 states, which active being first / active.
|
358
|
+
#
|
359
|
+
# :finalize => false will skip setup() from being called on game state
|
360
|
+
# that's being pushed down the stack, in this case Intro.setup().
|
361
|
+
#
|
362
|
+
push_game_state(Play, :finalize => false)
|
363
|
+
|
364
|
+
#
|
365
|
+
# This would remove Play state from the stack, going back to the Menu-state. But also:
|
366
|
+
# .. skipping the standard call to Menu#setup (the new game state)
|
367
|
+
# .. skipping the standard call to Play#finalize (the current game state)
|
368
|
+
#
|
369
|
+
# :setup => false can for example be useful when pop'ing a Pause game state. (see example4.rb)
|
370
|
+
#
|
371
|
+
pop_game_state(:setup => false, :finalize => :false)
|
372
|
+
|
373
|
+
#
|
374
|
+
# Replace the current game state with a new one.
|
375
|
+
# :setup and :finalize options are available here as well.
|
376
|
+
#
|
377
|
+
switch_game_state(Credits)
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
A GameState in Chingu is just a class with the following instance methods:
|
382
|
+
|
383
|
+
* initialize() - called only once with push_game_state(Intro) but every time with push_game_state(Intro.new)
|
384
|
+
* setup() - called each time the game state becomes active.
|
385
|
+
* button_down(id) - Called when a button is down
|
386
|
+
* button_up(id) - Called when a button is released
|
387
|
+
* update() - just as in your normal game loop, put your game logic here.
|
388
|
+
* draw() - just as in your normal game loop, put your screen manipulation here.
|
389
|
+
* finalize() - called when a game state de-activated (for example by pushing a new one on top with push_game_state)
|
390
|
+
|
391
|
+
Chingu::Window automatically creates a @game_state_manager and makes it accessible in our game loop.
|
392
|
+
By default the game loop calls update() / draw() on the the current game state.
|
393
|
+
|
394
|
+
Chingu also has a couple of helpers-methods for handling the game states:
|
395
|
+
In a main loop or in a game state:
|
396
|
+
* push_game_state(state) - adds a new gamestate on top of the stack, which then becomes the active one
|
397
|
+
* pop_game_state - removes active gamestate and activates the previous one
|
398
|
+
* switch_game_state(state) - replaces current game state with a new one
|
399
|
+
* current_game_state - returns the current game state
|
400
|
+
* previous_game_state - returns the previous game state (useful for pausing and dialog boxes, see example4.rb)
|
401
|
+
* pop_until_game_state(state) - pop game states until given state is found
|
402
|
+
* clear_game_states - removes all game states from stack
|
403
|
+
|
404
|
+
To switch to a certain gamestate with a keypress use Chingus input handler:
|
405
|
+
class Intro < Chingu::GameState
|
406
|
+
def setup
|
407
|
+
self.input = { :space => lambda{push_gamestate(Menu.new)} }
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
Or Chingus shortcut:
|
412
|
+
|
413
|
+
class Intro < Chingu::GameState
|
414
|
+
def setup
|
415
|
+
self.input = { :space => Menu }
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
Chingus inputhandler will detect that Menu is a gamestate-class, create a new instance, cache it and activate it with push_game_state().
|
420
|
+
|
421
|
+
=== Traits
|
422
|
+
Traits (often called behaivors) is a way of adding logic to any class inheriting from BasicGameObject / GameObject.
|
423
|
+
Chingus trait-implementation is just ordinary ruby modules with 3 special methods:
|
424
|
+
- setup_trait
|
425
|
+
- update_trait
|
426
|
+
- draw_trait
|
427
|
+
Each of those 3 methods must call "super" to continue the trait-chain.
|
428
|
+
|
429
|
+
The flow for a game object then becomes:
|
430
|
+
|
431
|
+
-- creating the object
|
432
|
+
1) GameObject#initialize(options)
|
433
|
+
2) GameObject#setup_trait(options)
|
434
|
+
-- each game iteration
|
435
|
+
3) GameObject#draw_trait
|
436
|
+
4) GameObject#draw
|
437
|
+
5) GameObject#update_trait
|
438
|
+
6) GameObject#update
|
439
|
+
|
440
|
+
There's a couple of traits included as default in Chingu:
|
441
|
+
|
442
|
+
==== Trait "timer"
|
443
|
+
Adds timer functionallity to your game object
|
444
|
+
during(300) { @color = Color.new(0xFFFFFFFF) } # forces @color to white ever update for 300 ms
|
445
|
+
after(400) { self.destroy } # destroy object after 400 ms
|
446
|
+
between(1000,2000) { self.rotate(10) } # starting after 1 second, call rotate(10) every update during 1 second
|
447
|
+
|
448
|
+
==== Trait "velocity"
|
449
|
+
Adds variables velocity_x, velocity_y, acceleration_x, acceleration_y, max_velocity to game object.
|
450
|
+
They modify x, y as you would expect. *speed / angle will come*
|
451
|
+
|
452
|
+
|
453
|
+
==== (IN DEVELOPMENT) Trait "effect"
|
454
|
+
Adds ability to automaticly fade, rotate and scale game objects.
|
455
|
+
* API isn't stabilized yet! *
|
456
|
+
|
457
|
+
==== (IN DEVELOPMENT) Trait "collision_detection"
|
458
|
+
Adds class and instance methods for basic collision detection.
|
459
|
+
|
460
|
+
# Class method example
|
461
|
+
# This will collide all Enemy-instances with all Bullet-instances using the attribute #radius from each object.
|
462
|
+
Enemy.each_radius_collision(Bullet) do |enemy, bullet|
|
463
|
+
end
|
464
|
+
|
465
|
+
# You can also use the instance methods. This will use the Rect bounding_box from @player and each EnemyRocket-object.
|
466
|
+
@player.each_bounding_box_collision(EnemyRocket) do |player, enemyrocket|
|
467
|
+
player.die!
|
468
|
+
end
|
469
|
+
|
470
|
+
* API isn't stabilized yet! *
|
471
|
+
|
472
|
+
==== (IN DEVELOPMENT) Trait "retrofy"
|
473
|
+
Providing easier handling of the "retrofy" effect (non-blurry zoom)
|
474
|
+
Aims to help out when using zoom-factor to create a retrofeeling with big pixels.
|
475
|
+
Provides screen_x and screen_y which takes the zoom into account
|
476
|
+
Also provides new code for draw() which uses screen_x / screen_y instead of x / y
|
477
|
+
|
478
|
+
|
479
|
+
=== Assets / Paths
|
480
|
+
|
481
|
+
You might wonder why this is necessary in the straight Gosu example:
|
482
|
+
ROOT_PATH = File.dirname(File.expand_path(__FILE__))
|
483
|
+
@image = Image.new(File.join(ROOT_PATH, "media", "player.png"))
|
484
|
+
|
485
|
+
It enables you to start your game from any directory and it will still find your assets (pictures, samples, fonts etc..) correctly.
|
486
|
+
For a local development version this might not be important, you're likely to start the game from the games root-dir.
|
487
|
+
But as soon as you try to deploy (for example to windows with OCRA - http://github.com/larsch/ocra/tree/master) you'll run into trouble of you don't do it like that.
|
488
|
+
|
489
|
+
Chingu solves this problem behind the scenes for the most common assets. The 2 lines above can be replaced with:
|
490
|
+
Image["player.png"]
|
491
|
+
|
492
|
+
You also have:
|
493
|
+
Sound["shot.png"]
|
494
|
+
Song["intromusic.ogg"]
|
495
|
+
|
496
|
+
By default Image, Sound and Sound searches the current directory and directory "media".
|
497
|
+
Add your own searchpaths like this:
|
498
|
+
Gosu::Image.autoload_dirs << File.join($window.root, "gfx")
|
499
|
+
Gosu::Sound.autoload_dirs << File.join($window.root, "samples")
|
500
|
+
|
501
|
+
This will add \path\to\your\game\gfx and \path\to\your\game\samples to Image and Sound.
|
502
|
+
|
503
|
+
Thanks to Jacious of rubygame-fame (http://rubygame.org/) for his named resource code powering this.
|
504
|
+
|
505
|
+
Tiles and fonts are trickier since they require extra parameters so you'll have to do those the ordinary way.
|
506
|
+
You have $window.root (equivalent to ROOT_PATH above) for free though which points to the dir containing the game.
|
507
|
+
|
508
|
+
=== Text
|
509
|
+
Text is a class to give the use of Gosu::Font more rubyish feel and fit it better into Chingu.
|
510
|
+
|
511
|
+
# Pure Gosu
|
512
|
+
@font = Gosu::Font.new($window, "verdana", 30)
|
513
|
+
@font.draw("A Text", 200, 50, 55, 2.0)
|
514
|
+
|
515
|
+
# Chingu
|
516
|
+
@text = Chingu::Text.create(:text => "A Text", :x => 200, :y => 50, :zorder => 55, :factor_x => 2.0)
|
517
|
+
@text.draw
|
518
|
+
|
519
|
+
@text.draw is usually not needed as Text is a GameObject and therefore automaticly updated/drawn (it #create is used instead of #new)
|
520
|
+
It's not only that the second example is readable by ppl now even familiar with Gosu, @text comes with a number of changeable properties, x,y,zorder,angle,factor_x,color,mode etc.
|
521
|
+
Set a new x or angle or color and it will instantly update on screen.
|
522
|
+
|
523
|
+
|
524
|
+
== MISC / FAQ
|
525
|
+
=== How do I access my main-window easily?
|
526
|
+
Chingu keeps a global variable, $window, which contains the Chingu::Window instance.
|
527
|
+
Since Chingu::Window is just Gosu::Window + some cheese you can do your $window.button_down?, $window.draw_line() etc from anywhere.
|
528
|
+
See http://www.libgosu.org/rdoc/classes/Gosu/Window.html for a full set of methods.
|
529
|
+
|
530
|
+
=== How did you decide on naming of methods / classes?
|
531
|
+
There's 1 zillion ways of naming stuff. As a general guideline I've tried to follow Gosus naming.
|
532
|
+
If Gosu didn't have a good name for a certain thing/method I've checked Ruby itself and then Rails since alot
|
533
|
+
of Ruby-devs are familiar with Rails.
|
534
|
+
|
535
|
+
== TODO:
|
536
|
+
* add :padding and :align => :topleft etc to class Text
|
537
|
+
* (skip) rename Chingu::Window so 'include Chingu' and 'include Gosu' wont make Window collide
|
538
|
+
* (done) BasicObject vs GameObject vs ScreenObject => Became BasicGameObject and GameObject
|
539
|
+
* (50%) some kind of componentsystem for GameObject (which should be cleaned up)
|
540
|
+
* (done) scale <--> growth parameter. See trait "effect"
|
541
|
+
* (done) Enemy.all ... instead of game_objects_of_type(Enemy) ? could this be cool / understandable?
|
542
|
+
* (done) Don't call .update(time) with timeparameter, make time available thru other means when needed.
|
543
|
+
* (10% done) debug screen / game state.. check out shawn24's elite irb sollution :)
|
544
|
+
* (done) Complete the input-definitions with all possible inputs (keyboard, gamepad, mouse)!
|
545
|
+
* (done) Complete input-stuff with released-states etc
|
546
|
+
* (done) More gfx effects, for example: fade in/out to a specific color (black makes sense between levels).
|
547
|
+
* (posted request on forums) Summon good proven community gosu snippets into Chingu
|
548
|
+
* (done) Generate docs @ ippa.github.com- http://rdoc.info/projects/ippa/chingu !
|
549
|
+
* (done) A good scene-manager to manage welcome screens, levels and game flow- GameStateManager / GameState !
|
550
|
+
* (20% done) make a playable simple game in examples\ that really depends on game states
|
551
|
+
* (done) Make a gem- first gem made on github
|
552
|
+
* (done) Automate gemgenning rake-task even more
|
553
|
+
* (done) More examples when effects are more complete
|
554
|
+
* class ChipmunkObject
|
555
|
+
* (done) class Actor/MovingActor with maybe a bit more logic then the basic GameObject.
|
556
|
+
* (60% done) Spell check all docs, sloppy spelling turns ppl off. tnx jduff ;).
|
557
|
+
* Tests
|
558
|
+
* (done) Streamline fps / tick code
|
559
|
+
* (done) Encapsulate Font.new / draw_rot with a "class Text < GameObject"
|
560
|
+
* (10% done) Make it possible for ppl to use the parts of Chingu they like
|
561
|
+
* (done) At least make GameStateManager really easy to use with pure Gosu / Document it!
|
562
|
+
* (50% done) Get better at styling rdocs
|
563
|
+
* (done) all �gamestate� ? �game state� ? it's "game state"
|
564
|
+
* (skipping) intergrate MovieMaker - solve this with traits instead.
|
565
|
+
* A more robust game state <-> game_object system to connect them together.
|
566
|
+
* FIX example4: :p => Pause.new would Change the "inside_game_state" to Pause and make @player belong to Pause.
|
567
|
+
|
568
|
+
== WHY?
|
569
|
+
* Plain Gosu is very minimalistic, perfect to build some higher level logic on!
|
570
|
+
* Deployment and asset handling should be simple
|
571
|
+
* Managing game states/scenes (intros, menus, levels etc) should be simple
|
572
|
+
* There are patterns in game development
|
573
|
+
|
574
|
+
== OPINIONS
|
575
|
+
* Less code is usually better
|
576
|
+
* Hash arguments FTW. And it becomes even better in 1.9.
|
577
|
+
* Don't separate too much from Gosus core-naming
|
578
|
+
|
579
|
+
== CREDITS:
|
580
|
+
* Jacius of Rubygame (For doing cool stuff that's well documented as re-usable). So far rect.rb and named_resource.rb is taken from Rubygame.
|
581
|
+
* Jduff for input / commits.
|
582
|
+
* Jlnr,Philymore,Shawn24,JamesKilton for constructive feedback/discussions.
|
583
|
+
* Thanks to http://github.com/tarcieri for require_all code, good stuff
|
584
|
+
|
585
|
+
== REQUIREMENTS:
|
586
|
+
* Gosu latest version
|
587
|
+
* Ruby 1.8.7+ (Works with 1.9+ as well!)
|
588
|
+
|