gamebox 0.1.1 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +9 -0
- data/History.txt +5 -0
- data/README.txt +4 -3
- data/Rakefile +12 -4
- data/TODO.txt +1 -5
- data/VERSION +1 -1
- data/docs/getting_started.rdoc +2 -2
- data/gamebox.gemspec +26 -10
- data/lib/gamebox/actor.rb +10 -3
- data/lib/gamebox/actor_factory.rb +2 -2
- data/lib/gamebox/actor_view.rb +14 -11
- data/lib/gamebox/actors/collidable_debugger.rb +35 -0
- data/lib/gamebox/actors/curtain.rb +2 -2
- data/lib/gamebox/actors/logo.rb +0 -6
- data/lib/gamebox/actors/score.rb +2 -5
- data/lib/gamebox/actors/spatial_debugger.rb +47 -0
- data/lib/gamebox/actors/svg_actor.rb +4 -6
- data/lib/gamebox/arbiter.rb +108 -15
- data/lib/gamebox/behavior.rb +29 -1
- data/lib/gamebox/behaviors/animated.rb +14 -23
- data/lib/gamebox/behaviors/audible.rb +1 -12
- data/lib/gamebox/behaviors/collidable.rb +29 -22
- data/lib/gamebox/behaviors/collidable/aabb_collidable.rb +61 -0
- data/lib/gamebox/behaviors/collidable/circle_collidable.rb +17 -0
- data/lib/gamebox/behaviors/collidable/collidable_shape.rb +35 -0
- data/lib/gamebox/behaviors/collidable/polygon_collidable.rb +85 -0
- data/lib/gamebox/behaviors/graphical.rb +13 -10
- data/lib/gamebox/behaviors/layered.rb +6 -20
- data/lib/gamebox/behaviors/physical.rb +116 -93
- data/lib/gamebox/class_finder.rb +6 -2
- data/lib/gamebox/config_manager.rb +24 -4
- data/lib/gamebox/data/config/objects.yml +5 -3
- data/lib/gamebox/ftor.rb +372 -0
- data/lib/gamebox/gamebox_application.rb +2 -8
- data/lib/gamebox/hooked_gosu_window.rb +30 -0
- data/lib/gamebox/input_manager.rb +78 -79
- data/lib/gamebox/lib/code_statistics.rb +1 -1
- data/lib/gamebox/lib/numbers_ext.rb +1 -1
- data/lib/gamebox/lib/rect.rb +612 -0
- data/lib/gamebox/physical_stage.rb +12 -2
- data/lib/gamebox/physics.rb +14 -3
- data/lib/gamebox/resource_manager.rb +28 -65
- data/lib/gamebox/sound_manager.rb +7 -13
- data/lib/gamebox/spatial_hash.rb +60 -14
- data/lib/gamebox/spatial_stagehand.rb +19 -0
- data/lib/gamebox/stage.rb +16 -1
- data/lib/gamebox/stage_manager.rb +1 -1
- data/lib/gamebox/svg_document.rb +1 -0
- data/lib/gamebox/tasks/gamebox_tasks.rb +23 -11
- data/lib/gamebox/templates/template_app/.gitignore +1 -0
- data/lib/gamebox/templates/template_app/Gemfile +1 -1
- data/lib/gamebox/templates/template_app/Rakefile +6 -21
- data/lib/gamebox/templates/template_app/config/environment.rb +14 -0
- data/lib/gamebox/templates/template_app/config/game.yml +2 -1
- data/lib/gamebox/templates/template_app/script/generate +0 -3
- data/lib/gamebox/templates/template_app/src/demo_stage.rb +1 -11
- data/lib/gamebox/templates/template_app/src/game.rb +0 -1
- data/lib/gamebox/templates/template_app/src/my_actor.rb +2 -2
- data/lib/gamebox/version.rb +1 -1
- data/lib/gamebox/views/graphical_actor_view.rb +15 -9
- data/lib/gamebox/wrapped_screen.rb +114 -7
- data/load_paths.rb +20 -0
- data/script/perf_spatial_hash.rb +73 -0
- data/spec/actor_view_spec.rb +1 -1
- data/spec/arbiter_spec.rb +264 -0
- data/spec/behavior_spec.rb +19 -2
- data/spec/collidable_spec.rb +105 -5
- data/spec/helper.rb +1 -1
- data/spec/label_spec.rb +1 -1
- data/spec/resource_manager_spec.rb +1 -1
- data/spec/spatial_hash_spec.rb +1 -1
- metadata +52 -10
- data/lib/gamebox/lib/surface_ext.rb +0 -76
@@ -1,9 +1,5 @@
|
|
1
|
-
require 'actor'
|
2
|
-
require 'actor_view'
|
3
|
-
|
4
1
|
require "enumerator"
|
5
2
|
|
6
|
-
|
7
3
|
# SvgActor knows how to build himself based on an svg document based on the :name
|
8
4
|
# passed in being the group name in the doc (layer).
|
9
5
|
class SvgActor < Actor
|
@@ -38,11 +34,13 @@ class SvgActor < Actor
|
|
38
34
|
end
|
39
35
|
|
40
36
|
class SvgActorView < ActorView
|
41
|
-
def draw(target,x_off,y_off)
|
37
|
+
def draw(target, x_off, y_off, z)
|
42
38
|
@actor.segments.each do |seg|
|
43
39
|
p1 = seg[0]
|
44
40
|
p2 = seg[1]
|
45
|
-
|
41
|
+
# TODO pull in draw_line_s?
|
42
|
+
target.draw_line p1.x+x_off, p1.y+y_off, p2.x+x_off, p2.y+y_off, [25,255,25,255], z
|
43
|
+
#target.draw_line_s [p1.x+x_off,p1.y+y_off], [p2.x+x_off,p2.y+y_off], [25,255,25,255], 6
|
46
44
|
end
|
47
45
|
end
|
48
46
|
end
|
data/lib/gamebox/arbiter.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# this module gets mixed into a stage to allow it to handle collision detection
|
2
2
|
module Arbiter
|
3
|
+
attr_reader :checks, :collisions
|
3
4
|
|
4
5
|
def register_collidable(actor)
|
5
6
|
@spatial_hash = stagehand(:spatial)
|
@@ -27,42 +28,62 @@ module Arbiter
|
|
27
28
|
|
28
29
|
first_objs.each do |fobj|
|
29
30
|
second_objs.each do |sobj|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
if fobj.to_i < sobj.to_i
|
32
|
+
@collision_handlers[fobj] ||= {}
|
33
|
+
@collision_handlers[fobj][sobj] = [false,block]
|
34
|
+
else
|
35
|
+
@collision_handlers[sobj] ||= {}
|
36
|
+
@collision_handlers[sobj][fobj] = [true,block]
|
37
|
+
end
|
35
38
|
end
|
36
39
|
end
|
37
40
|
end
|
38
41
|
|
39
42
|
def run_callbacks(collisions)
|
43
|
+
@collision_handlers ||= {}
|
40
44
|
collisions.each do |collision|
|
41
45
|
first = collision.first
|
42
46
|
second = collision.last
|
47
|
+
unless first.actor_type.to_i < second.actor_type.to_i
|
48
|
+
tmp = first
|
49
|
+
first = second
|
50
|
+
second = tmp
|
51
|
+
end
|
43
52
|
|
44
53
|
colliders = @collision_handlers[first.actor_type]
|
45
|
-
callback = colliders[second.actor_type] unless colliders.nil?
|
46
|
-
|
54
|
+
swapped, callback = colliders[second.actor_type] unless colliders.nil?
|
55
|
+
unless callback.nil?
|
56
|
+
if swapped
|
57
|
+
callback.call second, first
|
58
|
+
else
|
59
|
+
callback.call first, second
|
60
|
+
end
|
61
|
+
end
|
47
62
|
end
|
48
63
|
end
|
49
64
|
|
50
65
|
def find_collisions
|
51
66
|
@collidable_actors ||= []
|
67
|
+
@checks = 0
|
68
|
+
@collisions = 0
|
52
69
|
tmp_collidable_actors = @collidable_actors.dup
|
53
70
|
collisions = {}
|
54
71
|
|
55
72
|
@collidable_actors.each do |first|
|
56
73
|
x = first.x - @spatial_hash.cell_size
|
57
74
|
y = first.y - @spatial_hash.cell_size
|
75
|
+
# TODO base this on size of object
|
58
76
|
w = @spatial_hash.cell_size * 3
|
59
77
|
h = w
|
60
|
-
|
78
|
+
|
79
|
+
tmp_collidable_actors = @spatial_hash.neighbors_of(first)
|
61
80
|
|
62
81
|
tmp_collidable_actors.each do |second|
|
82
|
+
@checks += 1
|
63
83
|
if first != second && collide?(first, second)
|
64
84
|
collisions[second] ||= []
|
65
85
|
if !collisions[second].include?(first)
|
86
|
+
@collisions += 1
|
66
87
|
collisions[first] ||= []
|
67
88
|
collisions[first] << second
|
68
89
|
end
|
@@ -79,21 +100,93 @@ module Arbiter
|
|
79
100
|
end
|
80
101
|
|
81
102
|
def collide?(object, other)
|
82
|
-
|
103
|
+
# TODO perf analysis of this
|
104
|
+
self.send "collide_#{object.collidable_shape}_#{other.collidable_shape}?", object, other
|
83
105
|
end
|
84
106
|
|
85
107
|
def collide_circle_circle?(object, other)
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
y_prime = other.y + other.radius
|
108
|
+
x = object.center_x
|
109
|
+
y = object.center_y
|
110
|
+
x_prime = other.center_x
|
111
|
+
y_prime = other.center_y
|
91
112
|
|
92
113
|
x_dist = (x_prime - x) * (x_prime - x)
|
93
114
|
y_dist = (y_prime - y) * (y_prime - y)
|
94
115
|
|
95
116
|
total_radius = object.radius + other.radius
|
96
|
-
x_dist + y_dist
|
117
|
+
x_dist + y_dist <= (total_radius * total_radius)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Idea from:
|
121
|
+
# http://gpwiki.org/index.php/Polygon_Collision
|
122
|
+
# and http://www.gamedev.net/community/forums/topic.asp?topic_id=540755&whichpage=1�
|
123
|
+
def collide_polygon_polygon?(object, other)
|
124
|
+
if collide_circle_circle? object, other
|
125
|
+
# collect vector's perp
|
126
|
+
potential_sep_axis =
|
127
|
+
(object.cw_world_edge_normals | other.cw_world_edge_normals).uniq
|
128
|
+
potential_sep_axis.each do |axis|
|
129
|
+
return false unless project_and_detect(axis, object, other)
|
130
|
+
end
|
131
|
+
else
|
132
|
+
return false
|
133
|
+
end
|
134
|
+
true
|
135
|
+
end
|
136
|
+
alias collide_aabb_aabb? collide_polygon_polygon?
|
137
|
+
|
138
|
+
# returns true if the projections overlap
|
139
|
+
def project_and_detect(axis, a, b)
|
140
|
+
a_min, a_max = send("#{a.collidable_shape}_interval", axis, a)
|
141
|
+
b_min, b_max = send("#{b.collidable_shape}_interval", axis, b)
|
142
|
+
|
143
|
+
a_min <= b_max && b_min <= a_max
|
97
144
|
end
|
98
145
|
|
146
|
+
def polygon_interval(axis, object)
|
147
|
+
min = max = nil
|
148
|
+
object.cw_world_points.each do |edge|
|
149
|
+
# vector dot product
|
150
|
+
d = edge[0] * axis[0] + edge[1] * axis[1]
|
151
|
+
min ||= d
|
152
|
+
max ||= d
|
153
|
+
min = d if d < min
|
154
|
+
max = d if d > max
|
155
|
+
end
|
156
|
+
[min,max]
|
157
|
+
end
|
158
|
+
alias aabb_interval polygon_interval
|
159
|
+
|
160
|
+
def circle_interval(axis, object)
|
161
|
+
axis_x = axis[0]
|
162
|
+
axis_y = axis[1]
|
163
|
+
|
164
|
+
obj_x = object.center_x
|
165
|
+
obj_y = object.center_y
|
166
|
+
|
167
|
+
length = Math.sqrt(axis_x * axis_x + axis_y * axis_y)
|
168
|
+
cn = axis_x*obj_x + axis_y*obj_y
|
169
|
+
rlength = object.radius*length
|
170
|
+
min = cn - rlength
|
171
|
+
max = cn + rlength
|
172
|
+
[min,max]
|
173
|
+
end
|
174
|
+
|
175
|
+
def collide_polygon_circle?(object, other)
|
176
|
+
collide_circle_polygon?(other, object)
|
177
|
+
end
|
178
|
+
alias collide_aabb_circle? collide_polygon_circle?
|
179
|
+
|
180
|
+
def collide_circle_polygon?(object, other)
|
181
|
+
if collide_circle_circle? object, other
|
182
|
+
potential_sep_axis = other.cw_world_edge_normals
|
183
|
+
potential_sep_axis.each do |axis|
|
184
|
+
return false unless project_and_detect(axis, object, other)
|
185
|
+
end
|
186
|
+
true
|
187
|
+
else
|
188
|
+
false
|
189
|
+
end
|
190
|
+
end
|
191
|
+
alias collide_circle_aabb? collide_circle_polygon?
|
99
192
|
end
|
data/lib/gamebox/behavior.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
# Behavior is any type of behavior an actor can exibit.
|
2
2
|
class Behavior
|
3
|
-
attr_accessor :actor, :opts
|
3
|
+
attr_accessor :actor, :opts, :relegated_methods
|
4
4
|
|
5
5
|
def initialize(actor,opts={})
|
6
6
|
@actor = actor
|
7
7
|
@opts = opts
|
8
|
+
@relegated_methods = []
|
9
|
+
|
8
10
|
req_behs = self.class.required_behaviors
|
9
11
|
req_behs.each do |beh|
|
10
12
|
unless @actor.is? beh
|
@@ -18,6 +20,15 @@ class Behavior
|
|
18
20
|
end
|
19
21
|
|
20
22
|
def removed
|
23
|
+
target = self
|
24
|
+
|
25
|
+
@actor.instance_eval do
|
26
|
+
(class << self; self; end).class_eval do
|
27
|
+
target.relegated_methods.each do |meth|
|
28
|
+
remove_method meth
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
21
32
|
end
|
22
33
|
|
23
34
|
def update(time)
|
@@ -39,4 +50,21 @@ class Behavior
|
|
39
50
|
requires_behaviors(*args)
|
40
51
|
end
|
41
52
|
|
53
|
+
def relegates(*methods)
|
54
|
+
target = self
|
55
|
+
|
56
|
+
@actor.instance_eval do
|
57
|
+
(class << self; self; end).class_eval do
|
58
|
+
methods.each do |meth|
|
59
|
+
log("redefining #{meth} for #{@actor.class}") if @actor.respond_to? meth
|
60
|
+
target.relegated_methods << meth
|
61
|
+
|
62
|
+
define_method meth do |*args|
|
63
|
+
target.send meth, *args
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
42
70
|
end
|
@@ -18,31 +18,22 @@ class Animated < Behavior
|
|
18
18
|
@frame_num = 0
|
19
19
|
self.action = :idle
|
20
20
|
|
21
|
-
|
21
|
+
relegates :image, :width, :height,
|
22
|
+
:start_animating, :stop_animating, :animated,
|
23
|
+
:action, :action=
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
animated_obj.stop_animating
|
33
|
-
end
|
34
|
-
define_method :action do
|
35
|
-
animated_obj.action
|
36
|
-
end
|
37
|
-
define_method :action= do |action_sym|
|
38
|
-
animated_obj.action = action_sym
|
39
|
-
end
|
40
|
-
define_method :animated do
|
41
|
-
animated_obj
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def animated
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def width
|
32
|
+
image.width
|
33
|
+
end
|
45
34
|
|
35
|
+
def height
|
36
|
+
image.height
|
46
37
|
end
|
47
38
|
|
48
39
|
def update(time)
|
@@ -6,18 +6,7 @@ class Audible < Behavior
|
|
6
6
|
@sound_manager = @actor.stage.sound_manager
|
7
7
|
|
8
8
|
audible_obj = self
|
9
|
-
|
10
|
-
(class << self; self; end).class_eval do
|
11
|
-
define_method :play_sound do |*args|
|
12
|
-
audible_obj.play_sound *args
|
13
|
-
end
|
14
|
-
define_method :stop_sound do |*args|
|
15
|
-
audible_obj.stop_sound *args
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
|
9
|
+
relegates :play_sound, :stop_sound
|
21
10
|
end
|
22
11
|
|
23
12
|
# Plays a sound via the SoundManager. See SoundManager for
|
@@ -1,26 +1,19 @@
|
|
1
1
|
require 'behavior'
|
2
|
+
require 'collidable/circle_collidable'
|
3
|
+
require 'collidable/aabb_collidable'
|
4
|
+
require 'collidable/polygon_collidable'
|
2
5
|
|
6
|
+
# available collidable_shapes are :circle, :polygon, :aabb
|
3
7
|
class Collidable < Behavior
|
4
8
|
|
5
|
-
attr_accessor :
|
9
|
+
attr_accessor :collidable_shape, :cw_local_points, :shape
|
6
10
|
|
7
11
|
def setup
|
8
|
-
@shape =
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
@actor.instance_eval do
|
15
|
-
(class << self; self; end).class_eval do
|
16
|
-
define_method :shape do |*args|
|
17
|
-
collidable_obj.shape *args
|
18
|
-
end
|
19
|
-
define_method :radius do |*args|
|
20
|
-
collidable_obj.radius *args
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
12
|
+
@shape = build_shape
|
13
|
+
|
14
|
+
relegates :collidable_shape, :radius, :cw_world_points, :cw_world_lines, :center_x, :center_y, :cw_world_edge_normals
|
15
|
+
relegates :width unless @actor.respond_to? :width
|
16
|
+
relegates :height unless @actor.respond_to? :height
|
24
17
|
|
25
18
|
register_actor
|
26
19
|
end
|
@@ -29,13 +22,27 @@ class Collidable < Behavior
|
|
29
22
|
@actor.stage.register_collidable @actor
|
30
23
|
end
|
31
24
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
25
|
+
def build_shape
|
26
|
+
shape = nil
|
27
|
+
@collidable_shape = opts[:shape]
|
28
|
+
case @collidable_shape
|
29
|
+
when :circle
|
30
|
+
shape = CircleCollidable.new @actor, opts
|
31
|
+
when :aabb
|
32
|
+
shape = AaBbCollidable.new @actor, opts
|
33
|
+
when :polygon
|
34
|
+
shape = PolygonCollidable.new @actor, opts
|
35
|
+
end
|
36
|
+
|
37
|
+
shape.setup
|
38
|
+
shape
|
35
39
|
end
|
36
40
|
|
37
|
-
def
|
38
|
-
|
41
|
+
def update(time)
|
42
|
+
shape.update(time)
|
39
43
|
end
|
40
44
|
|
45
|
+
def method_missing(name, *args)
|
46
|
+
@shape.send(name, *args)
|
47
|
+
end
|
41
48
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'collidable/collidable_shape'
|
2
|
+
|
3
|
+
class AaBbCollidable < CollidableShape
|
4
|
+
attr_accessor :cw_local_points
|
5
|
+
|
6
|
+
# TODO infinite loop if actor hasn't defined width and it gets relegated to us
|
7
|
+
def calculate_radius
|
8
|
+
w = @actor.width
|
9
|
+
hw = w * 0.5
|
10
|
+
h = @actor.height
|
11
|
+
hh = h * 0.5
|
12
|
+
Math.sqrt(hw*hw+hh*hh)
|
13
|
+
end
|
14
|
+
|
15
|
+
def center_x
|
16
|
+
actor_x + @actor.width * 0.5
|
17
|
+
end
|
18
|
+
|
19
|
+
def center_y
|
20
|
+
actor_y + @actor.height * 0.5
|
21
|
+
end
|
22
|
+
|
23
|
+
def cw_world_points
|
24
|
+
@cached_points ||= @cw_local_points.map{|lp| [lp[0]+actor_x,lp[1]+actor_y]}
|
25
|
+
end
|
26
|
+
|
27
|
+
def cw_world_lines
|
28
|
+
return @cached_lines if @cached_lines
|
29
|
+
lines = []
|
30
|
+
|
31
|
+
hw = @actor.width * 0.5
|
32
|
+
hh = @actor.height * 0.5
|
33
|
+
lines = [
|
34
|
+
[[actor_x-hw,actor_y+hh], [actor_x+hw,actor_y+hh]],
|
35
|
+
[[actor_x+hw,actor_y+hh], [actor_x+hw,actor_y-hh]],
|
36
|
+
[[actor_x+hw,actor_y-hh], [actor_x-hw,actor_y-hh]],
|
37
|
+
[[actor_x-hw,actor_y-hh], [actor_x-hw,actor_y+hh]]
|
38
|
+
]
|
39
|
+
|
40
|
+
@cached_lines = lines
|
41
|
+
end
|
42
|
+
|
43
|
+
def cw_world_edge_normals
|
44
|
+
@cached_normals ||= [[1,0],[0,1]]
|
45
|
+
end
|
46
|
+
|
47
|
+
def recalculate_collidable_cache
|
48
|
+
unless @old_x == actor_x && @old_y == actor_y
|
49
|
+
clear_collidable_cache
|
50
|
+
@old_x = actor_x
|
51
|
+
@old_y = actor_y
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def clear_collidable_cache
|
56
|
+
@cached_points = nil
|
57
|
+
@cached_lines = nil
|
58
|
+
@cached_poly_center = nil
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|