ippa-chingu 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,5 +1,8 @@
1
+ === 0.0.3 / 2009-08-14
2
+ Too much to list. remade inputsystem. gamestates are better. window.rb is cleaner. lots of small bugfixes. Bigger readme.
3
+
1
4
  === 0.0.2 / 2009-08-10
2
5
  tons of new stuff and fixes. complete keymap. gamestate system. moreexamples/docs. better game_object.
3
6
 
4
7
  === 0.0.1 / 2009-08-05
5
- first release
8
+ first release
data/README.rdoc CHANGED
@@ -34,17 +34,27 @@ Chingu consists of the following core classes:
34
34
  The main window, use it at you use Gosu::Window.
35
35
 
36
36
  === Chingu::GameObject
37
- Use for your in game objects, got everything to put them on the screen.
37
+ Use for all your in game objects. The player, the enemies, the bullets, the powerups, the loot laying around.
38
+ 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.
39
+ It also gives you a couple of bonuses with chingu, as automatic updates/draws and easier input-mapping.
38
40
  Has either Chingu::Window or a Chingu::GameState as "owner".
39
41
 
40
42
  === Chingu::Text
41
- Makes use of Gosu::Font more rubyish and powerful
43
+ Makes use of Gosu::Font more rubyish and powerful.
44
+ In it's core, another Chingu::GameObject + Gosu::Font.
45
+
46
+ === Chingu::GameStateManager
47
+ Keeps track of the game states. Implements a stack-based system with push_game_state and pop_game_state.
42
48
 
43
49
  === Chingu::GameState
44
- A "standalone game loop" that can be switched on/off to control game flow.
50
+ A "standalone game loop" that can be activated and deactivated to control game flow.
51
+ A game state is very much like a main gosu window. You define update() and draw() in a gamestate.
52
+ It comes with 2 extras that main window doesn't have. #setup (called when activated) and #finalize (called when deactivated)
53
+
54
+ If using game states, the flow of draw/update/button_up/button_down is:
55
+ Chingu::Window --> Chingu::GameStateManager --> Chingu::GameState.
56
+ For example, inside game state Menu you call push_game_state(Level). When Level exists, it will go back to Menu.
45
57
 
46
- === Chingu::GameStateManager
47
- Keeps track of the game states. The flow of draw/update/button_down is Chingu::Window --> Chingu::GameStateManager --> Chingu::GameState.
48
58
 
49
59
  == THE BASICS
50
60
 
@@ -105,10 +115,10 @@ Chingu doesn't change any fundamental concept of Gosu, but it will make the abov
105
115
  #
106
116
  class Game < Chingu:Window
107
117
  def initialize
108
- super # This is always needed
118
+ super # This is always needed if you want to take advantage of what chingu offers
109
119
  #
110
120
  # Player will automaticly be updated and drawn since it's a Chingu::GameObject
111
- # You'll need your own Game#update/#draw after a while, but just put #super there and Chingu can do its thing!
121
+ # You'll need your own Game#update/#draw after a while, but just put #super there and Chingu can do its thing.
112
122
  #
113
123
  @player = Player.new
114
124
  @player.input = {:left => :move_left, :right => :move_right}
@@ -178,6 +188,65 @@ I've chose to base it around Image#draw_rot. So basically all the arguments that
178
188
  #
179
189
  @player = Player.new(:draw => false, :update => false)
180
190
 
