gemini 1.0.0 → 1.0.1
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 +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
|
|