chingu 0.7.6.4 → 0.7.6.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -3
- data/.rspec +2 -0
- data/README.rdoc +48 -33
- data/Rakefile +42 -22
- data/chingu.gemspec +26 -4
- data/examples/example21.yml +55 -55
- data/examples/example6_transitional_game_state.rb +3 -2
- data/examples/example9_collision_detection.rb +12 -22
- data/lib/chingu.rb +1 -1
- data/lib/chingu/assets.rb +3 -1
- data/lib/chingu/basic_game_object.rb +1 -1
- data/lib/chingu/core_ext/array.rb +1 -0
- data/lib/chingu/game_object_list.rb +1 -12
- data/lib/chingu/game_state.rb +1 -1
- data/lib/chingu/game_state_manager.rb +1 -0
- data/lib/chingu/game_states/edit.rb +5 -2
- data/lib/chingu/game_states/fade_to.rb +4 -8
- data/lib/chingu/text.rb +1 -1
- data/lib/chingu/traits/bounding_circle.rb +2 -6
- data/lib/chingu/traits/collision_detection.rb +7 -7
- data/lib/chingu/traits/velocity.rb +9 -6
- data/spec/chingu/fpscounter_spec.rb +43 -0
- data/spec/chingu/game_object_spec.rb +96 -0
- data/spec/chingu/inflector_spec.rb +21 -0
- data/spec/chingu/rect_20x20.png +0 -0
- data/spec/chingu/text_spec.rb +18 -0
- data/spec/spec_helper.rb +20 -0
- data/specs.watchr +74 -0
- metadata +63 -4
@@ -49,13 +49,14 @@ class State1 < Chingu::GameState
|
|
49
49
|
# This is another way of achieving the same thing as the out-commeted draw-code
|
50
50
|
# Since .create is used, it's automatically updated and drawn
|
51
51
|
#
|
52
|
-
def
|
52
|
+
def initialize(options = {})
|
53
|
+
super
|
53
54
|
Chingu::GameObject.create(:image => "ruby.png", :rotation_center => :top_left)
|
54
55
|
end
|
55
56
|
|
56
57
|
#def draw
|
57
58
|
# Image["ruby.png"].draw(0,0,0)
|
58
|
-
#end
|
59
|
+
#end
|
59
60
|
end
|
60
61
|
|
61
62
|
class State2 < Chingu::GameState
|
@@ -19,40 +19,31 @@ class Game < Chingu::Window
|
|
19
19
|
end
|
20
20
|
|
21
21
|
class FireCube < Chingu::GameObject
|
22
|
-
traits :velocity, :collision_detection
|
23
|
-
attr_accessor :color
|
22
|
+
traits :velocity, :collision_detection, :bounding_circle
|
23
|
+
attr_accessor :color
|
24
24
|
|
25
25
|
def initialize(options)
|
26
26
|
super
|
27
27
|
@mode = :additive
|
28
28
|
|
29
|
+
@image = Image["circle.png"]
|
30
|
+
|
29
31
|
# initialize with a rightwards velocity with some damping to look more realistic
|
30
|
-
|
31
|
-
|
32
|
+
self.velocity_x = options[:velocity_x] || 1 + rand(2)
|
33
|
+
self.velocity_y = options[:velocity_y] || 1 + rand(2)
|
34
|
+
self.factor = 2
|
32
35
|
|
33
|
-
@
|
34
|
-
@radius = 6
|
36
|
+
@color = Color::BLUE
|
35
37
|
|
36
|
-
|
37
|
-
@red = Color.new(255,255,10,10)
|
38
|
-
@color = @blue
|
39
|
-
end
|
40
|
-
|
41
|
-
def bounding_box
|
42
|
-
@box
|
43
|
-
end
|
44
|
-
|
45
|
-
def draw
|
46
|
-
$window.fill_rect(@box, @color)
|
38
|
+
cache_bounding_circle # This does a lot for performance
|
47
39
|
end
|
48
40
|
|
49
41
|
def update
|
50
|
-
@
|
51
|
-
@color = @blue
|
42
|
+
@color = Color::BLUE
|
52
43
|
end
|
53
44
|
|
54
45
|
def die!
|
55
|
-
@color =
|
46
|
+
@color = Color::RED
|
56
47
|
end
|
57
48
|
|
58
49
|
end
|
@@ -95,8 +86,7 @@ class ParticleState < Chingu::GameState
|
|
95
86
|
cube1.die!
|
96
87
|
cube2.die!
|
97
88
|
end
|
98
|
-
|
99
|
-
game_objects.destroy_if { |object| object.color.alpha == 0 }
|
89
|
+
|
100
90
|
end
|
101
91
|
|
102
92
|
def draw
|
data/lib/chingu.rb
CHANGED
data/lib/chingu/assets.rb
CHANGED
@@ -33,7 +33,9 @@ module Gosu
|
|
33
33
|
include Chingu::NamedResource
|
34
34
|
|
35
35
|
def self.autoload(name)
|
36
|
-
(path = find_file(name)) ? Gosu::Image.new($window, path, true) : nil
|
36
|
+
ret = (path = find_file(name)) ? Gosu::Image.new($window, path, true) : nil
|
37
|
+
raise "Can't load image \"#{name}\"" if ret.nil?
|
38
|
+
return ret
|
37
39
|
end
|
38
40
|
end
|
39
41
|
|
@@ -31,7 +31,6 @@ module Chingu
|
|
31
31
|
@game_objects = options[:game_objects] || []
|
32
32
|
@add_game_objects = []
|
33
33
|
@remove_game_objects = []
|
34
|
-
#@game_objects_by_class = Hash.new
|
35
34
|
end
|
36
35
|
|
37
36
|
def to_s
|
@@ -40,34 +39,24 @@ module Chingu
|
|
40
39
|
|
41
40
|
def of_class(klass)
|
42
41
|
@game_objects.select { |game_object| game_object.is_a? klass }
|
43
|
-
#@game_objects_by_class[klass] || []
|
44
42
|
end
|
45
43
|
|
46
44
|
def destroy_all
|
47
45
|
@game_objects.clear
|
48
|
-
#@game_objects_of_class.clear
|
49
46
|
end
|
50
47
|
alias :clear :destroy_all
|
51
48
|
alias :remove_all :destroy_all
|
52
49
|
|
53
50
|
def add_game_object(object)
|
54
|
-
#@game_objects.push(object)
|
55
51
|
@add_game_objects.push(object)
|
56
|
-
|
57
|
-
|
58
|
-
#(@game_objects_by_class[object.class] ||= []).push(object)
|
59
52
|
end
|
60
53
|
|
61
54
|
def remove_game_object(object)
|
62
|
-
#@game_objects.delete(object)
|
63
55
|
@remove_game_objects.push(object)
|
64
|
-
|
65
|
-
#@game_objects_by_class[object.class].delete(object)
|
66
56
|
end
|
67
57
|
|
68
58
|
def destroy_if
|
69
59
|
@game_objects.reject! { |object| yield(object) }
|
70
|
-
#@game_objects_by_class.delete_if { |klass, object| yield(object) }
|
71
60
|
end
|
72
61
|
|
73
62
|
def size
|
@@ -141,4 +130,4 @@ module Chingu
|
|
141
130
|
@game_objects.each { |object| object.show! }
|
142
131
|
end
|
143
132
|
end
|
144
|
-
end
|
133
|
+
end
|
data/lib/chingu/game_state.rb
CHANGED
@@ -78,7 +78,7 @@ module Chingu
|
|
78
78
|
# Include the module, which will add the containing methods as instance methods
|
79
79
|
include mod
|
80
80
|
|
81
|
-
# Does sub-module "
|
81
|
+
# Does sub-module "ClassMethods" exists?
|
82
82
|
# (eg: Chingu::Traits::Timer::ClassMethods)
|
83
83
|
if mod.const_defined?("ClassMethods")
|
84
84
|
# Add methods in scope ClassMethods as.. class methods!
|
@@ -100,6 +100,7 @@ module Chingu
|
|
100
100
|
# .. and finalize() is called on the game state we're switching _from_.
|
101
101
|
#
|
102
102
|
def switch_game_state(state, options = {})
|
103
|
+
puts state.class
|
103
104
|
options = {:setup => true, :finalize => true, :transitional => true}.merge(options)
|
104
105
|
|
105
106
|
@previous_game_state = current_game_state
|
@@ -136,10 +136,13 @@ module Chingu
|
|
136
136
|
if game_object.image
|
137
137
|
game_object.size = [32,32]
|
138
138
|
game_object.cache_bounding_box if game_object.respond_to?(:cache_bounding_box)
|
139
|
+
x += 40
|
140
|
+
else
|
141
|
+
puts "Skipping #{klass} - no image" if @debug
|
142
|
+
game_object.destroy
|
139
143
|
end
|
140
|
-
x += 40
|
141
144
|
rescue
|
142
|
-
puts "Couldn't use #{klass} in editor
|
145
|
+
puts "Couldn't use #{klass} in editor: #{$!}"
|
143
146
|
end
|
144
147
|
end
|
145
148
|
end
|
@@ -39,15 +39,13 @@ module Chingu
|
|
39
39
|
@options = {:speed => 3}.merge(options)
|
40
40
|
|
41
41
|
@new_game_state = new_game_state
|
42
|
-
@new_game_state = new_game_state.new if new_game_state.is_a? Class
|
43
|
-
|
44
|
-
#@manager = options[:game_state_manager] || self
|
45
|
-
#@manager = game_state_manager
|
42
|
+
@new_game_state = new_game_state.new if new_game_state.is_a? Class
|
43
|
+
@new_game_state.game_objects.sync
|
46
44
|
end
|
47
45
|
|
48
46
|
def setup
|
49
47
|
@color = Gosu::Color.new(0,0,0,0)
|
50
|
-
|
48
|
+
|
51
49
|
if previous_game_state
|
52
50
|
@fading_in = false
|
53
51
|
@alpha = 0.0
|
@@ -55,7 +53,7 @@ module Chingu
|
|
55
53
|
@fading_in = true
|
56
54
|
@alpha = 255.0
|
57
55
|
end
|
58
|
-
|
56
|
+
|
59
57
|
update # Since draw is called before update
|
60
58
|
end
|
61
59
|
|
@@ -77,7 +75,6 @@ module Chingu
|
|
77
75
|
if @fading_in
|
78
76
|
@new_game_state.draw
|
79
77
|
else
|
80
|
-
## @manager.previous_game_state.draw
|
81
78
|
previous_game_state.draw
|
82
79
|
end
|
83
80
|
|
@@ -88,7 +85,6 @@ module Chingu
|
|
88
85
|
end
|
89
86
|
|
90
87
|
if @fading_in && @alpha == 0
|
91
|
-
##@manager.switch_game_state(@new_game_state, :transitional => false)
|
92
88
|
switch_game_state(@new_game_state, :transitional => false)
|
93
89
|
end
|
94
90
|
|
data/lib/chingu/text.rb
CHANGED
@@ -51,7 +51,7 @@ module Chingu
|
|
51
51
|
#
|
52
52
|
# Takes the standard GameObject-hash-arguments but also:
|
53
53
|
# :text - a string of text
|
54
|
-
# :font_name|:font - Name of a system font, or a filename to a TTF file (must contain
|
54
|
+
# :font_name|:font - Name of a system font, or a filename to a TTF file (must contain ? does not work on Linux).
|
55
55
|
# :height|:size - Height of the font in pixels.
|
56
56
|
# :line_spacing - Spacing between two lines of text in pixels.
|
57
57
|
# :max_width - Width of the bitmap that will be returned. Text will be split into multiple lines to avoid drawing over the right border. When a single word is too long, it will be truncated.
|
@@ -47,11 +47,8 @@ module Chingu
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def radius
|
50
|
-
return @cached_radius if @cached_radius
|
51
|
-
|
52
|
-
width = self.image.width * self.factor_x.abs
|
53
|
-
height = self.image.height * self.factor_y.abs
|
54
|
-
radius = (width + height) / 4
|
50
|
+
return @cached_radius if @cached_radius
|
51
|
+
radius = (self.width + self.height) / 4
|
55
52
|
radius = radius * trait_options[:bounding_circle][:scale] if trait_options[:bounding_circle][:scale]
|
56
53
|
return radius
|
57
54
|
end
|
@@ -61,7 +58,6 @@ module Chingu
|
|
61
58
|
end
|
62
59
|
|
63
60
|
def cache_bounding_circle
|
64
|
-
@cached_radius = nil
|
65
61
|
@cached_radius = self.radius
|
66
62
|
end
|
67
63
|
|
@@ -111,7 +111,7 @@ module Chingu
|
|
111
111
|
#
|
112
112
|
def each_collision(*klasses)
|
113
113
|
Array(klasses).each do |klass|
|
114
|
-
(klass.respond_to?(:all) ? klass.all : klass).each do |object|
|
114
|
+
(klass.respond_to?(:all) ? klass.all : Array(klass)).each do |object|
|
115
115
|
yield(self, object) if collides?(object)
|
116
116
|
end
|
117
117
|
end
|
@@ -119,7 +119,7 @@ module Chingu
|
|
119
119
|
|
120
120
|
def first_collision(*klasses)
|
121
121
|
Array(klasses).each do |klass|
|
122
|
-
(klass.respond_to?(:all) ? klass.all : klass).each do |object|
|
122
|
+
(klass.respond_to?(:all) ? klass.all : Array(klass)).each do |object|
|
123
123
|
return object if collides?(object)
|
124
124
|
end
|
125
125
|
end
|
@@ -132,7 +132,7 @@ module Chingu
|
|
132
132
|
#
|
133
133
|
def each_bounding_circle_collision(*klasses)
|
134
134
|
Array(klasses).each do |klass|
|
135
|
-
(klass.respond_to?(:all) ? klass.all : klass).each do |object|
|
135
|
+
(klass.respond_to?(:all) ? klass.all : Array(klass)).each do |object|
|
136
136
|
next unless self.collidable && object.collidable
|
137
137
|
yield(self, object) if Gosu.distance(self.x, self.y, object.x, object.y) < self.radius + object.radius
|
138
138
|
end
|
@@ -145,7 +145,7 @@ module Chingu
|
|
145
145
|
#
|
146
146
|
def each_bounding_box_collision(*klasses)
|
147
147
|
Array(klasses).each do |klass|
|
148
|
-
(klass.respond_to?(:all) ? klass.all : klass).each do |object|
|
148
|
+
(klass.respond_to?(:all) ? klass.all : Array(klass)).each do |object|
|
149
149
|
return false unless self.collidable && object.collidable
|
150
150
|
yield(self, object) if self.bounding_box.collide_rect?(object.bounding_box)
|
151
151
|
end
|
@@ -159,7 +159,7 @@ module Chingu
|
|
159
159
|
#
|
160
160
|
def each_bounding_circle_collision(*klasses)
|
161
161
|
Array(klasses).each do |klass|
|
162
|
-
object2_list = (klass.respond_to?(:all) ? klass.all : klass)
|
162
|
+
object2_list = (klass.respond_to?(:all) ? klass.all : Array(klass))
|
163
163
|
#total_radius = object1.radius + object2.radius # possible optimization?
|
164
164
|
|
165
165
|
self.all.each do |object1|
|
@@ -177,7 +177,7 @@ module Chingu
|
|
177
177
|
#
|
178
178
|
def each_bounding_box_collision(*klasses)
|
179
179
|
Array(klasses).each do |klass|
|
180
|
-
object2_list = (klass.respond_to?(:all) ? klass.all : klass)
|
180
|
+
object2_list = (klass.respond_to?(:all) ? klass.all : Array(klass))
|
181
181
|
self.all.each do |object1|
|
182
182
|
object2_list.each do |object2|
|
183
183
|
next if object1 == object2 # Don't collide objects with themselves
|
@@ -233,7 +233,7 @@ module Chingu
|
|
233
233
|
# end
|
234
234
|
# end
|
235
235
|
#end
|
236
|
-
object2_list = (klass.respond_to?(:all) ? klass.all : klass)
|
236
|
+
object2_list = (klass.respond_to?(:all) ? klass.all : Array(klass))
|
237
237
|
self.all.each do |object1|
|
238
238
|
object2_list.each do |object2|
|
239
239
|
next if object1 == object2 # Don't collide objects with themselves
|
@@ -58,8 +58,9 @@ module Chingu
|
|
58
58
|
#
|
59
59
|
# Sets X and Y velocity with one single call. Takes an Array-argument with 2 values.
|
60
60
|
#
|
61
|
-
def velocity=(
|
62
|
-
@velocity_x, @velocity_y =
|
61
|
+
def velocity=(value)
|
62
|
+
@velocity_x, @velocity_y = value && return if value.is_a? Array
|
63
|
+
@velocity_x, @velocity_y = value, value
|
63
64
|
end
|
64
65
|
|
65
66
|
def velocity; [@velocity_x, @velocity_y]; end
|
@@ -67,8 +68,9 @@ module Chingu
|
|
67
68
|
#
|
68
69
|
# Sets X and Y acceleration with one single call. Takes an Array-argument with 2 values.
|
69
70
|
#
|
70
|
-
def acceleration=(
|
71
|
-
@acceleration_x, @acceleration_y =
|
71
|
+
def acceleration=(value)
|
72
|
+
@acceleration_x, @acceleration_y = value && return if value.is_a? Array
|
73
|
+
@acceleration_x, @acceleration_y = value, value
|
72
74
|
end
|
73
75
|
|
74
76
|
def acceleration; [@acceleration_x, @acceleration_y]; end
|
@@ -76,8 +78,9 @@ module Chingu
|
|
76
78
|
#
|
77
79
|
# Sets X and Y acceleration with one single call. Takes an Array-argument with 2 values.
|
78
80
|
#
|
79
|
-
def max_velocity=(
|
80
|
-
@max_velocity_x, @max_velocity_y =
|
81
|
+
def max_velocity=(value)
|
82
|
+
@max_velocity_x, @max_velocity_y = value && return if value.is_a? Array
|
83
|
+
@max_velocity_x, @max_velocity_y = value, value
|
81
84
|
end
|
82
85
|
|
83
86
|
def max_velocity; [@max_velocity_x, @max_velocity_y]; end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Chingu
|
4
|
+
|
5
|
+
describe FPSCounter do
|
6
|
+
|
7
|
+
it { should respond_to(:fps) }
|
8
|
+
it { should respond_to(:milliseconds_since_last_tick) }
|
9
|
+
it { should respond_to(:ticks) }
|
10
|
+
|
11
|
+
describe "#register_tick" do
|
12
|
+
before do
|
13
|
+
Gosu.stub(:milliseconds).and_return(1000)
|
14
|
+
subject { FPSCounter.new }
|
15
|
+
end
|
16
|
+
|
17
|
+
it "increases the tick counter" do
|
18
|
+
expect {
|
19
|
+
subject.register_tick
|
20
|
+
}.to change(subject, :ticks).from(0).to(1)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "keeps track of the fps" do
|
24
|
+
expect {
|
25
|
+
subject.register_tick
|
26
|
+
Gosu.stub(:milliseconds).and_return(1500)
|
27
|
+
subject.register_tick
|
28
|
+
Gosu.stub(:milliseconds).and_return(2000)
|
29
|
+
subject.register_tick
|
30
|
+
}.to change(subject, :fps).from(0).to(3) # #register_tick has been called 3 times within 1 second = 3 FPS
|
31
|
+
end
|
32
|
+
|
33
|
+
it "calculates how many milliseconds passed since last game loop iteration and returns that value" do
|
34
|
+
Gosu.stub(:milliseconds).and_return(2000)
|
35
|
+
subject.register_tick.should equal 1000
|
36
|
+
subject.milliseconds_since_last_tick.should eql(1000)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Chingu
|
4
|
+
|
5
|
+
describe GameObject do
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
@game = Chingu::Window.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it { should respond_to(:x) }
|
12
|
+
it { should respond_to(:y) }
|
13
|
+
it { should respond_to(:angle) }
|
14
|
+
it { should respond_to(:center_x) }
|
15
|
+
it { should respond_to(:center_y) }
|
16
|
+
it { should respond_to(:factor_x) }
|
17
|
+
it { should respond_to(:factor_y) }
|
18
|
+
it { should respond_to(:zorder) }
|
19
|
+
it { should respond_to(:mode) }
|
20
|
+
it { should respond_to(:color) }
|
21
|
+
|
22
|
+
describe "a newly created GameObject" do
|
23
|
+
before do
|
24
|
+
subject { GameObject.new }
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should have default values" do
|
28
|
+
subject.angle.should == 0
|
29
|
+
subject.x.should == 0
|
30
|
+
subject.y.should == 0
|
31
|
+
subject.factor_x.should == 1
|
32
|
+
subject.factor_y.should == 1
|
33
|
+
subject.center_x.should == 0.5
|
34
|
+
subject.center_y.should == 0.5
|
35
|
+
subject.mode.should == :default
|
36
|
+
subject.image.should == nil
|
37
|
+
subject.color.to_s.should == Gosu::Color::WHITE.to_s
|
38
|
+
subject.alpha.should == 255
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should wrap angle at 360" do
|
42
|
+
subject.angle.should == 0
|
43
|
+
subject.angle += 30
|
44
|
+
subject.angle.should == 30
|
45
|
+
subject.angle += 360
|
46
|
+
subject.angle.should == 30
|
47
|
+
end
|
48
|
+
|
49
|
+
it "shouldn't allow alpha below 0" do
|
50
|
+
subject.alpha = -10
|
51
|
+
subject.alpha.should == 0
|
52
|
+
end
|
53
|
+
|
54
|
+
it "shouldn't allow alpha above 255" do
|
55
|
+
subject.alpha = 1000
|
56
|
+
subject.alpha.should == 255
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "GameObject with an image" do
|
62
|
+
before do
|
63
|
+
p Image.autoload_dirs
|
64
|
+
subject { GameObject.new(:image => "rect_20x20.png") }
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should have width,height & size" do
|
68
|
+
subject.height.should == 20
|
69
|
+
subject.width.should == 20
|
70
|
+
subject.size.should == [20,20]
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should adapt width,height & size to scaling" do
|
74
|
+
subject.factor = 2
|
75
|
+
subject.height.should == 40
|
76
|
+
subject.width.should == 40
|
77
|
+
subject.size.should == [40,40]
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should adapt factor_x/factor_y to new size" do
|
81
|
+
subject.size = [10,40] # half the width, double the height
|
82
|
+
subject.height.should == 10
|
83
|
+
subject.width.should == 40
|
84
|
+
subject.factor_x.should == 0.5
|
85
|
+
subject.factor_y.should == 2
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
after(:all) do
|
91
|
+
$window.close
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|