chingu 0.5.8.2 → 0.5.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data.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
|