191
+ === Input
192
+ One of the core things I wanted a more natural way of inputhandling.
193
+ You can define input -> actions on Chingu::Window, Chingu::GameState and Chingu::GameObject.
194
+ Like this:
195
+
196
+ #
197
+ # When left arrow is pressed, call @player.turn_left ... and so on.
198
+ #
199
+ @player.input = { :left => :turn_left, :right => :turn_right, :left => :halt_left, :right => :halt_right }
200
+
201
+
202
+ #
203
+ # In Gosu the equivalent would be:
204
+ #
205
+ def button_down(id)
206
+ @player.turn_left if id == Button::KbLeft
207
+ @player.turn_right if id == Button::KbRight
208
+ end
209
+
210
+ def button_up(id)
211
+ @player.halt_left if id == Button::KbLeft
212
+ @player.halt_right if id == Button::KbRight
213
+ end
214
+
215
+
216
+ Another more complex example:
217
+
218
+ #
219
+ # So what happens here?
220
+ #
221
+ # Pressing P would create an game state out of class Pause, cache it and activate it.
222
+ # Pressing ESC would call Play#close
223
+ # Holding down LEFT would call Play#move_left on every game iteration
224
+ # Holding down RIGHT would call Play#move_right on every game iteration
225
+ # Relasing SPACE would call Play#fire
226
+ #
227
+
228
+ class Play < Chingu::GameState
229
+ def initialize
230
+ self.input = { :p => Pause, :escape => :close, :holding_left => :move_left, :holding_right => :move_right, :released_space => :fire }
231
+ end
232
+ end
233
+ class Pause < Chingu::GameState
234
+ # pause logic here
235
+ end
236
+
237
+ In Gosu the above code would include code in button_up(), button_down() and a check for button_down?() in update().
238
+
239
+ Every symbol can be prefixed by either "released_" or "holding_" while no prefix at all defaults to pressed once.
240
+ So, why not :up_space or :relase_space instead of :released_space?
241
+ Or :hold_left or :down_left instead of :holding_left?
242
+
243
+ :up_space doesn't sound like english, :release_space sounds more like a command then an event.
244
+
245
+ :holding_left sounds like something that's happening over a period of time, not a single trigger, which corresponds good to what's happening when using it.
246
+
247
+ And with the default :space => :something youd imagine that :something is called once. You press :space once, :something get's executed once.
248
+
249
+
181
250
  === GameState / GameStateManager
