gemini 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/gemini +20 -12
- data/package/jar/gemini.jar +0 -0
- data/src/base_state.rb +6 -12
- data/src/behaviors/animated_sprite.rb +6 -2
- data/src/behaviors/audible.rb +6 -1
- data/src/behaviors/axis_stateful.rb +2 -0
- data/src/behaviors/big_sprite.rb +2 -1
- data/src/behaviors/bounding_box_collidable.rb +2 -0
- data/src/behaviors/camera_anchored_drawable.rb +1 -1
- data/src/behaviors/cardinal_movable.rb +18 -11
- data/src/behaviors/clickable.rb +1 -0
- data/src/behaviors/countable.rb +1 -0
- data/src/behaviors/debug_physical.rb +25 -13
- data/src/behaviors/debug_tangible.rb +1 -0
- data/src/behaviors/drawable.rb +1 -0
- data/src/behaviors/drawable_shape.rb +4 -3
- data/src/behaviors/fading_image_trail_emittable.rb +4 -0
- data/src/behaviors/game_object_emittable.rb +1 -0
- data/src/behaviors/gravity_source.rb +3 -2
- data/src/behaviors/inertial.rb +3 -0
- data/src/behaviors/movable2d.rb +1 -0
- data/src/behaviors/multi_animated_sprite.rb +7 -1
- data/src/behaviors/physical.rb +123 -77
- data/src/behaviors/physical_cardinal_movable.rb +14 -13
- data/src/behaviors/physical_sprite.rb +25 -9
- data/src/behaviors/platformer_controllable.rb +9 -2
- data/src/behaviors/pointer.rb +2 -0
- data/src/behaviors/pressable.rb +4 -0
- data/src/behaviors/receives_events.rb +3 -0
- data/src/behaviors/regional.rb +5 -0
- data/src/behaviors/repulsive.rb +28 -0
- data/src/behaviors/rotates_to_point.rb +2 -1
- data/src/behaviors/spatial.rb +4 -4
- data/src/behaviors/sprite.rb +17 -6
- data/src/behaviors/stateful.rb +2 -0
- data/src/behaviors/taggable.rb +1 -0
- data/src/behaviors/tangible.rb +8 -0
- data/src/behaviors/timeable.rb +9 -0
- data/src/behaviors/top_down_vehicle.rb +1 -0
- data/src/behaviors/triangle_trail_emittable.rb +5 -0
- data/src/behaviors/updates.rb +1 -0
- data/src/behaviors/updates_at_consistant_rate.rb +1 -0
- data/src/behaviors/vectored_movement.rb +1 -0
- data/src/behaviors/world_collidable.rb +1 -0
- data/src/game_objects/static_sprite.rb +3 -2
- data/src/game_objects/triangle_trail.rb +1 -1
- data/src/gemini_version.rb +1 -1
- data/src/managers/input_support/input_mapping.rb +10 -10
- data/src/managers/scrolling_render_manager.rb +8 -6
- data/src/project_generator.rb +6 -0
- data/src/vector.rb +19 -7
- metadata +4 -3
data/bin/gemini
CHANGED
@@ -2,17 +2,25 @@
|
|
2
2
|
$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..', 'src'))
|
3
3
|
require 'project_generator'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
5
|
+
begin
|
6
|
+
rawr_install_args = ''
|
7
|
+
project_dir = nil
|
8
|
+
until ARGV.empty?
|
9
|
+
arg = ARGV.shift
|
10
|
+
case arg
|
11
|
+
when /-RI(.+)/
|
12
|
+
rawr_install_args << $1 + ' '
|
13
|
+
else
|
14
|
+
project_dir = arg
|
15
|
+
end
|
14
16
|
end
|
17
|
+
raise "No project dir provided" if project_dir.nil?
|
18
|
+
generator = Gemini::ProjectGenerator.new(:project_dir => project_dir, :rawr_install => rawr_install_args)
|
19
|
+
generator.generate_project
|
20
|
+
rescue => exception
|
21
|
+
abort [
|
22
|
+
"Usage: gemini [-RI--no-download] <dir_name>",
|
23
|
+
"Error: #{exception.message}",
|
24
|
+
exception.backtrace
|
25
|
+
].join("\n")
|
15
26
|
end
|
16
|
-
raise "No project dir provided" if project_dir.nil?
|
17
|
-
generator = Gemini::ProjectGenerator.new(:project_dir => project_dir, :rawr_install => rawr_install_args)
|
18
|
-
generator.generate_project
|
data/package/jar/gemini.jar
CHANGED
Binary file
|
data/src/base_state.rb
CHANGED
@@ -38,19 +38,13 @@ module Gemini
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def create_on_layer(type, layer_name, *params)
|
41
|
-
game_object_type =
|
41
|
+
game_object_type = if :game_object == type.to_sym || :GameObject == type.to_sym
|
42
|
+
Gemini::GameObject
|
43
|
+
elsif Module.const_defined?(type.camelize.to_sym)
|
42
44
|
type.constantize
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
begin
|
47
|
-
type.constantize
|
48
|
-
rescue NameError
|
49
|
-
"Gemini::#{type}".constantize
|
50
|
-
end
|
51
|
-
rescue LoadError
|
52
|
-
"Gemini::#{type}".constantize
|
53
|
-
end
|
45
|
+
else
|
46
|
+
require type.underscore
|
47
|
+
type.camelize.constantize
|
54
48
|
end
|
55
49
|
game_object = game_object_type.new(self, *params)
|
56
50
|
add_game_object_to_layer game_object, layer_name
|
@@ -1,3 +1,4 @@
|
|
1
|
+
#A 2D sprite with mutltiple frames of animation.
|
1
2
|
class AnimatedSprite < Gemini::Behavior
|
2
3
|
include_class 'org.newdawn.slick.Image'
|
3
4
|
include_class 'org.newdawn.slick.Animation'
|
@@ -17,7 +18,8 @@ class AnimatedSprite < Gemini::Behavior
|
|
17
18
|
end
|
18
19
|
@mode = :normal
|
19
20
|
end
|
20
|
-
|
21
|
+
|
22
|
+
#Takes one or more Images or names of files in the data directory to add to the animation.
|
21
23
|
def sprites(*sprite_names)
|
22
24
|
sprite_names.each do |sprite_name|
|
23
25
|
if sprite_name.kind_of? Image
|
@@ -29,12 +31,14 @@ class AnimatedSprite < Gemini::Behavior
|
|
29
31
|
@target.set_image @animation.current_frame
|
30
32
|
end
|
31
33
|
alias_method :set_sprites, :sprites
|
32
|
-
|
34
|
+
|
35
|
+
#Sets frames per second for the animation.
|
33
36
|
def animation_fps(fps)
|
34
37
|
@fps = fps
|
35
38
|
(0...@animation.frame_count).each {|i| @animation.set_duration(i, 1000.0/@fps)}
|
36
39
|
end
|
37
40
|
|
41
|
+
#Sets animation mode. Possible values are :normal, :looping, or :ping_pong.
|
38
42
|
def animation_mode(mode)
|
39
43
|
case mode
|
40
44
|
when :normal
|
data/src/behaviors/audible.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
|
+
#Makes an object emit sounds.
|
1
2
|
class Audible < Gemini::Behavior
|
2
3
|
|
4
|
+
#Load a sound file and assign a reference to it, which can later be passed to emit_sound.
|
3
5
|
def load_sound(reference, path)
|
4
6
|
@target.game_state.manager(:sound).add_sound(reference, path)
|
5
7
|
end
|
6
|
-
|
8
|
+
|
9
|
+
#Plays a sound whose reference was assigned via load_sound.
|
10
|
+
#Pass in volume to play it back quieter or louder (1.0 is normal).
|
11
|
+
#Pass in pitch for a higher or lower tone (1.0 is normal).
|
7
12
|
def emit_sound(reference, volume = 1.0, pitch = 1.0)
|
8
13
|
@target.game_state.manager(:sound).play_sound(reference, volume, pitch)
|
9
14
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
+
#Makes an object transition between several states in succession.
|
1
2
|
class AxisStateful < Gemini::Behavior
|
3
|
+
#Indicates that an object has transitioned from one state to another along an axis.
|
2
4
|
class AxialStateTransferEvent
|
3
5
|
attr_accessor :before_state, :after_state, :axis
|
4
6
|
def initialize(axis, before_state, after_state)
|
data/src/behaviors/big_sprite.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
# Simplistic implementation of a big image to be used as a background image
|
2
1
|
require 'behaviors/drawable'
|
3
2
|
|
3
|
+
#Simplistic implementation of a big image to be used as a background image. See Sprite.
|
4
4
|
class BigSprite < Drawable
|
5
5
|
include_class 'org.newdawn.slick.BigImage'
|
6
6
|
attr_accessor :image
|
@@ -38,6 +38,7 @@ class BigSprite < Drawable
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
+
#Takes a BigImage, but is otherwise identical to Sprite.set_image.
|
41
42
|
def image=(sprite_name)
|
42
43
|
if sprite_name.kind_of? BigImage
|
43
44
|
@image = sprite_name
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'behavior_event'
|
2
2
|
include_class 'org.newdawn.slick.geom.Rectangle'
|
3
3
|
|
4
|
+
#Makes an object generate a BoundingBoxCollisionEvent if its bounding box intersects another's.
|
4
5
|
class BoundingBoxCollidable < Gemini::Behavior
|
5
6
|
depends_on :Spatial2d
|
6
7
|
|
@@ -15,6 +16,7 @@ class BoundingBoxCollidable < Gemini::Behavior
|
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
19
|
+
#Indicates that one object has collided with another.
|
18
20
|
class BoundingBoxCollisionEvent < Gemini::BehaviorEvent
|
19
21
|
attr_accessor :colliding_object, :collided_object
|
20
22
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
#These are sprites that deliberately ignore the scrolling of the render manager.
|
2
2
|
class CameraAnchoredDrawable < Gemini::Behavior
|
3
3
|
#TODO: Change this to Drawable when the engine supports overriding declared methods (such as draw)
|
4
4
|
depends_on :Sprite
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# A CardinalMovable can move north, east, south and west.
|
2
2
|
# Movement along certain axis can be constrained, for example, pong has horizonal (north/east)
|
3
|
-
#
|
4
|
-
# TODO: Allow disabling of diaganol easily
|
3
|
+
# TODO: Allow disabling of diagonal easily
|
5
4
|
# TODO: Allow disabling of directions
|
6
5
|
# TODO: Allow speed limit per axis
|
7
6
|
# TODO: Allow speed limit per direction
|
@@ -14,8 +13,8 @@ class CardinalMovable < Gemini::Behavior
|
|
14
13
|
SOUTH_EAST = :south_east
|
15
14
|
SOUTH_WEST = :south_west
|
16
15
|
NORTH_WEST = :north_west
|
17
|
-
|
18
|
-
CARDINAL_DIRECTIONS = [NORTH, EAST, SOUTH, WEST] +
|
16
|
+
DIAGONAL_DIRECTIONS = [NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST]
|
17
|
+
CARDINAL_DIRECTIONS = [NORTH, EAST, SOUTH, WEST] + DIAGONAL_DIRECTIONS
|
19
18
|
DIRECTION_TRANSLATION_IN_DEGREES = []
|
20
19
|
|
21
20
|
depends_on :Tangible
|
@@ -39,12 +38,12 @@ class CardinalMovable < Gemini::Behavior
|
|
39
38
|
def facing_direction=(direction)
|
40
39
|
if @allowed_directions.include? direction
|
41
40
|
if @moving && orthogonal_directions?(@facing_direction, direction)
|
42
|
-
|
41
|
+
diagonal_direction = begin
|
43
42
|
"#{self.class}::#{@facing_direction.to_s.upcase}_#{direction.to_s.upcase}".constantize
|
44
43
|
rescue
|
45
44
|
"#{self.class}::#{direction.to_s.upcase}_#{@facing_direction.to_s.upcase}".constantize
|
46
45
|
end
|
47
|
-
@facing_direction =
|
46
|
+
@facing_direction = diagonal_direction
|
48
47
|
else
|
49
48
|
@facing_direction = direction
|
50
49
|
end
|
@@ -52,12 +51,16 @@ class CardinalMovable < Gemini::Behavior
|
|
52
51
|
end
|
53
52
|
alias_method :set_facing_direction, :facing_direction=
|
54
53
|
|
54
|
+
#Set direction(s) the object is allowed to move in.
|
55
|
+
#Takes either a single directional constant, or an Array of them.
|
55
56
|
def constrain_direction(directions)
|
56
57
|
directions = [directions] unless directions.kind_of? Array
|
57
58
|
directions.each
|
58
59
|
directions.each { |direction| @allowed_directions.delete direction }
|
59
60
|
end
|
60
61
|
|
62
|
+
#Initiate movement in a given direction.
|
63
|
+
#Takes a message with a directional constant as its value.
|
61
64
|
def begin_cardinal_movement(message)
|
62
65
|
direction = message.value
|
63
66
|
return unless @allowed_directions.include? direction
|
@@ -66,9 +69,11 @@ class CardinalMovable < Gemini::Behavior
|
|
66
69
|
@cardinal_velocity = direction_to_polar_vector(@facing_direction)
|
67
70
|
end
|
68
71
|
|
72
|
+
#Halt movement in a given direction.
|
73
|
+
#Takes a message with a directional constant as its value.
|
69
74
|
def end_cardinal_movement(message)
|
70
75
|
direction = message.value
|
71
|
-
@facing_direction = other_direction(@facing_direction, direction) if
|
76
|
+
@facing_direction = other_direction(@facing_direction, direction) if diagonal_direction? @facing_direction
|
72
77
|
if @facing_direction == direction
|
73
78
|
@cardinal_velocity = Vector.new(0,0)
|
74
79
|
@moving = false
|
@@ -76,6 +81,8 @@ class CardinalMovable < Gemini::Behavior
|
|
76
81
|
@cardinal_velocity = direction_to_polar_vector(@facing_direction)
|
77
82
|
end
|
78
83
|
end
|
84
|
+
|
85
|
+
private
|
79
86
|
|
80
87
|
def direction_to_polar_vector(direction)
|
81
88
|
angle = case direction
|
@@ -99,12 +106,12 @@ class CardinalMovable < Gemini::Behavior
|
|
99
106
|
Vector.from_polar_vector(@cardinal_speed, angle)
|
100
107
|
end
|
101
108
|
|
102
|
-
def
|
103
|
-
|
109
|
+
def diagonal_direction?(direction)
|
110
|
+
DIAGONAL_DIRECTIONS.include? direction
|
104
111
|
end
|
105
112
|
|
106
|
-
def other_direction(
|
107
|
-
|
113
|
+
def other_direction(diagonal_direction, direction)
|
114
|
+
diagonal_direction.to_s.sub(direction.to_s, '').sub('_', '').to_sym
|
108
115
|
end
|
109
116
|
|
110
117
|
def orthogonal_directions?(direction_a, direction_b)
|
data/src/behaviors/clickable.rb
CHANGED
data/src/behaviors/countable.rb
CHANGED
@@ -1,8 +1,17 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
#Makes an object draw its interactions with the physics engine on the screen.
|
2
|
+
class DebugPhysical < Gemini::Behavior
|
3
|
+
|
4
|
+
PhysVector = Java::net::phys2d::math::Vector2f
|
5
|
+
PhysCircle = Java::net.phys2d.raw.shapes.Circle
|
6
|
+
PhysPolygon = Java::net.phys2d.raw.shapes.Polygon
|
7
|
+
PhysLine = Java::net.phys2d.raw.shapes.Line
|
8
|
+
SlickVector = Java::org::newdawn::slick::geom::Vector2f
|
9
|
+
SlickPolygon = Java::org.newdawn.slick.geom.Polygon
|
10
|
+
SlickCircle = Java::org.newdawn.slick.geom.Circle
|
11
|
+
SlickLine = Java::org.newdawn.slick.geom.Line
|
12
|
+
|
13
|
+
include_class 'net.phys2d.raw.shapes.Box'
|
14
|
+
|
6
15
|
def load
|
7
16
|
@target.game_state.manager(:render).on_after_render do |graphics|
|
8
17
|
draw(graphics)
|
@@ -10,19 +19,22 @@ class DebugPhysical < Gemini::Behavior #Drawable
|
|
10
19
|
end
|
11
20
|
|
12
21
|
def unload
|
13
|
-
# Remove listener for after render
|
22
|
+
# TODO: Remove listener for after render?
|
14
23
|
end
|
15
|
-
|
24
|
+
|
25
|
+
private
|
16
26
|
def draw(graphics)
|
17
27
|
#TODO: Support joints and composite bodies(?)
|
18
28
|
body = @target.instance_variable_get(:@__behaviors)[:Physical].instance_variable_get(:@body)
|
19
29
|
physics_shape = body.shape
|
20
|
-
graphics_shape = if physics_shape.kind_of?
|
21
|
-
|
22
|
-
elsif physics_shape.kind_of?
|
23
|
-
|
24
|
-
elsif physics_shape.kind_of?
|
25
|
-
|
30
|
+
graphics_shape = if physics_shape.kind_of? Box
|
31
|
+
SlickPolygon.new(physics_shape.get_points(body.position, body.rotation).map{|point| [point.x, point.y]}.flatten.to_java(:float))
|
32
|
+
elsif physics_shape.kind_of? PhysPolygon
|
33
|
+
SlickPolygon.new(physics_shape.get_vertices(body.position, body.rotation).map{|point| [point.x, point.y]}.flatten.to_java(:float))
|
34
|
+
elsif physics_shape.kind_of? PhysCircle
|
35
|
+
SlickCircle.new(body.position.x, body.position.y, physics_shape.radius)
|
36
|
+
# elsif physics_shape.kind_of? PhysLine
|
37
|
+
# SlickLine.new(body_position.x, )
|
26
38
|
else
|
27
39
|
raise "#{self.class} does not know how to draw the shape #{physics_shape.class}"
|
28
40
|
end
|
data/src/behaviors/drawable.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'behaviors/drawable'
|
2
2
|
|
3
|
+
#Makes an object draw itself on the screen as a polygon.
|
3
4
|
class DrawableShape < Drawable
|
4
5
|
include_class 'org.newdawn.slick.geom.Vector2f'
|
5
6
|
include_class 'org.newdawn.slick.geom.Polygon'
|
@@ -12,6 +13,9 @@ class DrawableShape < Drawable
|
|
12
13
|
$v = 0.0
|
13
14
|
end
|
14
15
|
|
16
|
+
#Set the shape to draw.
|
17
|
+
#Accepts :Polygon or the name of a class in the DrawableShape namespace.
|
18
|
+
#TODO: There are no DrawableShape::* classes yet!
|
15
19
|
def set_visual_shape(shape, *params)
|
16
20
|
if shape == :polygon || shape == :Polygon
|
17
21
|
@visual_shape = "#{self.class}::#{shape}".constantize.new
|
@@ -25,9 +29,6 @@ class DrawableShape < Drawable
|
|
25
29
|
|
26
30
|
def image=(image)
|
27
31
|
@image = image
|
28
|
-
puts "width: #{@image.width}, height: #{@image.height}"
|
29
|
-
puts "width / screen width: #{@image.width.to_f / @target.game_state.screen_width.to_f}"
|
30
|
-
puts "1/width: #{1.0 / @image.width.to_f}"
|
31
32
|
end
|
32
33
|
alias_method :set_image, :image=
|
33
34
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
#Makes an object leave a trail of images behind it that fade over time.
|
1
2
|
class FadingImageTrailEmittable < Gemini::Behavior
|
2
3
|
depends_on :Updates
|
3
4
|
|
@@ -17,10 +18,13 @@ class FadingImageTrailEmittable < Gemini::Behavior
|
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
21
|
+
#Sets the Image to emit.
|
20
22
|
def emit_fading_image(image)
|
21
23
|
@image = image
|
22
24
|
end
|
23
25
|
|
26
|
+
#Causes fading images to appear at some distance away from object.
|
27
|
+
#Takes a Vector with the x/y offset at which to emit the images.
|
24
28
|
def emit_fading_image_trail_from_offset(offset)
|
25
29
|
@fading_image_offset = offset
|
26
30
|
end
|
@@ -1,7 +1,8 @@
|
|
1
|
-
#
|
1
|
+
#Makes an object attract other Physical game objects towards it.
|
2
2
|
class GravitySource < Gemini::Behavior
|
3
3
|
depends_on :Physical
|
4
4
|
|
5
|
+
#The attractive force to exert. 0 means no pull.
|
5
6
|
attr_accessor :gravity
|
6
7
|
alias_method :set_gravity, :gravity=
|
7
8
|
|
@@ -18,4 +19,4 @@ class GravitySource < Gemini::Behavior
|
|
18
19
|
end
|
19
20
|
end
|
20
21
|
end
|
21
|
-
end
|
22
|
+
end
|
data/src/behaviors/inertial.rb
CHANGED
data/src/behaviors/movable2d.rb
CHANGED
@@ -1,10 +1,16 @@
|
|
1
|
+
#Gives an object multiple animations it can switch between.
|
1
2
|
class MultiAnimatedSprite < Gemini::Behavior
|
2
3
|
depends_on :AnimatedSprite
|
3
4
|
|
4
5
|
def load
|
5
6
|
@animations = {}
|
6
7
|
end
|
7
|
-
|
8
|
+
|
9
|
+
#Add an animation for later use.
|
10
|
+
#Takes a hash with the following keys and values:
|
11
|
+
#[:name] The name to assign the animation.
|
12
|
+
#[:sprites] A list of Image names.
|
13
|
+
#[:speed] The speed at which to animate.
|
8
14
|
def add_animation(options)
|
9
15
|
name = options[:name]
|
10
16
|
sprites = options[:sprites]
|
data/src/behaviors/physical.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'behaviors/spatial'
|
2
2
|
|
3
|
+
#Makes an object interact with the physics engine.
|
3
4
|
class Physical < Gemini::Behavior
|
4
|
-
|
5
|
-
SQUARE_ROOT_OF_TWO = Math.sqrt(2)
|
5
|
+
|
6
6
|
INFINITE_MASS = Java::net::phys2d::raw::Body::INFINITE_MASS
|
7
|
-
|
7
|
+
|
8
8
|
include_class "net.phys2d.raw.Body"
|
9
9
|
include_class "net.phys2d.raw.shapes.Box"
|
10
10
|
include_class "net.phys2d.raw.shapes.Circle"
|
@@ -14,12 +14,13 @@ class Physical < Gemini::Behavior
|
|
14
14
|
include_class "net.phys2d.math.Vector2f"
|
15
15
|
include_class 'net.phys2d.raw.AngleJoint'
|
16
16
|
include_class 'net.phys2d.raw.BasicJoint'
|
17
|
-
|
17
|
+
include_class 'net.phys2d.raw.SpringJoint'
|
18
|
+
|
18
19
|
attr_reader :mass, :name, :shape
|
19
20
|
depends_on :Spatial
|
20
21
|
depends_on :Updates
|
21
|
-
wrap_with_callbacks :mass
|
22
|
-
|
22
|
+
wrap_with_callbacks :mass=, :add_to_world, :body_position=, :set_body_position
|
23
|
+
|
23
24
|
def load
|
24
25
|
@mass = 1
|
25
26
|
@shape = Box.new(1,1)
|
@@ -30,27 +31,32 @@ class Physical < Gemini::Behavior
|
|
30
31
|
@target.enable_listeners_for :physical_collided
|
31
32
|
@target.on_after_move { move @target.x , @target.y}
|
32
33
|
# Manual angular damping. New Phys2D may support this? If not, file a bug.
|
33
|
-
@target.on_update
|
34
|
+
@target.on_update do |delta|
|
35
|
+
next if @angular_damping.zero? || angular_velocity.zero?
|
36
|
+
decay = (delta * @angular_damping * angular_velocity) / (mass * shape.surface_factor)
|
37
|
+
set_angular_velocity(angular_velocity - decay) unless decay.nan?
|
38
|
+
end
|
34
39
|
end
|
35
|
-
|
40
|
+
|
36
41
|
def add_excluded_physical(physical_game_object)
|
37
42
|
@body.add_excluded_body physical_game_object.instance_variable_get(:@__behaviors)[:Physical].instance_variable_get(:@body)
|
38
43
|
end
|
39
|
-
|
44
|
+
|
40
45
|
def physics_bitmask
|
41
46
|
@body.bitmask
|
42
47
|
end
|
43
|
-
|
48
|
+
|
44
49
|
def physics_bitmask=(bitmask)
|
45
50
|
@body.bitmask = bitmask
|
46
51
|
end
|
47
52
|
alias_method :set_physics_bitmask, :physics_bitmask=
|
48
|
-
|
49
|
-
|
53
|
+
|
54
|
+
|
50
55
|
def body_position
|
51
56
|
@body.position
|
52
57
|
end
|
53
|
-
|
58
|
+
|
59
|
+
#Takes a Vector with the x/y coordinates to move the object to.
|
54
60
|
def body_position=(vector)
|
55
61
|
# set_position doesn't take a vector, only x/y
|
56
62
|
@body.set_position(vector.x, vector.y)
|
@@ -58,24 +64,29 @@ class Physical < Gemini::Behavior
|
|
58
64
|
alias_method :set_body_position, :body_position=
|
59
65
|
|
60
66
|
# See about Phys2D joints here: http://www.cokeandcode.com/phys2d/source/javadoc/net/phys2d/raw/Joint.html
|
61
|
-
#
|
62
|
-
# Both physicals might want to own the joint
|
63
|
-
# This seems to need more work
|
67
|
+
# TODO: Make the joint a game object and/or behavior
|
64
68
|
def join_to_physical(physical_game_object, options={})
|
65
69
|
other_body = physical_game_object.instance_variable_get(:@__behaviors)[:Physical].instance_variable_get(:@body)
|
66
|
-
case options[:joint]
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
70
|
+
joint = case options[:joint]
|
71
|
+
when :angle
|
72
|
+
AngleJoint.new(@body, other_body, options[:self_body_point].to_phys2d_vector, options[:other_body_point].to_phys2d_vector, options[:self_body_angle], options[:other_body_angle])
|
73
|
+
when :basic
|
74
|
+
joint = BasicJoint.new(@body, other_body, options[:anchor].to_phys2d_vector)
|
75
|
+
joint.relaxation = options[:relaxation] if options[:relaxation]
|
76
|
+
joint
|
77
|
+
when :spring
|
78
|
+
joint = SpringJoint.new(@body, other_body, options[:self_anchor].to_phys2d_vector, options[:other_anchor].to_phys2d_vector)
|
79
|
+
joint
|
80
|
+
else
|
81
|
+
raise "Joint type #{options[:joint].inspect} not supported."
|
82
|
+
end
|
83
|
+
@world.add joint
|
74
84
|
end
|
75
85
|
|
86
|
+
#The maximum speed the object is allowed to travel. Takes either a Vector with the x/y limits or the numeric value to assign to both x and y.
|
76
87
|
def speed_limit=(vector_or_shared_value)
|
77
88
|
if vector_or_shared_value.kind_of? Numeric
|
78
|
-
axis_limit_x = axis_limit_y = vector_or_shared_value / SQUARE_ROOT_OF_TWO
|
89
|
+
axis_limit_x = axis_limit_y = vector_or_shared_value / Gemini::Math::SQUARE_ROOT_OF_TWO
|
79
90
|
else
|
80
91
|
axis_limit_x = vector_or_shared_value.x
|
81
92
|
axis_limit_y = vector_or_shared_value.y
|
@@ -83,7 +94,7 @@ class Physical < Gemini::Behavior
|
|
83
94
|
@body.set_max_velocity(axis_limit_x, axis_limit_y)
|
84
95
|
end
|
85
96
|
alias_method :set_speed_limit, :speed_limit=
|
86
|
-
|
97
|
+
|
87
98
|
# def safe_move=(safe_move)
|
88
99
|
# @safe_move = safe_move
|
89
100
|
# if safe_move
|
@@ -101,7 +112,9 @@ class Physical < Gemini::Behavior
|
|
101
112
|
# end
|
102
113
|
# end
|
103
114
|
# alias_method :set_safe_move, :safe_move=
|
104
|
-
|
115
|
+
|
116
|
+
#Attempt to move the object to the given coordinates.
|
117
|
+
#Note that the object might be blocked if its path intersects another physical object.
|
105
118
|
def wish_move(x, y)
|
106
119
|
# WARNING: @body.last_position is not to be trusted. We'll just handle it ourselves.
|
107
120
|
@last_x = @target.x
|
@@ -109,45 +122,50 @@ class Physical < Gemini::Behavior
|
|
109
122
|
@body.move(x, y)
|
110
123
|
#@body.set_position(@last_x, @last_y) if @target.game_state.manager(:physics).colliding? @body
|
111
124
|
end
|
112
|
-
|
125
|
+
|
113
126
|
def width
|
114
127
|
@body.shape.bounds.width
|
115
128
|
end
|
116
|
-
|
129
|
+
|
117
130
|
def height
|
118
131
|
@body.shape.bounds.height
|
119
132
|
end
|
120
|
-
|
133
|
+
|
121
134
|
def radius
|
122
135
|
@body.shape.radius
|
123
136
|
end
|
124
|
-
|
137
|
+
|
125
138
|
def box_size
|
126
139
|
@body.shape.size
|
127
140
|
end
|
128
|
-
|
129
|
-
|
130
|
-
|
141
|
+
|
142
|
+
#Set the absolute rotation.
|
143
|
+
def physical_rotation=(degrees)
|
144
|
+
@body.rotation = Gemini::Math.degrees_to_radians(degrees)
|
131
145
|
end
|
132
146
|
alias_method :set_physical_rotation, :physical_rotation=
|
133
|
-
|
147
|
+
|
148
|
+
#Set the rotation relative to the current rotation.
|
134
149
|
def rotate_physical(degrees)
|
135
150
|
@body.adjust_rotation Gemini::Math.degrees_to_radians(degrees)
|
136
151
|
end
|
137
|
-
|
152
|
+
|
138
153
|
def physical_rotation
|
139
154
|
Gemini::Math.radians_to_degrees(@body.rotation)
|
140
155
|
end
|
141
|
-
|
156
|
+
|
142
157
|
def physical_rotatable?
|
143
158
|
@body.rotatable?
|
144
159
|
end
|
145
|
-
|
160
|
+
|
161
|
+
#Set whether the object is allowed to rotate.
|
146
162
|
def physical_rotatable=(rotatable)
|
147
163
|
@body.rotatable = rotatable
|
148
164
|
end
|
149
165
|
alias_method :set_physical_rotatable, :physical_rotatable=
|
150
|
-
|
166
|
+
|
167
|
+
#Push on the object.
|
168
|
+
#Takes either a Vector or x/y values representing the force to apply.
|
151
169
|
def add_force(x_or_vector, y = nil)
|
152
170
|
if y.nil?
|
153
171
|
@body.add_force(x_or_vector.to_phys2d_vector)
|
@@ -155,15 +173,18 @@ class Physical < Gemini::Behavior
|
|
155
173
|
@body.add_force(Vector2f.new(x_or_vector, y))
|
156
174
|
end
|
157
175
|
end
|
158
|
-
|
176
|
+
|
177
|
+
#Set the force being applied to the object. Disregards all other forces.
|
159
178
|
def set_force(x, y)
|
160
179
|
@body.set_force(x, y)
|
161
180
|
end
|
162
|
-
|
181
|
+
|
163
182
|
def force
|
164
183
|
@body.force
|
165
184
|
end
|
166
|
-
|
185
|
+
|
186
|
+
#Adjust the object's velocity.
|
187
|
+
#Takes either a Vector or x/y values representing the adjustment to make.
|
167
188
|
def add_velocity(x_or_vector, y = nil)
|
168
189
|
if x_or_vector.kind_of? Vector
|
169
190
|
@body.adjust_velocity(x_or_vector.to_phys2d_vector)
|
@@ -171,25 +192,30 @@ class Physical < Gemini::Behavior
|
|
171
192
|
@body.adjust_velocity(Java::net::phys2d::math::Vector2f.new(x_or_vector, y))
|
172
193
|
end
|
173
194
|
end
|
174
|
-
|
195
|
+
|
175
196
|
def velocity
|
176
197
|
@body.velocity.to_vector
|
177
198
|
end
|
178
|
-
|
199
|
+
|
179
200
|
def velocity=(vector)
|
180
201
|
@body.adjust_velocity(Java::net::phys2d::math::Vector2f.new(vector.x - @body.velocity.x, vector.y - @body.velocity.y))
|
181
202
|
end
|
182
203
|
alias_method :set_velocity, :velocity=
|
183
|
-
|
204
|
+
|
184
205
|
def angular_velocity
|
185
206
|
@body.angular_velocity
|
186
207
|
end
|
187
|
-
|
208
|
+
|
188
209
|
def angular_velocity=(delta)
|
189
210
|
@body.adjust_angular_velocity(delta - angular_velocity)
|
190
211
|
end
|
191
212
|
alias_method :set_angular_velocity, :angular_velocity=
|
192
|
-
|
213
|
+
|
214
|
+
def apply_angular_velocity(velocity)
|
215
|
+
@body.adjust_angular_velocity velocity
|
216
|
+
end
|
217
|
+
|
218
|
+
#Immediately halt movement.
|
193
219
|
def come_to_rest
|
194
220
|
current_velocity = @body.velocity
|
195
221
|
add_velocity(-current_velocity.x, -current_velocity.y)
|
@@ -197,7 +223,9 @@ class Physical < Gemini::Behavior
|
|
197
223
|
@body.is_resting = true
|
198
224
|
end
|
199
225
|
|
200
|
-
#
|
226
|
+
#Takes the following values:
|
227
|
+
#[:infinite] Object has infinite mass.
|
228
|
+
#[number] Value to set the mass to.
|
201
229
|
def mass=(mass)
|
202
230
|
@mass = mass
|
203
231
|
x, y = @target.x, @target.y
|
@@ -208,21 +236,28 @@ class Physical < Gemini::Behavior
|
|
208
236
|
@body.move x, y
|
209
237
|
end
|
210
238
|
alias_method :set_mass, :mass=
|
211
|
-
|
239
|
+
|
212
240
|
def restitution
|
213
241
|
@body.restitution
|
214
242
|
end
|
215
|
-
|
243
|
+
|
216
244
|
def restitution=(restitution)
|
217
245
|
@body.restitution = restitution
|
218
246
|
end
|
219
247
|
alias_method :set_restitution, :restitution=
|
220
|
-
|
248
|
+
|
249
|
+
#Set the shape of the object as seen by the physics engine.
|
250
|
+
#call-seq:
|
251
|
+
#set_shape(:Box, width, height)
|
252
|
+
#set_shape(:Circle, radius)
|
253
|
+
#set_shape(:Polygon, *vectors)
|
254
|
+
#
|
221
255
|
def set_shape(shape, *params)
|
222
256
|
# Save off data that is destroyed when @body.set is called
|
223
257
|
saved_damping = damping
|
224
258
|
saved_angular_damping = angular_damping
|
225
259
|
saved_body_position = body_position
|
260
|
+
saved_friction = friction
|
226
261
|
saved_position = @target.position
|
227
262
|
saved_x, saved_y = @target.x, @target.y
|
228
263
|
if shape.respond_to?(:to_str) || shape.kind_of?(Symbol)
|
@@ -238,91 +273,102 @@ class Physical < Gemini::Behavior
|
|
238
273
|
@body.set(@shape, @mass)
|
239
274
|
set_body_position saved_body_position
|
240
275
|
@target.set_position saved_position
|
276
|
+
set_friction saved_friction
|
241
277
|
@target.move(saved_x, saved_y)
|
242
278
|
self.damping = saved_damping
|
243
|
-
self.angular_damping = saved_angular_damping
|
279
|
+
self.angular_damping = saved_angular_damping
|
244
280
|
end
|
245
|
-
|
281
|
+
|
246
282
|
def name=(name)
|
247
283
|
@name = name
|
248
284
|
setup_body
|
249
285
|
end
|
250
|
-
|
286
|
+
|
287
|
+
#Place this object under the control of the physics engine.
|
251
288
|
def add_to_world(world)
|
252
289
|
world.add @body
|
253
290
|
@world = world
|
254
291
|
end
|
255
|
-
|
292
|
+
|
293
|
+
#Remove this object from the control of the physics engine.
|
256
294
|
def remove_from_world(world)
|
257
295
|
world.remove @body
|
258
296
|
@world = nil
|
259
297
|
end
|
260
|
-
|
261
|
-
|
262
|
-
|
298
|
+
|
299
|
+
#Turn debug mode on or off for this object.
|
300
|
+
def physical_debug_mode=(flag)
|
301
|
+
if flag
|
263
302
|
@target.add_behavior :DebugPhysical
|
264
303
|
else
|
265
304
|
@target.remove_behavior :DebugPhysical
|
266
305
|
end
|
267
306
|
end
|
268
307
|
alias_method :set_physical_debug_mode, :physical_debug_mode=
|
269
|
-
|
270
|
-
def movable=(
|
271
|
-
@body.moveable =
|
308
|
+
|
309
|
+
def movable=(flag)
|
310
|
+
@body.moveable = flag
|
272
311
|
end
|
273
312
|
alias_method :set_movable, :movable=
|
274
|
-
|
313
|
+
|
275
314
|
def movable?
|
276
315
|
@body.moveable?
|
277
316
|
end
|
278
|
-
|
317
|
+
|
318
|
+
#The amount of air friction slowing the object's movement.
|
279
319
|
def damping
|
280
320
|
@body.damping
|
281
321
|
end
|
282
|
-
|
322
|
+
|
283
323
|
def damping=(damping)
|
284
324
|
@body.damping = damping
|
285
325
|
end
|
286
326
|
alias_method :set_damping, :damping=
|
287
|
-
|
327
|
+
|
288
328
|
def angular_damping
|
289
329
|
@angular_damping
|
330
|
+
#@body.rot_damping
|
290
331
|
end
|
291
|
-
|
332
|
+
|
292
333
|
def angular_damping=(damping)
|
293
334
|
@angular_damping = damping
|
335
|
+
#@body.rot_damping = damping
|
294
336
|
end
|
295
|
-
alias_method :set_angular_damping, :
|
296
|
-
|
337
|
+
alias_method :set_angular_damping, :angular_damping=
|
338
|
+
|
339
|
+
#Set this object as immobile.
|
297
340
|
def set_static_body
|
298
341
|
@body.moveable = false
|
299
342
|
@body.rotatable = false
|
300
343
|
@body.is_resting = true
|
301
344
|
end
|
302
|
-
|
303
|
-
|
304
|
-
|
345
|
+
|
346
|
+
#Set whether gravity affects this object.
|
347
|
+
def gravity_effected=(flag)
|
348
|
+
@body.gravity_effected = flag
|
305
349
|
end
|
306
350
|
alias_method :set_gravity_effected, :gravity_effected=
|
307
|
-
|
351
|
+
|
352
|
+
#The amount of friction slowing the object's movement.
|
308
353
|
def friction
|
309
354
|
@body.friction
|
310
355
|
end
|
311
|
-
|
356
|
+
|
312
357
|
def friction=(friction)
|
313
358
|
@body.friction = friction
|
314
359
|
end
|
315
360
|
alias_method :set_friction, :friction=
|
316
|
-
|
361
|
+
|
317
362
|
# def get_colliding_game_objects(tangible_game_object)
|
318
363
|
# # TODO: Tangibles only?
|
319
364
|
# tangible_game_object
|
320
365
|
# end
|
321
|
-
|
366
|
+
|
367
|
+
#Get a list of CollisionEvents for objects currently colliding with this one.
|
322
368
|
def get_collision_events
|
323
369
|
@world.get_contacts(@body)
|
324
370
|
end
|
325
|
-
|
371
|
+
|
326
372
|
private
|
327
373
|
def setup_body
|
328
374
|
if @name
|
@@ -330,7 +376,7 @@ private
|
|
330
376
|
else
|
331
377
|
@body = Body.new(@shape, @mass)
|
332
378
|
end
|
333
|
-
|
379
|
+
|
334
380
|
x = @body.position.x
|
335
381
|
y = @body.position.y
|
336
382
|
|