chingu 0.5.8.2 → 0.5.9
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/README.rdoc +33 -13
- data/chingu.gemspec +2 -2
- data/examples/example13.rb +49 -13
- data/examples/high_score_list.yml +22 -20
- data/lib/chingu.rb +1 -1
- data/lib/chingu/game_object.rb +7 -4
- data/lib/chingu/high_score_list.rb +112 -15
- metadata +2 -2
- metadata.gz.sig +0 -0
data.tar.gz.sig
CHANGED
Binary file
|
data/README.rdoc
CHANGED
@@ -71,15 +71,18 @@ It currently has to be namespaced to Chingu::Traits for "has_trait" to work insi
|
|
71
71
|
== OTHER CLASSES / HELPERS
|
72
72
|
|
73
73
|
=== Chingu::Text
|
74
|
-
Makes use of
|
75
|
-
In it's core, another Chingu::GameObject +
|
74
|
+
Makes use of Image#from_text more rubyish and powerful.
|
75
|
+
In it's core, another Chingu::GameObject + image genning with Image#from_text.
|
76
76
|
|
77
77
|
=== Chingu::Animation
|
78
78
|
Load and interact with tile-based animations. loop, bounce and access invidual frame(s) easily.
|
79
|
-
An "@image = @animation.next
|
79
|
+
An "@image = @animation.next" in your Player#update is usually enough to get you started!
|
80
80
|
|
81
81
|
=== Chingu::Parallax
|
82
|
-
A class for easy
|
82
|
+
A class for easy parallaxscrolling. Add layers with different damping, move the camera to generate a new snapshot. See example3.rb for more.
|
83
|
+
|
84
|
+
=== Chingu::HighScoreList
|
85
|
+
A class to keep track of high scores, limit the list, automatic sorting on score, save/load to disc. See example13.rb for more.
|
83
86
|
|
84
87
|
=== Various Helpers
|
85
88
|
Both $window and game states gets some new graphical helpers, currently only 3, but quite useful:
|
@@ -147,7 +150,7 @@ You're probably familiar with this very common Gosu pattern:
|
|
147
150
|
Game.new.show # Start the Game update/draw loop!
|
148
151
|
|
149
152
|
|
150
|
-
Chingu doesn't change
|
153
|
+
Chingu doesn't change the fundamental concept/flow of Gosu, but it will make the above code cleaner:
|
151
154
|
|
152
155
|
#
|
153
156
|
# We use Chingu::Window instead of Gosu::Window
|
@@ -157,7 +160,7 @@ Chingu doesn't change any fundamental concept of Gosu, but it will make the abov
|
|
157
160
|
super # This is always needed if you want to take advantage of what chingu offers
|
158
161
|
#
|
159
162
|
# Player will automaticly be updated and drawn since it's a Chingu::GameObject
|
160
|
-
# You'll need your own
|
163
|
+
# You'll need your own Chingu::Window#update and Chingu::Window#draw after a while, but just put #super there and Chingu can do its thing.
|
161
164
|
#
|
162
165
|
@player = Player.create
|
163
166
|
@player.input = {:left => :move_left, :right => :move_right}
|
@@ -166,7 +169,8 @@ Chingu doesn't change any fundamental concept of Gosu, but it will make the abov
|
|
166
169
|
|
167
170
|
#
|
168
171
|
# If we create classes from Chingu::GameObject we get stuff for free.
|
169
|
-
# The accessors
|
172
|
+
# The accessors image,x,y,zorder,angle,factor_x,factor_y,center_x,center_y,mode,alpha.
|
173
|
+
# We also get a default #draw which draws the image to screen with the parameters listed above.
|
170
174
|
# You might recognize those from #draw_rot - http://www.libgosu.org/rdoc/classes/Gosu/Image.html#M000023
|
171
175
|
# And in it's core, that's what Chingu::GameObject is, an encapsulation of draw_rot with some extra cheese.
|
172
176
|
# For example, we get automatic calls to draw/update with Chingu::GameObject, which usually is what you want.
|
@@ -193,6 +197,22 @@ Roughly 50 lines became 26 more powerful lines. (you can do @player.angle = 100
|
|
193
197
|
If you've worked with Gosu for a while you're probably tired of passing around the window-parameter.
|
194
198
|
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
199
|
|
200
|
+
The basic flow of Chingu::Window once show() is called is this (this is called one game iteration or game loop):
|
201
|
+
|
202
|
+
- Chingu::Window#draw() is called
|
203
|
+
-- draw() is called on game objects belonging to Chingu::Window
|
204
|
+
-- draw() is called on all game objects belonging to current game state
|
205
|
+
|
206
|
+
- Chingu::Window#update() is called
|
207
|
+
-- Input for Chingu::Window is processed
|
208
|
+
-- Input for all game objects belonging to Chingu::Window is processed
|
209
|
+
-- update() is called on all game objects belonging to Chingu::Window
|
210
|
+
-- Input for current game state is processed
|
211
|
+
-- Input for game objects belonging to current game state is processed
|
212
|
+
-- update() is called on all game objects belonging to current game state
|
213
|
+
|
214
|
+
... the above is repeatet until game exists.
|
215
|
+
|
196
216
|
=== Chingu::GameObject
|
197
217
|
This is our basic "game unit"-class, meaning most in game objects (players, enemies, bullets etc) should be inherited from Chingu::GameObject.
|
198
218
|
The basic ideas behind it are:
|
@@ -473,19 +493,20 @@ The flow for a game object then becomes:
|
|
473
493
|
There's a couple of traits included as default in Chingu:
|
474
494
|
|
475
495
|
==== Trait "timer"
|
476
|
-
Adds timer
|
496
|
+
Adds timer functionality to your game object
|
477
497
|
during(300) { @color = Color.new(0xFFFFFFFF) } # forces @color to white every update for 300 ms
|
478
498
|
after(400) { self.destroy } # destroy object after 400 ms
|
479
499
|
between(1000,2000) { self.rotate(10) } # starting after 1 second, call rotate(10) every update during 1 second
|
500
|
+
every(2000) { Sound["bleep.wav"].play } # play bleep.wav every 2 seconds
|
480
501
|
|
481
502
|
==== Trait "velocity"
|
482
|
-
Adds
|
503
|
+
Adds accessors velocity_x, velocity_y, acceleration_x, acceleration_y, max_velocity to game object.
|
483
504
|
They modify x, y as you would expect. *speed / angle will come*
|
484
505
|
|
485
506
|
|
486
|
-
====
|
487
|
-
Adds
|
488
|
-
|
507
|
+
==== Trait "effect"
|
508
|
+
Adds accessors rotation_rate, fade_rate and scale_rate to game object.
|
509
|
+
They modify angle, alpha and factor_x/factor_y each update. Since this is pretty easy to do yourself this trait might be up for deprecation.
|
489
510
|
|
490
511
|
==== (IN DEVELOPMENT) Trait "collision_detection"
|
491
512
|
Adds class and instance methods for basic collision detection.
|
@@ -510,7 +531,6 @@ Also provides new code for draw() which uses screen_x / screen_y instead of x /
|
|
510
531
|
|
511
532
|
|
512
533
|
|
513
|
-
|
514
534
|
=== Assets / Paths
|
515
535
|
|
516
536
|
You might wonder why this is necessary in the straight Gosu example:
|
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.5.
|
5
|
+
s.version = "0.5.9"
|
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-10-
|
9
|
+
s.date = %q{2009-10-25}
|
10
10
|
s.description = %q{OpenGL accelerated 2D game framework for Ruby.
|
11
11
|
Builds on the awesome Gosu (Ruby/C++) which provides all the core functionality.
|
12
12
|
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.}
|
data/examples/example13.rb
CHANGED
@@ -10,10 +10,10 @@ include Chingu
|
|
10
10
|
class Game < Chingu::Window
|
11
11
|
def initialize
|
12
12
|
super(640,400)
|
13
|
-
self.input = {:esc => :exit}
|
14
|
-
self.caption = "Example of Chingus HighScore class"
|
13
|
+
self.input = {:esc => :exit, :space => :remote_high_score, :a => :add}
|
14
|
+
self.caption = "Example of Chingus HighScore class. Press Space to go to fetch high scores remotely!"
|
15
15
|
|
16
|
-
PulsatingText.create("HIGH SCORES", :x => $window.width/2, :y => 50, :size => 70)
|
16
|
+
@title = PulsatingText.create("HIGH SCORES", :x => $window.width/2, :y => 50, :size => 70)
|
17
17
|
|
18
18
|
#
|
19
19
|
# Load a list from disk, defaults to "high_score_list.yml"
|
@@ -25,29 +25,65 @@ class Game < Chingu::Window
|
|
25
25
|
# Add some new high scores to the list. :name and :score are required but you can put whatever.
|
26
26
|
# They will mix with the old scores, automatic default sorting on :score
|
27
27
|
#
|
28
|
-
10.times
|
28
|
+
10.times do
|
29
|
+
data = {:name => "NEW", :score => rand(10000)}
|
30
|
+
|
31
|
+
position = @high_score_list.add(data)
|
32
|
+
if position
|
33
|
+
puts "#{data[:name]} - #{data[:score]} got position #{position}"
|
34
|
+
else
|
35
|
+
puts "#{data[:name]} - #{data[:score]} didn't make it"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
create_text
|
40
|
+
|
41
|
+
# @high_score_list.save_to_file # Uncomment to save list to disk
|
42
|
+
end
|
43
|
+
|
44
|
+
def remote_high_score
|
45
|
+
game_objects.destroy_all
|
46
|
+
|
47
|
+
@title = PulsatingText.create("REMOTE WEBSERVICE HIGH SCORES", :x => $window.width/2, :y => 50, :size => 30)
|
48
|
+
|
49
|
+
#
|
50
|
+
# :game_id is the unique ID the game has on www.gamercv.com
|
51
|
+
# :user is the login for that specific game (set by owner)
|
52
|
+
# :password is the password for that specific game (set by owner)
|
53
|
+
#
|
54
|
+
# To read a high score list only :game_id is required
|
55
|
+
# To write to a high score list :user and :password is required as well
|
56
|
+
#
|
57
|
+
@high_score_list = HighScoreList.load_remote(:game_id => 1, :user => "chingu", :password => "chingu", :size => 10)
|
58
|
+
create_text
|
59
|
+
end
|
60
|
+
|
61
|
+
def add
|
62
|
+
data = {:name => "NEW", :score => 1600}
|
63
|
+
position = @high_score_list.add(data)
|
64
|
+
puts "Got position: #{position.to_s}"
|
65
|
+
create_text
|
66
|
+
end
|
67
|
+
|
68
|
+
def create_text
|
69
|
+
@score_texts ||= []
|
70
|
+
@score_texts.each { |text| text.destroy }
|
29
71
|
|
30
72
|
#
|
31
73
|
# Iterate through all high scores and create the visual represenation of it
|
32
74
|
#
|
33
75
|
@high_score_list.each_with_index do |high_score, index|
|
34
76
|
y = index * 25 + 100
|
35
|
-
Text.create(high_score[:name], :x => 200, :y => y, :size => 20)
|
36
|
-
Text.create(high_score[:score], :x => 400, :y => y, :size => 20)
|
77
|
+
@score_texts << Text.create(high_score[:name], :x => 200, :y => y, :size => 20)
|
78
|
+
@score_texts << Text.create(high_score[:score], :x => 400, :y => y, :size => 20)
|
37
79
|
end
|
38
80
|
|
39
81
|
5.times do
|
40
82
|
score = rand(20000)
|
41
|
-
puts "position for #{score}: #{@high_score_list.position_by_score(score)}"
|
83
|
+
puts "position for possible score #{score}: #{@high_score_list.position_by_score(score)}"
|
42
84
|
end
|
43
|
-
|
44
|
-
# @high_score_list.save # Uncomment to save list to disk
|
45
85
|
end
|
46
86
|
|
47
|
-
def update
|
48
|
-
super
|
49
|
-
self.caption = "FPS #{$window.fps} - game objects: #{game_objects.size}"
|
50
|
-
end
|
51
87
|
end
|
52
88
|
|
53
89
|
#
|
@@ -1,21 +1,23 @@
|
|
1
1
|
---
|
2
|
-
- :name:
|
3
|
-
:score:
|
4
|
-
- :name:
|
5
|
-
:score:
|
6
|
-
- :name:
|
7
|
-
:score:
|
8
|
-
- :name:
|
9
|
-
:score:
|
10
|
-
- :name:
|
11
|
-
:score:
|
12
|
-
- :name:
|
13
|
-
:score:
|
14
|
-
- :name:
|
15
|
-
:score:
|
16
|
-
- :name:
|
17
|
-
:score:
|
18
|
-
- :name:
|
19
|
-
:score:
|
20
|
-
- :name:
|
21
|
-
:score:
|
2
|
+
- :name: NEW
|
3
|
+
:score: 9971
|
4
|
+
- :name: NEW
|
5
|
+
:score: 9960
|
6
|
+
- :name: NEW
|
7
|
+
:score: 9912
|
8
|
+
- :name: NEW
|
9
|
+
:score: 9900
|
10
|
+
- :name: NEW
|
11
|
+
:score: 9885
|
12
|
+
- :name: NEW
|
13
|
+
:score: 9840
|
14
|
+
- :name: NEW
|
15
|
+
:score: 9839
|
16
|
+
- :name: NEW
|
17
|
+
:score: 9746
|
18
|
+
- :name: NEW
|
19
|
+
:score: 9742
|
20
|
+
- :name: NEW
|
21
|
+
:score: 9736
|
22
|
+
- :name: NEW
|
23
|
+
:score: 9706
|
data/lib/chingu.rb
CHANGED
data/lib/chingu/game_object.rb
CHANGED
@@ -47,13 +47,16 @@ module Chingu
|
|
47
47
|
@y = options[:y] || 0
|
48
48
|
@angle = options[:angle] || 0
|
49
49
|
|
50
|
-
|
50
|
+
|
51
51
|
self.factor = options[:factor] || 1.0
|
52
|
-
@center_x = options[:center_x] if options[:center_x]
|
53
|
-
@center_y = options[:center_y] if options[:center_y]
|
54
52
|
@factor_x = options[:factor_x] if options[:factor_x]
|
55
53
|
@factor_y = options[:factor_y] if options[:factor_y]
|
56
|
-
|
54
|
+
|
55
|
+
self.center = options[:center] || 0.5
|
56
|
+
self.rotation_center(options[:rotation_center]) if options[:rotation_center]
|
57
|
+
@center_x = options[:center_x] if options[:center_x]
|
58
|
+
@center_y = options[:center_y] if options[:center_y]
|
59
|
+
|
57
60
|
if options[:color].is_a?(Gosu::Color)
|
58
61
|
@color = options[:color]
|
59
62
|
else
|
@@ -25,7 +25,11 @@ module Chingu
|
|
25
25
|
#
|
26
26
|
# - Keeps a local YAML file with highscores, default highscores.yml in root game dir.
|
27
27
|
# - Add, delete, clear highscores
|
28
|
-
# - Iterate through highscores with simple
|
28
|
+
# - Iterate through highscores with simple HighScores#each
|
29
|
+
#
|
30
|
+
# Working with the high score list:
|
31
|
+
# ...as a local file requires gem 'yaml'
|
32
|
+
# ...as a remote gamercv.com resource requires gem 'crack' and 'rest_client'
|
29
33
|
#
|
30
34
|
class HighScoreList
|
31
35
|
attr_reader :file
|
@@ -34,10 +38,16 @@ module Chingu
|
|
34
38
|
# Create a new high score list with 0 entries
|
35
39
|
#
|
36
40
|
def initialize(options = {})
|
37
|
-
require 'yaml'
|
38
41
|
@file = options[:file] || "high_score_list.yml"
|
39
42
|
@size = options[:size] || 100
|
40
43
|
@sort_on = options[:sort_on] || :score
|
44
|
+
|
45
|
+
@user = options[:user]
|
46
|
+
@password = options[:password]
|
47
|
+
@game_id = options[:game_id]
|
48
|
+
@server = "http://api.gamercv.com"
|
49
|
+
@resource = nil
|
50
|
+
|
41
51
|
@high_scores = Array.new
|
42
52
|
end
|
43
53
|
|
@@ -46,25 +56,63 @@ module Chingu
|
|
46
56
|
# If no :file is given, HighScoreList tries to load from file "high_score_list.yml"
|
47
57
|
#
|
48
58
|
def self.load(options = {})
|
59
|
+
require 'yaml'
|
49
60
|
high_score_list = HighScoreList.new(options)
|
50
61
|
high_score_list.load
|
51
62
|
return high_score_list
|
52
63
|
end
|
53
64
|
|
65
|
+
def self.load_remote(options = {})
|
66
|
+
high_score_list = HighScoreList.new(options)
|
67
|
+
high_score_list.load_remote
|
68
|
+
return high_score_list
|
69
|
+
end
|
70
|
+
|
54
71
|
#
|
55
|
-
#
|
72
|
+
# Adda a new high score to the local file
|
56
73
|
# 'data' is a hash of key/value-pairs that needs to contain at least the keys :name and :score
|
74
|
+
# Returns the position it got in the list, with 1 beeing the first positions
|
57
75
|
#
|
58
76
|
def add(data)
|
59
77
|
raise "No :name value in high score!" if data[:name].nil?
|
60
78
|
raise "No :score value in high score!" if data[:score].nil?
|
61
|
-
|
62
|
-
|
63
|
-
@high_scores.sort! { |a, b| b[@sort_on] <=> a[@sort_on] }
|
64
|
-
@high_scores = @high_scores[0..@size]
|
79
|
+
@resource ? add_remote(data) : add_local(data)
|
80
|
+
position_by_score(data[:score])
|
65
81
|
end
|
66
82
|
alias << add
|
67
83
|
|
84
|
+
#
|
85
|
+
# POSTs a new high score to the remote web service
|
86
|
+
#
|
87
|
+
def add_remote(data)
|
88
|
+
begin
|
89
|
+
@res = @resource.post({:high_score => data})
|
90
|
+
data = Crack::XML.parse(@res)
|
91
|
+
add_to_list(force_symbol_hash(data["high_score"]))
|
92
|
+
rescue RestClient::RequestFailed
|
93
|
+
puts "RequestFailed: couldn't add high score"
|
94
|
+
rescue RestClient::Unauthorized
|
95
|
+
puts "Unauthorized to add high score (check :user and :password arguments)"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Adds a new high score to local file
|
101
|
+
# Returns the position it got in the list, with 1 beeing the first positions
|
102
|
+
#
|
103
|
+
def add_local(data)
|
104
|
+
add_to_list(force_symbol_hash(data))
|
105
|
+
save_to_file
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# Returns the position of full data-hash data entry, used internally
|
110
|
+
#
|
111
|
+
def position_by_data(data)
|
112
|
+
position = @high_scores.rindex(data)
|
113
|
+
position += 1 if position
|
114
|
+
end
|
115
|
+
|
68
116
|
#
|
69
117
|
# Returns the position 'score' would get in among the high scores:
|
70
118
|
# @high_score_list.position_by_score(999999999) # most likely returns 1 for the number one spot
|
@@ -79,6 +127,45 @@ module Chingu
|
|
79
127
|
return nil
|
80
128
|
end
|
81
129
|
|
130
|
+
#
|
131
|
+
# Load data from previously specified @file
|
132
|
+
#
|
133
|
+
def load
|
134
|
+
@high_scores = YAML.load_file(@file) if File.exists?(@file)
|
135
|
+
@high_scores = @high_scores[0..@size]
|
136
|
+
end
|
137
|
+
|
138
|
+
#
|
139
|
+
# Load data from remove web service.
|
140
|
+
# Under the hood, this is accomplished through a simple REST-interface
|
141
|
+
# The returned XML-data is converted into a simple Hash (@high_scores), which is also returned from this method.
|
142
|
+
#
|
143
|
+
def load_remote
|
144
|
+
raise "You need to specify a Game_id to load a remote high score list" unless defined?(@game_id)
|
145
|
+
raise "You need to specify a User to load a remote high score list" unless defined?(@user)
|
146
|
+
raise "You need to specify a Password to load a remote high score list" unless defined?(@password)
|
147
|
+
|
148
|
+
require 'rest_client'
|
149
|
+
require 'crack/xml'
|
150
|
+
@resource = RestClient::Resource.new "#{@server}/games/#{@game_id}/high_scores", :user => @user, :password => @password, :timeout => 20, :open_timeout => 5
|
151
|
+
|
152
|
+
@high_scores.clear
|
153
|
+
begin
|
154
|
+
res = @resource.get
|
155
|
+
data = Crack::XML.parse(res)
|
156
|
+
if data["high_scores"]
|
157
|
+
data["high_scores"].each do |high_score|
|
158
|
+
@high_scores.push(force_symbol_hash(high_score))
|
159
|
+
end
|
160
|
+
end
|
161
|
+
rescue RestClient::ResourceNotFound
|
162
|
+
puts "Couldn't find Resource, did you specify a correct :game_id ?"
|
163
|
+
end
|
164
|
+
|
165
|
+
@high_scores = @high_scores[0..@size]
|
166
|
+
return @high_scores
|
167
|
+
end
|
168
|
+
|
82
169
|
#
|
83
170
|
# Direct access to invidual high scores
|
84
171
|
#
|
@@ -97,21 +184,31 @@ module Chingu
|
|
97
184
|
@high_scores.each_with_index { |high_score, index| yield high_score, index }
|
98
185
|
end
|
99
186
|
|
100
|
-
#
|
101
|
-
# Load data from previously specified @file
|
102
|
-
#
|
103
|
-
def load
|
104
|
-
@high_scores = YAML.load_file(@file) if File.exists?(@file)
|
105
|
-
end
|
106
|
-
|
107
187
|
#
|
108
188
|
# Save high score data into previously specified @file
|
109
189
|
#
|
110
|
-
def
|
190
|
+
def save_to_file
|
191
|
+
require 'yaml'
|
111
192
|
File.open(@file, 'w') do |out|
|
112
193
|
YAML.dump(@high_scores, out)
|
113
194
|
end
|
114
195
|
end
|
196
|
+
|
197
|
+
private
|
115
198
|
|
199
|
+
def add_to_list(data)
|
200
|
+
@high_scores.push(data)
|
201
|
+
@high_scores.sort! { |a, b| b[@sort_on] <=> a[@sort_on] }
|
202
|
+
@high_scores = @high_scores[0..@size]
|
203
|
+
end
|
204
|
+
|
205
|
+
def force_symbol_hash(hash)
|
206
|
+
symbol_hash = {}
|
207
|
+
hash.each_pair do |key, value|
|
208
|
+
symbol_hash[key.to_sym] = value
|
209
|
+
end
|
210
|
+
return symbol_hash
|
211
|
+
end
|
212
|
+
|
116
213
|
end
|
117
214
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chingu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ippa
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
hxtMlw==
|
31
31
|
-----END CERTIFICATE-----
|
32
32
|
|
33
|
-
date: 2009-10-
|
33
|
+
date: 2009-10-25 01:00:00 +02:00
|
34
34
|
default_executable:
|
35
35
|
dependencies:
|
36
36
|
- !ruby/object:Gem::Dependency
|
metadata.gz.sig
CHANGED
Binary file
|