chingu 0.7.6.4 → 0.7.6.5
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/.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
|