182
251
  Chingu incorporates a basic push/pop game state system (as discussed here: http://www.gamedev.net/community/forums/topic.asp?topic_id=477320).
183
252
 
@@ -186,7 +255,9 @@ Game states is a way of organizing your intros, menus, levels.
186
255
  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.
187
256
 
188
257
 
258
+ # A simple GameState-example
189
259
  class Intro < Chingu::GameState
260
+
190
261
  def update
191
262
  # game logic here
192
263
  end
@@ -194,44 +265,62 @@ Game states aren't complicated. In Chingu a GameState is a class that behaves mo
194
265
  def draw
195
266
  # screen manipulation here
196
267
  end
197
-
198
- def button_down(id)
199
- # called when a button is pressed
268
+
269
+ # Called when we enter the game state
270
+ def setup
271
+ @player.angle = 0 # point player upwards
200
272
  end
201
273
 
274
+ # Called when we leave the current game state
202
275
  def finalize
203
- push_gamestate(Menu.new) # Called when Intro dies for whatever reason.
276
+ push_game_state(Menu) # switch to game state "Menu"
204
277
  end
205
-
206
- # etc etc
278
+
207
279
  end
208
280
 
209
281
  Looks familiar ye?
210
- Active that game state/game loop in your main window (which is always the spider in the net).
282
+ You can activate the above game state in 2 ways
211
283
 
212
284
  class Game < Chingu::Window
213
285
  def initialize
214
- push_gamestate(Intro.new)
286
+ #
287
+ # 1) Create a new Intro-object and activate it (pushing to the top).
288
+ # This version makes more sense if you want to pass parameters to the gamestate, for example:
289
+ # push_game_state(Level.new(:level_nr => 10))
290
+ #
291
+ push_game_state(Intro.new)
292
+
293
+ #
294
+ # 2) This leaves the actual object-creation to the game state manager.
295
+ # This results in only 1 object is ever created of class 'Intro'.
296
+ # The second time 'push_game_state(Intro)' is called, it will re-use the last one.
297
+ # This means code in Intro#initialize() is only called once, Intro#setup() is called everytime Intro is activated though.
298
+ #
299
+ push_game_state(Intro)
215
300
  end
216
301
  end
217
302
 
218
303
  A GameState in Chingu is just a class with the following instance methods:
219
304
 
220
- * setup() - called when game state becomes active (switch_gamestate(gamestate) for example)
305
+ * initialize() - called only once with push_game_state(Intro) but everytime with push_game_state(Intro.new)
306
+ * setup() - called each time the game state becomes active.
221
307
  * button_down(id) - Called when a button is down
222
308
  * button_up(id) - Called when a button is released
223
309
  * update() - just as in your normal game loop, put your game logic here.
224
310
  * draw() - just as in your normal game loop, put your screen manipulation here.
225
- * finalize() - called when a game state is finished
311
+ * finalize() - called when a game state de-activated (for example by pushing a new one on top with push_game_state)
226
312
 
227
313
  Chingu::Window automatically creates a @game_state_manager and makes it accessible in our game loop.
228
314
  By default the game loop calls update() / draw() on the the current game state.
229
315
 
230
- Chingu also has a couple of helpers to easy change between game states.
316
+ Chingu also has a couple of helpers-methods for handling the game states:
231
317
  In a main loop or in a game state:
232
- * push_gamestate(state) - adds a new gamestate, which then becomes the active one
233
- * pop_gamestate - removes active gamestate and activates the previous one
234
- * switch_gamestate(state) - pop all gamestates until given state is found
318
+ * push_game_state(state) - adds a new gamestate on top of the stack, which then becomes the active one
319
+ * pop_game_state - removes active gamestate and activates the previous one
320
+ * current_game_state - returns the current game state
321
+ * previous_game_state - returns the previous game state (useful for pausing and dialog boxes, see example4.rb)
322
+ * pop_until_game_state(state) - pop game states until given state is found
323
+ * clear_game_states - removes all game states from stack
235
324
 
236
325
  To switch to a certain gamestate with a keypress use Chingus input handler:
237
326
  class Intro < Chingu::GameState
@@ -240,15 +329,15 @@ To switch to a certain gamestate with a keypress use Chingus input handler:
240
329
  end
241
330
  end
242
331
 
243
- Or Chingus pretty shortcut:
332
+ Or Chingus shortcut:
244
333
 
245
334
  class Intro < Chingu::GameState
246
335
  def setup
247
- self.input = { :space => Menu } # { :space => Menu.new } works as well.
336
+ self.input = { :space => Menu } # or { :space => Menu.new } if you want to create a new object each time.
248
337
  end
249
338
  end
250
339
 
251
- Chingu will detect that Menu is a gamestate-class and call push_gamestate on it when space is pressed inside Intro.
340
+ Chingus inputhandler will detect that Menu is a gamestate-class, create a new instance, cache it and activate it with push_game_state().
252
341
 
253
342
  === Assets / Paths
254
343
 
@@ -285,12 +374,13 @@ It's not only that the second example is readable by ppl now even familiar with
285
374
 
286
375
  == TODO:
287
376
  * (done) Complete the input-definitions with all possible inputs (keyboard, gamepad, mouse)!
288
- * Complete input-stuff with released-states etc
377
+ * (done) Complete input-stuff with released-states etc
289
378
  * More gfx effects, for example: fade in/out to a specific color (black makes sense between levels).
290
- * Summon good proven community gosu snippets into Chingu
379
+ * (posted request on forums) Summon good proven community gosu snippets into Chingu
291
380
  * (done) Generate docs @ ippa.github.com- http://rdoc.info/projects/ippa/chingu !
292
381
  * (done) A good scene-manager to manage welcome screens, levels and game flow- GameStateManager / GameState !
293
382
  * More docs
383
+ * make a playable simple game in examples\ that really depends on game states
294
384
  * (done) Make a gem- first gem made on github
295
385
  * Automate gemgenning rake-task even more
296
386
  * More examples when effects are more complete
@@ -298,14 +388,14 @@ It's not only that the second example is readable by ppl now even familiar with
298
388
  * class Actor/MovingActor with maybe abit more logic then the basic GameObject. Would ppl find is useful?
299
389
  * Spell check all docs, sloppy spelling turns ppl off.
300
390
  * Tests
301
- * Streamline fps / tick code
391
+ * (done) Streamline fps / tick code
302
392
  * (done) Encapsulate Font.new / draw_rot with a "class Text < GameObject"
303
- * Make it possible for ppl to use the parts of Chingu they like
393
+ * (10% done) Make it possible for ppl to use the parts of Chingu they like
304
394
  * A more robust game state <-> game_object system to connect them together.
305
395
  * Get better at styling rdocs
306
- * all �gamestate� ? �game state� ?
307
- * FIX example4: :p => Pause.new would Change the "inside_game_state" to Pause and make @player belong to Pause.
396
+ * (done) all �gamestate� ? �game state� ? it's "game state"
308
397
  * intergrate rubygame_movie_make (maybe after a rename, GameAutomator? GameSequence?
398
+ * FIX example4: :p => Pause.new would Change the "inside_game_state" to Pause and make @player belong to Pause.
309
399
 
310
400
  == WHY?
311
401
  * Plain Gosu is very minimalistic, perfect to build some higher level logic on!
data/chingu.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{chingu}
5
- s.version = "0.2.0"
5
+ s.version = "0.3.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["ippa"]
9
- s.date = %q{2009-08-10}
9
+ s.date = %q{2009-08-14}
10
10
  s.description = %q{Game framework built on top of the OpenGL accelerated game lib Gosu. It adds simple yet powerfull game states, prettier inputhandling, deploymentsafe asset-handling, a basic re-usable game object and automation of common task.}
11
11
  s.email = ["ippa@rubylicio.us"]
12
12
  s.extra_rdoc_files = ["History.txt", "Manifest.txt"]
@@ -24,11 +24,11 @@ Gem::Specification.new do |s|
24
24
  s.specification_version = 2
25
25
 
26
26
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
- s.add_development_dependency(%q<hoe>, [">= 2.3.2"])
27
+ s.add_development_dependency(%q<hoe>, [">= 2.3.3"])
28
28
  else
29
- s.add_dependency(%q<hoe>, [">= 2.3.2"])
29
+ s.add_dependency(%q<hoe>, [">= 2.3.3"])
30
30
  end
31
31
  else
32
- s.add_dependency(%q<hoe>, [">= 2.3.2"])
32
+ s.add_dependency(%q<hoe>, [">= 2.3.3"])
33
33
  end
34
34
  end
data/examples/example1.rb CHANGED
@@ -15,7 +15,13 @@ class Game < Chingu::Window
15
15
  def initialize
16
16
  super
17
17
  @player = Player.new(:x => 200, :y => 200, :image => Image["spaceship.png"])
18
- @player.input = {:left => :move_left, :right => :move_right, :up => :move_up, :down => :move_down}
18
+ @player.input = { :holding_left => :move_left, :holding_right => :move_right,
19
+ :holding_up => :move_up, :holding_down => :move_down}
20
+ end
21
+
22
+ def update
23
+ super
24
+ self.caption = "FPS: #{self.fps} milliseconds_since_last_tick: #{self.milliseconds_since_last_tick}"
19
25
  end
20
26
  end
21
27
 
data/examples/example2.rb CHANGED
@@ -15,7 +15,11 @@ class Game < Chingu::Window
15
15
  super
16
16
 
17
17
  @player = Player.new(:x => 200, :y => 200, :image => Image["spaceship.png"])
18
- @player.input = {:left => :move_left, :right => :move_right, :up => :move_up, :down => :move_down, :space => :fire}
18
+ @player.input = { :holding_left => :move_left,
19
+ :holding_right => :move_right,
20
+ :holding_up => :move_up,
21
+ :holding_down => :move_down,
22
+ :space => :fire}
19
23
  end
20
24
 
21
25
  #
@@ -43,18 +47,23 @@ class Game < Chingu::Window
43
47
 
44
48
  end
45
49
 
50
+ #
51
+ # Our Player
52
+ #
46
53
  class Player < Chingu::GameObject
54
+ def initialize(options = {})
55
+ super
56
+ @image = Image["spaceship.png"]
57
+ end
58
+
47
59
  def move_left; @x -= 1; end
48
60
  def move_right; @x += 1; end
49
61
  def move_up; @y -= 1; end
50
- def move_down; @y += 1; end
51
-
62
+ def move_down; @y += 1; end
63
+
52
64
  def fire
53
65
  Bullet.new(:x => @x, :y => @y)
54
- end
55
-
56
- def update
57
- end
66
+ end
58
67
  end
59
68
 
60
69
  class Bullet < Chingu::GameObject
@@ -67,7 +76,7 @@ class Bullet < Chingu::GameObject
67
76
  end
68
77
 
69
78
  # Move the bullet forward
70
- def update
79
+ def update(time)
71
80
  @y -= 2
72
81
  end
73
82
 
data/examples/example3.rb CHANGED
@@ -9,7 +9,7 @@ include Gosu
9
9
  class Game < Chingu::Window
10
10
  def initialize
11
11
  super
12
- self.input = {:left => :scroll_left, :right => :scroll_right, :escape => :close}
12
+ self.input = {:holding_left => :scroll_left, :holding_right => :scroll_right, :escape => :close}
13
13
 
14
14
  @parallax = Chingu::Parallax.new(:x => 0, :y => 0, :center_x => 0, :center_y => 0)
15
15
 
data/examples/example4.rb CHANGED
@@ -5,7 +5,7 @@ include Gosu
5
5
  #
6
6
  # Example demonstrating jumping between 4 different game states.
7
7
  #
8
- # push_gamestate, pop_gamestate and previous_gamestate are 3 helpers that Chingu mixes in
8
+ # push_game_state, pop_game_state, current_game_state previous_game_state are 4 helper-methods that Chingu mixes in
9
9
  # into Chingu::Window and Chingu::GameState
10
10
  #
11
11
  # Behind the scenes they work against @game_state_manager that's autocreated within Chingu::Window.
@@ -19,7 +19,7 @@ include Gosu
19
19
  #
20
20
  # 3) @game_state_manager calls draw / update on the current active game state
21
21
  #
22
- # 4) Each gamestate keeps a collection @game_objects which it calls draw / update on.
22
+ # 4) Each game state keeps a collection @game_objects which it calls draw / update on.
23
23
  # Any object based on Chingu::GameObject (In this example Player and Text) automatically
24
24
  # gets added to the correct state or or main window.
25
25
  #
@@ -29,19 +29,18 @@ include Gosu
29
29
  #
30
30
  class Game < Chingu::Window
31
31
  def initialize
32
- super
33
- push_gamestate(Intro)
32
+ super
33
+
34
+ push_game_state(Intro)
34
35
 
35
36
  # Yes you can do crazy things like this :)
36
- self.input = { :left_mouse_button => lambda{Chingu::Text.new(:text => "Woff!")}}
37
+ self.input = { :left_mouse_button => lambda{Chingu::Text.new(:text => "Woff!")}, :esc => :close}
37
38
  end
38
39
  end
39
40
 
40
- #
41
- # Our Player
42
- #
41
+ # Our Player
43
42
  class Player < Chingu::GameObject
44
- def initialize(options)
43
+ def initialize(options = {})
45
44
  super
46
45
  @image = Image["spaceship.png"]
47
46
  end
@@ -49,16 +48,42 @@ class Player < Chingu::GameObject
49
48
  def move_left; @x -= 1; end
50
49
  def move_right; @x += 1; end
51
50
  def move_up; @y -= 1; end
52
- def move_down; @y += 1; end
51
+ def move_down; @y += 1; end
52
+
53
+ def fire
54
+ Bullet.new(:x => @x, :y => @y)
55
+ end
56
+ end
57
+
58
+ # The bullet the Player fires
59
+ class Bullet < Chingu::GameObject
60
+ def initialize(options)
61
+ super
62
+ @image = Image["fire_bullet.png"]
63
+ end
64
+
65
+ def update(time)
66
+ @y -= 2
67
+ end
53
68
  end
54
69
 
70
+
55
71
  #
56
72
  # GAMESTATE #1 - INTRO
57
73
  #
58
74
  class Intro < Chingu::GameState
59
- def setup
60
- @title = Chingu::Text.new(:text=>"Intro (press space)", :x=>200, :y=>50, :size=>30)
61
- self.input = { :space => Menu, :escape => :close }
75
+ def initialize(options)
76
+ super
77
+ @title = Chingu::Text.new(:text=>"Press and release F1", :x=>200, :y=>50, :size=>30)
78
+ self.input = { :f1 => :pressed, :released_f1 => :released, :f2 => Menu}
79
+ end
80
+
81
+ def pressed
82
+ @title.text = "F1 pressed (F2 to continue)"
83
+ end
84
+
85
+ def released
86
+ @title.text = "F1 released (F2 to continue)"
62
87
  end
63
88
  end
64
89
 
@@ -66,9 +91,10 @@ end
66
91
  # GAMESTATE #2 - MENU
67
92
  #
68
93
  class Menu < Chingu::GameState
69
- def setup
70
- @title = Chingu::Text.new(:text => "GameState Menu (press 'm')", :x => 200, :y => 50, :size=>30)
71
- self.input = { :m => Level.new(:level => 10) }
94
+ def initialize(options)
95
+ super
96
+ @title = Chingu::Text.new(:text => "Press 'S' to Start game", :x=>100, :y=>50, :size=>30)
97
+ self.input = { :s => Level.new(:level => 10) }
72
98
  end
73
99
  end
74
100
 
@@ -76,34 +102,56 @@ end
76
102
  # GAMESTATE #3 - LEVEL (Gameplay, yay)
77
103
  #
78
104
  class Level < Chingu::GameState
79
- def setup
80
- @title = Chingu::Text.new(:text=>"Level #{options[:level].to_s}. Pause with 'P'", :x=>200, :y=>10, :size => 30)
81
- @player = Player.new(:x => 200, :y => 200)
82
- @player.input = {:left => :move_left, :right => :move_right, :up => :move_up, :down => :move_down, :left_ctrl => :fire}
105
+ #
106
+ # initialize() is called when you create the game state
107
+ #
108
+ def initialize(options)
109
+ super
110
+ @title = Chingu::Text.new(:text=>"Level #{options[:level].to_s}. P: pause R:restart", :x=>20, :y=>10, :size=>30)
111
+ @player = Player.new
112
+ @player.input = { :holding_left => :move_left,
113
+ :holding_right => :move_right,
114
+ :holding_up => :move_up,
115
+ :holding_down => :move_down,
116
+ :left_ctrl => :fire}
83
117
 
84
118
  #
85
119
  # The input-handler understands gamestates. P is pressed --> push_gamegate(Pause)
120
+ # You can also give it Procs/Lambdas which it will execute when key is pressed.
86
121
  #
87
- self.input = {:p => Pause, :escape => :close}
88
- end
122
+ self.input = {:p => Pause, :r => lambda{ current_game_state.setup } }
123
+ end
124
+
125
+ #
126
+ # setup() is called each time you switch to the game state (and on creation time).
127
+ # You can skip setup by switching with push_game_state(:setup => false) or pop_game_state(:setup => false)
128
+ #
129
+ # This can be useful if you want to display some kind of box above the gameplay (pause/options/info/... box)
130
+ #
131
+ def setup
132
+ # Place player in a good starting position
133
+ @player.x = $window.width/2
134
+ @player.y = $window.height - @player.image.height
135
+ end
89
136
  end
90
137
 
91
138
  #
92
139
  # SPECIAL GAMESTATE - Pause
93
140
  #
94
141
  class Pause < Chingu::GameState
95
- def setup
142
+ def initialize(options)
143
+ super
96
144
  @title = Chingu::Text.new(:text=>"PAUSED (press 'u' to un-pause)", :x=>100, :y=>200, :size=>20, :color => Color.new(0xFF00FF00))
97
145
  self.input = { :u => :un_pause }
98
146
  end
99
147
 
100
148
  def un_pause
101
- pop_gamestate # Return the previous gamestate
149
+ pop_game_state(:setup => false) # Return the previous game state, dont call setup()
102
150
  end
103
151
 
104
152
  def draw
105
- previous_gamestate.draw # Draw prev gamestate onto screen
106
- super # Draw game objects in current game state, this includes Chingu::Texts
153
+ previous_game_state.draw # Draw prev game state onto screen (in this case our level)
154
+ super # Draw game objects in current game state, this includes Chingu::Texts
107
155
  end
108
156
  end
109
157