gemini 1.0.0
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/bin/gemini +18 -0
- data/build_configuration.rb +24 -0
- data/lib/ibxm.jar +0 -0
- data/lib/jinput.jar +0 -0
- data/lib/jogg-0.0.7.jar +0 -0
- data/lib/jorbis-0.0.15.jar +0 -0
- data/lib/jruby-complete.jar +0 -0
- data/lib/lwjgl.jar +0 -0
- data/lib/lwjgl_util_applet.jar +0 -0
- data/lib/native_files/OpenAL32.dll +0 -0
- data/lib/native_files/jinput-dx8.dll +0 -0
- data/lib/native_files/jinput-raw.dll +0 -0
- data/lib/native_files/libjinput-linux.so +0 -0
- data/lib/native_files/libjinput-linux64.so +0 -0
- data/lib/native_files/libjinput-osx.jnilib +0 -0
- data/lib/native_files/liblwjgl.jnilib +0 -0
- data/lib/native_files/liblwjgl.so +0 -0
- data/lib/native_files/liblwjgl64.so +0 -0
- data/lib/native_files/libopenal.so +0 -0
- data/lib/native_files/lwjgl.dll +0 -0
- data/lib/native_files/openal.dylib +0 -0
- data/lib/natives-linux.jar +0 -0
- data/lib/natives-mac.jar +0 -0
- data/lib/natives-win32.jar +0 -0
- data/lib/phys2d.jar +0 -0
- data/lib/slick.jar +0 -0
- data/package/jar/gemini.jar +0 -0
- data/src/base_state.rb +112 -0
- data/src/behavior.rb +230 -0
- data/src/behavior_event.rb +23 -0
- data/src/behaviors.txt +5 -0
- data/src/behaviors/animated_sprite.rb +51 -0
- data/src/behaviors/audible.rb +11 -0
- data/src/behaviors/axis_stateful.rb +33 -0
- data/src/behaviors/big_sprite.rb +56 -0
- data/src/behaviors/bounding_box_collidable.rb +25 -0
- data/src/behaviors/camera_anchored_drawable.rb +20 -0
- data/src/behaviors/cardinal_movable.rb +114 -0
- data/src/behaviors/clickable.rb +18 -0
- data/src/behaviors/countable.rb +31 -0
- data/src/behaviors/debug_physical.rb +31 -0
- data/src/behaviors/debug_tangible.rb +30 -0
- data/src/behaviors/drawable.rb +6 -0
- data/src/behaviors/drawable_shape.rb +43 -0
- data/src/behaviors/fading_image_trail_emittable.rb +28 -0
- data/src/behaviors/game_object_emittable.rb +12 -0
- data/src/behaviors/gravity_source.rb +21 -0
- data/src/behaviors/inertial.rb +11 -0
- data/src/behaviors/movable2d.rb +9 -0
- data/src/behaviors/multi_animated_sprite.rb +22 -0
- data/src/behaviors/physical.rb +348 -0
- data/src/behaviors/physical_cardinal_movable.rb +110 -0
- data/src/behaviors/physical_sprite.rb +29 -0
- data/src/behaviors/platformer_controllable.rb +144 -0
- data/src/behaviors/pointer.rb +28 -0
- data/src/behaviors/pressable.rb +13 -0
- data/src/behaviors/receives_events.rb +21 -0
- data/src/behaviors/regional.rb +71 -0
- data/src/behaviors/rotates_to_point.rb +18 -0
- data/src/behaviors/spatial.rb +41 -0
- data/src/behaviors/spline_stretchable_sprite.rb +45 -0
- data/src/behaviors/sprite.rb +99 -0
- data/src/behaviors/stateful.rb +31 -0
- data/src/behaviors/taggable.rb +27 -0
- data/src/behaviors/tangible.rb +51 -0
- data/src/behaviors/timeable.rb +79 -0
- data/src/behaviors/top_down_vehicle.rb +41 -0
- data/src/behaviors/triangle_trail_emittable.rb +41 -0
- data/src/behaviors/updates.rb +10 -0
- data/src/behaviors/updates_at_consistant_rate.rb +27 -0
- data/src/behaviors/vectored_movement.rb +47 -0
- data/src/behaviors/world_collidable.rb +8 -0
- data/src/color.rb +70 -0
- data/src/game_object.rb +174 -0
- data/src/game_objects/background.rb +9 -0
- data/src/game_objects/fading_image.rb +23 -0
- data/src/game_objects/icon_strip_counter_display.rb +58 -0
- data/src/game_objects/static_sprite.rb +18 -0
- data/src/game_objects/tangible_object.rb +4 -0
- data/src/game_objects/text.rb +59 -0
- data/src/game_objects/triangle_trail.rb +85 -0
- data/src/gemini.rb +110 -0
- data/src/gemini_version.rb +3 -0
- data/src/inflector.rb +68 -0
- data/src/input_helpers/joystick_dead_zone_filter.rb +9 -0
- data/src/listenable_mixin.rb +15 -0
- data/src/managers/basic_game_object_manager.rb +81 -0
- data/src/managers/basic_physics_manager.rb +97 -0
- data/src/managers/basic_render_manager.rb +48 -0
- data/src/managers/basic_tangible_manager.rb +33 -0
- data/src/managers/basic_update_manager.rb +27 -0
- data/src/managers/diagnostic/diagnostic_input_manager.rb +0 -0
- data/src/managers/input_manager.rb +229 -0
- data/src/managers/input_support/input_mapping.rb +126 -0
- data/src/managers/input_support/input_message.rb +5 -0
- data/src/managers/input_support/slick_input_listener.rb +19 -0
- data/src/managers/input_support/slick_input_message.rb +11 -0
- data/src/managers/input_support/slick_input_translator.rb +15 -0
- data/src/managers/message_queue.rb +54 -0
- data/src/managers/scrolling_render_manager.rb +44 -0
- data/src/managers/sound_manager.rb +60 -0
- data/src/managers/tag_manager.rb +43 -0
- data/src/math.rb +13 -0
- data/src/music.rb +14 -0
- data/src/org/rubyforge/rawr/Main.java +67 -0
- data/src/platform.rb +28 -0
- data/src/proc_enhancement.rb +5 -0
- data/src/project_generator.rb +122 -0
- data/src/skeleton.txt +10 -0
- data/src/spline.rb +13 -0
- data/src/states/input_diagnostic_state.rb +53 -0
- data/src/vector.rb +99 -0
- data/test/test_state.rb +3 -0
- metadata +181 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
class Gemini::TriangleTrail < Gemini::GameObject
|
2
|
+
#has_behavior :Movable2d
|
3
|
+
has_behavior :Spatial
|
4
|
+
attr_accessor :radius, :alpha
|
5
|
+
|
6
|
+
def load
|
7
|
+
@trail = []
|
8
|
+
@trail_poly_size = 10
|
9
|
+
@trail_size = 50
|
10
|
+
@radius = 10
|
11
|
+
@alpha = 0.5
|
12
|
+
@flip = false
|
13
|
+
#TODO: Remove message when the callback stuff is checked in
|
14
|
+
@first_move = true
|
15
|
+
listen_for(:after_move) do |message|
|
16
|
+
if @first_move
|
17
|
+
@first_move = false
|
18
|
+
next
|
19
|
+
end
|
20
|
+
@trail.pop if @trail.size >= @trail_size
|
21
|
+
@trail.unshift([x, y])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def draw(graphics)
|
26
|
+
if @trail.size > 3
|
27
|
+
gl = Java::org::newdawn::slick::opengl::renderer::Renderer.get
|
28
|
+
|
29
|
+
#Java::org::newdawn::slick::opengl::SlickCallable.new do
|
30
|
+
callable_class = Java::org::newdawn::slick::opengl::SlickCallable
|
31
|
+
callable_class.enter_safe_block
|
32
|
+
# use LWJGL's const directly, Slick's wrapper does not have the triangle strip const
|
33
|
+
triangle_strip_enum = Java::org::lwjgl::opengl::GL11::GL_TRIANGLE_STRIP
|
34
|
+
gl.gl_begin triangle_strip_enum
|
35
|
+
gl.gl_color4f(0.0, 1.0, 1.0, @alpha)
|
36
|
+
|
37
|
+
origin_x, origin_y = calculate_point_on_trail_edge(@trail[1], @trail[0], @radius, @flip)
|
38
|
+
gl.gl_vertex2f(origin_x, origin_y)
|
39
|
+
|
40
|
+
flipped_origin_x, flipped_origin_y = calculate_point_on_trail_edge(@trail[1], @trail[0], @radius, !@flip)
|
41
|
+
gl.gl_vertex2f(flipped_origin_x, flipped_origin_y)
|
42
|
+
|
43
|
+
last_trail_vector = @trail[1]
|
44
|
+
#gl.gl_color4f(0.0, 1.0, 1.0, alpha)
|
45
|
+
@trail[2..-1].each_with_index do |trail_vector, index|
|
46
|
+
next if trail_vector == last_trail_vector
|
47
|
+
actual_trail_size = (@trail.size - 1).to_f
|
48
|
+
trail_radius = (@radius) * ((actual_trail_size - (index + 2).to_f) / actual_trail_size)
|
49
|
+
|
50
|
+
rotated_x, rotated_y = calculate_point_on_trail_edge(trail_vector, last_trail_vector, trail_radius, @flip)
|
51
|
+
gl.gl_vertex2f(rotated_x, rotated_y)
|
52
|
+
rotated_x, rotated_y = calculate_point_on_trail_edge(trail_vector, last_trail_vector, trail_radius, !@flip)
|
53
|
+
gl.gl_vertex2f(rotated_x, rotated_y)
|
54
|
+
|
55
|
+
last_trail_vector = trail_vector
|
56
|
+
end
|
57
|
+
gl.gl_end
|
58
|
+
callable_class.leave_safe_block
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def calculate_point_on_trail_edge(current_vector, previous_vector, trail_radius, flip)
|
63
|
+
trail_diff = [current_vector[0] - previous_vector[0], current_vector[1] - previous_vector[1]]
|
64
|
+
diff_angle = Math.atan2(trail_diff[1].to_f, trail_diff[0].to_f)
|
65
|
+
|
66
|
+
rotation_angle = (Math::PI * 1) / 2
|
67
|
+
rotation_angle = -rotation_angle if flip
|
68
|
+
|
69
|
+
sine = Math.sin(diff_angle + rotation_angle)
|
70
|
+
cosine = Math.cos(diff_angle + rotation_angle)
|
71
|
+
rotated_weight_x = cosine
|
72
|
+
rotated_weight_y = sine
|
73
|
+
|
74
|
+
rotated_x = (trail_radius * rotated_weight_x) + current_vector[0]
|
75
|
+
rotated_y = (trail_radius * rotated_weight_y) + current_vector[1]
|
76
|
+
|
77
|
+
[rotated_x, rotated_y]
|
78
|
+
end
|
79
|
+
|
80
|
+
def normalize_vector(vector)
|
81
|
+
max_value = vector.map{|value| value.abs}.max
|
82
|
+
return Array.new(vector.size, 0) if max_value.zero?
|
83
|
+
vector.map {|value| value / max_value}
|
84
|
+
end
|
85
|
+
end
|
data/src/gemini.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + '/managers').gsub('%20', ' ')
|
2
|
+
$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + '/game_objects').gsub('%20', ' ')
|
3
|
+
#$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + '/states')
|
4
|
+
$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + '/input_helpers').gsub('%20', ' ')
|
5
|
+
|
6
|
+
# Because Windows isn't friendly with JRuby
|
7
|
+
$LOAD_PATH << 'managers'
|
8
|
+
$LOAD_PATH << 'game_objects'
|
9
|
+
#$LOAD_PATH << 'states'
|
10
|
+
$LOAD_PATH << 'input_helpers'
|
11
|
+
|
12
|
+
require 'platform'
|
13
|
+
require 'color'
|
14
|
+
require 'vector'
|
15
|
+
require 'spline'
|
16
|
+
require 'inflector'
|
17
|
+
|
18
|
+
require 'math'
|
19
|
+
require 'proc_enhancement'
|
20
|
+
require 'game_object'
|
21
|
+
require 'message_queue'
|
22
|
+
require 'input_manager'
|
23
|
+
require 'base_state'
|
24
|
+
require 'inflector'
|
25
|
+
require 'basic_game_object_manager'
|
26
|
+
require 'basic_update_manager'
|
27
|
+
require 'basic_render_manager'
|
28
|
+
|
29
|
+
module Gemini
|
30
|
+
class Main < Java::org::newdawn::slick::BasicGame
|
31
|
+
include_class 'org.newdawn.slick.AppGameContainer'
|
32
|
+
attr_accessor :screen_width, :screen_height, :initial_state
|
33
|
+
|
34
|
+
def self.start_app(screen_title, screen_width, screen_height, initial_state, fullscreen=false)
|
35
|
+
puts "in start app"
|
36
|
+
main = Main.new(screen_title, initial_state)
|
37
|
+
main.screen_width = screen_width
|
38
|
+
main.screen_height = screen_height
|
39
|
+
container = AppGameContainer.new(main, screen_width, screen_height, fullscreen)
|
40
|
+
container.vsync = false
|
41
|
+
container.maximum_logic_update_interval = 60
|
42
|
+
container.smooth_deltas = false #true
|
43
|
+
#main.container = container
|
44
|
+
container.start
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.create_canvas(screen_title, screen_width, screen_height, initial_state)
|
48
|
+
main = Main.new screen_title, initial_state
|
49
|
+
main.screen_width = screen_width
|
50
|
+
main.screen_height = screen_height
|
51
|
+
puts "creating canvas"
|
52
|
+
$canvas = Java::org::newdawn::slick::CanvasGameContainer.new(main)
|
53
|
+
puts "setting size to #{screen_width}, #{screen_height}"
|
54
|
+
$canvas.set_size(screen_width, screen_height)
|
55
|
+
puts "done setting size"
|
56
|
+
container = $canvas.container
|
57
|
+
container.vsync = true
|
58
|
+
container.maximum_logic_update_interval = 60
|
59
|
+
#container.start
|
60
|
+
$canvas
|
61
|
+
end
|
62
|
+
|
63
|
+
def initialize(screen_title=nil, initial_state=:MainState)
|
64
|
+
super(screen_title)
|
65
|
+
@initial_state = initial_state
|
66
|
+
@fresh_state = true
|
67
|
+
end
|
68
|
+
|
69
|
+
def init(container)
|
70
|
+
@container = container
|
71
|
+
BaseState.active_state = load_state(@initial_state)
|
72
|
+
BaseState.active_state.load
|
73
|
+
end
|
74
|
+
|
75
|
+
def update(container, delta)
|
76
|
+
#don't tell the new state that it now has to update load time worth of a delta
|
77
|
+
if @fresh_state
|
78
|
+
delta = 0
|
79
|
+
@fresh_state = false
|
80
|
+
end
|
81
|
+
# Workaround for image loading with Slick.
|
82
|
+
# Must be done in game init or game loop (instead of immediately in the event).
|
83
|
+
if @queued_state
|
84
|
+
@queued_state.load(*@state_args)
|
85
|
+
BaseState.active_state = @queued_state
|
86
|
+
@queued_state = nil
|
87
|
+
@fresh_state = true
|
88
|
+
return
|
89
|
+
end
|
90
|
+
BaseState.active_state.manager(:input).poll(@screen_width, @screen_height, delta)
|
91
|
+
BaseState.active_state.manager(:message_queue).process_messages(delta)
|
92
|
+
BaseState.active_state.manager(:update).update(delta)
|
93
|
+
BaseState.active_state.manager(:game_object).__process_pending_game_objects
|
94
|
+
end
|
95
|
+
|
96
|
+
def render(container, graphics)
|
97
|
+
BaseState.active_state.manager(:render).render(graphics)
|
98
|
+
end
|
99
|
+
|
100
|
+
def load_state(state_name, args = [])
|
101
|
+
require "states/#{state_name.underscore}" unless Object.const_defined? state_name.camelize
|
102
|
+
@state_args = args
|
103
|
+
state_name.camelize.constantize.new @container, self
|
104
|
+
end
|
105
|
+
|
106
|
+
def queue_state(state)
|
107
|
+
@queued_state = state
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/src/inflector.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# This code is a modified version of the Inflector class
|
2
|
+
# from the Ruby on Rails project (http://www.rubyonrails.com)
|
3
|
+
|
4
|
+
module Monkeybars
|
5
|
+
module Inflector
|
6
|
+
# The reverse of +camelize+. Makes an underscored form from the expression in the string.
|
7
|
+
#
|
8
|
+
# Changes '::' to '/' to convert namespaces to paths.
|
9
|
+
#
|
10
|
+
# Examples
|
11
|
+
# "ActiveRecord".underscore #=> "active_record"
|
12
|
+
# "ActiveRecord::Errors".underscore #=> active_record/errors
|
13
|
+
def underscore()
|
14
|
+
self.to_s.gsub(/::/, '/').
|
15
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
16
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
17
|
+
tr("-", "_").
|
18
|
+
downcase
|
19
|
+
end
|
20
|
+
|
21
|
+
# Constantize tries to find a declared constant with the name specified
|
22
|
+
# in the string. It raises a NameError when the name is not in CamelCase
|
23
|
+
# or is not initialized.
|
24
|
+
#
|
25
|
+
# Examples
|
26
|
+
# "Module".constantize #=> Module
|
27
|
+
# "Class".constantize #=> Class
|
28
|
+
def constantize()
|
29
|
+
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ self.to_s
|
30
|
+
raise NameError, "#{self.inspect} is not a valid constant name!"
|
31
|
+
end
|
32
|
+
|
33
|
+
Object.module_eval("::#{$1}", __FILE__, __LINE__)
|
34
|
+
end
|
35
|
+
|
36
|
+
# By default, camelize converts strings to UpperCamelCase. If the argument to camelize
|
37
|
+
# is set to ":lower" then camelize produces lowerCamelCase.
|
38
|
+
#
|
39
|
+
# camelize will also convert '/' to '::' which is useful for converting paths to namespaces
|
40
|
+
#
|
41
|
+
# Examples
|
42
|
+
# "active_record".camelize #=> "ActiveRecord"
|
43
|
+
# "active_record".camelize(:lower) #=> "activeRecord"
|
44
|
+
# "active_record/errors".camelize #=> "ActiveRecord::Errors"
|
45
|
+
# "active_record/errors".camelize(:lower) #=> "activeRecord::Errors"
|
46
|
+
def camelize(first_letter_in_uppercase = true)
|
47
|
+
if first_letter_in_uppercase
|
48
|
+
self.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
49
|
+
else
|
50
|
+
self.to_s[0..0] + camelize(self.to_s)[1..-1]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class String
|
57
|
+
include Monkeybars::Inflector
|
58
|
+
end
|
59
|
+
|
60
|
+
class Symbol
|
61
|
+
include Monkeybars::Inflector
|
62
|
+
end
|
63
|
+
|
64
|
+
class Class
|
65
|
+
def constantize
|
66
|
+
self
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Gemini
|
2
|
+
module ListenableMixin
|
3
|
+
def __added_listener_for(target, method_name, callback)
|
4
|
+
@__methods_registered ||= {}
|
5
|
+
@__methods_registered[target] = [method_name, callback]
|
6
|
+
end
|
7
|
+
|
8
|
+
def __remove_listeners
|
9
|
+
return if @__methods_registered.nil?
|
10
|
+
@__methods_registered.each do |target, listener_method_name_and_callback|
|
11
|
+
target.send("remove_#{listener_method_name_and_callback.first}", self, listener_method_name_and_callback.last)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
class BasicGameObjectManager < Gemini::GameObject
|
2
|
+
attr_reader :layers
|
3
|
+
FRONT_LAYER = 'front'
|
4
|
+
BACK_LAYER = 'back'
|
5
|
+
|
6
|
+
def load
|
7
|
+
enable_listeners_for :before_add_game_object, :after_add_game_object, :before_remove_game_object, :after_remove_game_object
|
8
|
+
#@layers = Hash.new{|h,k| h[k] = []}
|
9
|
+
@layer_order = [:default]
|
10
|
+
@layers = {:default => []}
|
11
|
+
@game_objects_to_remove = []
|
12
|
+
@game_objects_to_add = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def __process_pending_game_objects
|
16
|
+
@game_objects_to_remove.pop.__destroy until @game_objects_to_remove.empty?
|
17
|
+
until @game_objects_to_add.empty?
|
18
|
+
layer, game_object = @game_objects_to_add.pop
|
19
|
+
@layers[layer] << game_object
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_game_object(game_object)
|
24
|
+
notify :before_add_game_object, game_object, :default
|
25
|
+
@layers[:default] << game_object
|
26
|
+
notify :after_add_game_object, game_object, :default
|
27
|
+
end
|
28
|
+
|
29
|
+
def remove_game_object(game_object)
|
30
|
+
owning_layer = @layers.values.find {|layer| layer.include? game_object}
|
31
|
+
return if owning_layer.nil? #NOTE: Not sure if this is the right thing to do, but at least no exception is thrown
|
32
|
+
notify :before_remove_game_object, game_object
|
33
|
+
owning_layer.delete game_object
|
34
|
+
game_object.unload
|
35
|
+
notify :after_remove_game_object, game_object
|
36
|
+
@game_objects_to_remove.push game_object
|
37
|
+
#game_object.unload
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_game_object_to_layer(game_object, layer)
|
41
|
+
notify :before_add_game_object, game_object, layer
|
42
|
+
@layers[layer] << game_object
|
43
|
+
#@game_objects_to_add.push [layer, game_object]
|
44
|
+
notify :after_add_game_object, game_object
|
45
|
+
end
|
46
|
+
|
47
|
+
def move_game_object_to_layer(game_object, layer_name)
|
48
|
+
owning_layer = @layers.values.find {|layer| layer.include? game_object}
|
49
|
+
owning_layer.delete game_object
|
50
|
+
@layers[layer_name] << game_object
|
51
|
+
end
|
52
|
+
|
53
|
+
def game_objects
|
54
|
+
game_objects = []
|
55
|
+
@layers.values.each do |layer|
|
56
|
+
layer.each do |game_object|
|
57
|
+
game_objects << game_object
|
58
|
+
end
|
59
|
+
end
|
60
|
+
game_objects
|
61
|
+
end
|
62
|
+
|
63
|
+
def layers_by_order
|
64
|
+
@layer_order.map {|layer_name| @layers[layer_name]}.compact
|
65
|
+
end
|
66
|
+
|
67
|
+
def add_layer_at(layer_name, location)
|
68
|
+
if location.kind_of? Numeric
|
69
|
+
@layer_order.delete_at(location) if @layer_order[location].nil?
|
70
|
+
@layer_order.insert(location, layer_name)
|
71
|
+
else # string/symbol
|
72
|
+
case location.to_s
|
73
|
+
when FRONT_LAYER
|
74
|
+
@layer_order << layer_name
|
75
|
+
when BACK_LAYER
|
76
|
+
@layer_order.unshift(layer_name)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
@layers[layer_name] = []
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'behaviors/physical'
|
2
|
+
|
3
|
+
class BasicPhysicsManager < Gemini::GameObject
|
4
|
+
INTERPOLATION_THESHOLD = 6.0
|
5
|
+
MILLISECONDS_PER_UPDATE = 6 # 6000/ 1000
|
6
|
+
DELTA_FACTOR = 0.01
|
7
|
+
include_class 'net.phys2d.math.Vector2f'
|
8
|
+
include_class 'net.phys2d.raw.World'
|
9
|
+
include_class 'net.phys2d.raw.strategies.QuadSpaceStrategy'
|
10
|
+
include_class 'net.phys2d.raw.strategies.BruteCollisionStrategy'
|
11
|
+
has_behavior :ReceivesEvents
|
12
|
+
|
13
|
+
def load
|
14
|
+
@delta_debt = 0
|
15
|
+
@world = World.new(Vector2f.new(0, 0), 5, QuadSpaceStrategy.new(20, 5))
|
16
|
+
# @world = World.new(Vector2f.new(0, 0), 10, BruteCollisionStrategy.new)
|
17
|
+
@world.add_listener self
|
18
|
+
@game_state.manager(:update).on_update do |delta|
|
19
|
+
update delta
|
20
|
+
end
|
21
|
+
|
22
|
+
@game_state.manager(:game_object).on_after_add_game_object do |game_object|
|
23
|
+
add_to_world game_object if game_object.kind_of? Physical
|
24
|
+
end
|
25
|
+
|
26
|
+
@game_state.manager(:game_object).on_after_remove_game_object do |game_object|
|
27
|
+
game_object.remove_from_world(@world) if game_object.kind_of? Physical
|
28
|
+
end
|
29
|
+
|
30
|
+
handle_event :toggle_debug_mode, :toggle_debug_mode
|
31
|
+
end
|
32
|
+
|
33
|
+
def update(delta)
|
34
|
+
delta += @delta_debt
|
35
|
+
@delta_debt = 0
|
36
|
+
if delta == MILLISECONDS_PER_UPDATE
|
37
|
+
step
|
38
|
+
else
|
39
|
+
temp_delta = delta
|
40
|
+
until temp_delta <= 0
|
41
|
+
new_delta = temp_delta > MILLISECONDS_PER_UPDATE ? MILLISECONDS_PER_UPDATE : temp_delta
|
42
|
+
if new_delta < MILLISECONDS_PER_UPDATE
|
43
|
+
@delta_debt = new_delta
|
44
|
+
return #don't step, we'll try again next update
|
45
|
+
else
|
46
|
+
step
|
47
|
+
temp_delta -= new_delta
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# there's a typo in the API, I swears it.
|
54
|
+
def collision_occured(event)
|
55
|
+
event.body_a.user_data.notify :physical_collided, PhysicsMessage.new(event, event.body_b.user_data)
|
56
|
+
event.body_b.user_data.notify :physical_collided, PhysicsMessage.new(event, event.body_a.user_data)
|
57
|
+
end
|
58
|
+
|
59
|
+
def toggle_debug_mode(message)
|
60
|
+
@debug_mode = !@debug_mode
|
61
|
+
@game_state.manager(:game_object).game_objects.each do |game_object|
|
62
|
+
game_object.set_physical_debug_mode(@debug_mode) if game_object.kind_of? Physical
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def gravity=(gravity_or_x)
|
67
|
+
if gravity_or_x.kind_of? Numeric
|
68
|
+
@world.set_gravity(0, gravity_or_x)
|
69
|
+
else
|
70
|
+
raise "Not implemented yet"
|
71
|
+
# @world.set_gravity(gravity_or_x)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
def step
|
77
|
+
@world.step # must step at fixed rate (1/60f) or results are unpredictable
|
78
|
+
end
|
79
|
+
|
80
|
+
def add_to_world(game_object)
|
81
|
+
game_object.add_to_world(@world)
|
82
|
+
game_object.set_physical_debug_mode(true) if @debug_mode
|
83
|
+
end
|
84
|
+
# def colliding?(body)
|
85
|
+
# 0 < @world.get_contacts(body).size
|
86
|
+
# end
|
87
|
+
|
88
|
+
#TODO: Make subclass of Message
|
89
|
+
class PhysicsMessage
|
90
|
+
attr_reader :other, :event
|
91
|
+
|
92
|
+
def initialize(slick_event, other)
|
93
|
+
@event = slick_event
|
94
|
+
@other = other
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|