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.
Files changed (52) hide show
  1. data/bin/gemini +20 -12
  2. data/package/jar/gemini.jar +0 -0
  3. data/src/base_state.rb +6 -12
  4. data/src/behaviors/animated_sprite.rb +6 -2
  5. data/src/behaviors/audible.rb +6 -1
  6. data/src/behaviors/axis_stateful.rb +2 -0
  7. data/src/behaviors/big_sprite.rb +2 -1
  8. data/src/behaviors/bounding_box_collidable.rb +2 -0
  9. data/src/behaviors/camera_anchored_drawable.rb +1 -1
  10. data/src/behaviors/cardinal_movable.rb +18 -11
  11. data/src/behaviors/clickable.rb +1 -0
  12. data/src/behaviors/countable.rb +1 -0
  13. data/src/behaviors/debug_physical.rb +25 -13
  14. data/src/behaviors/debug_tangible.rb +1 -0
  15. data/src/behaviors/drawable.rb +1 -0
  16. data/src/behaviors/drawable_shape.rb +4 -3
  17. data/src/behaviors/fading_image_trail_emittable.rb +4 -0
  18. data/src/behaviors/game_object_emittable.rb +1 -0
  19. data/src/behaviors/gravity_source.rb +3 -2
  20. data/src/behaviors/inertial.rb +3 -0
  21. data/src/behaviors/movable2d.rb +1 -0
  22. data/src/behaviors/multi_animated_sprite.rb +7 -1
  23. data/src/behaviors/physical.rb +123 -77
  24. data/src/behaviors/physical_cardinal_movable.rb +14 -13
  25. data/src/behaviors/physical_sprite.rb +25 -9
  26. data/src/behaviors/platformer_controllable.rb +9 -2
  27. data/src/behaviors/pointer.rb +2 -0
  28. data/src/behaviors/pressable.rb +4 -0
  29. data/src/behaviors/receives_events.rb +3 -0
  30. data/src/behaviors/regional.rb +5 -0
  31. data/src/behaviors/repulsive.rb +28 -0
  32. data/src/behaviors/rotates_to_point.rb +2 -1
  33. data/src/behaviors/spatial.rb +4 -4
  34. data/src/behaviors/sprite.rb +17 -6
  35. data/src/behaviors/stateful.rb +2 -0
  36. data/src/behaviors/taggable.rb +1 -0
  37. data/src/behaviors/tangible.rb +8 -0
  38. data/src/behaviors/timeable.rb +9 -0
  39. data/src/behaviors/top_down_vehicle.rb +1 -0
  40. data/src/behaviors/triangle_trail_emittable.rb +5 -0
  41. data/src/behaviors/updates.rb +1 -0
  42. data/src/behaviors/updates_at_consistant_rate.rb +1 -0
  43. data/src/behaviors/vectored_movement.rb +1 -0
  44. data/src/behaviors/world_collidable.rb +1 -0
  45. data/src/game_objects/static_sprite.rb +3 -2
  46. data/src/game_objects/triangle_trail.rb +1 -1
  47. data/src/gemini_version.rb +1 -1
  48. data/src/managers/input_support/input_mapping.rb +10 -10
  49. data/src/managers/scrolling_render_manager.rb +8 -6
  50. data/src/project_generator.rb +6 -0
  51. data/src/vector.rb +19 -7
  52. 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
- rawr_install_args = ''
6
- project_dir = nil
7
- until ARGV.empty?
8
- arg = ARGV.shift
9
- case arg
10
- when /-RI(.+)/
11
- rawr_install_args << $1 + ' '
12
- else
13
- project_dir = arg
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
Binary file
@@ -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 = begin
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
- rescue NameError
44
- begin
45
- require type.underscore
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
@@ -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)
@@ -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
- # These are sprites that deliberately ignore the scrolling of the render manager.
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
- # movement constrained
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
- DIAGONOL_DIRECTIONS = [NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST]
18
- CARDINAL_DIRECTIONS = [NORTH, EAST, SOUTH, WEST] + DIAGONOL_DIRECTIONS
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
- diagonol_direction = begin
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 = diagonol_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 diagonol_direction? @facing_direction
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 diagonol_direction?(direction)
103
- DIAGONOL_DIRECTIONS.include? direction
109
+ def diagonal_direction?(direction)
110
+ DIAGONAL_DIRECTIONS.include? direction
104
111
  end
105
112
 
106
- def other_direction(diagonol_direction, direction)
107
- diagonol_direction.to_s.sub(direction.to_s, '').sub('_', '').to_sym
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)
@@ -1,3 +1,4 @@
1
+ #Makes an object clickable with the mouse.
1
2
  class Clickable < Gemini::Behavior
2
3
  depends_on :ReceivesEvents
3
4
  depends_on :Regional
@@ -1,3 +1,4 @@
1
+ #Makes an object receive events when its numbers have changed.
1
2
  class Countable < Gemini::Behavior
2
3
  attr_accessor :count
3
4
  wrap_with_callbacks :count=
@@ -1,8 +1,17 @@
1
- class DebugPhysical < Gemini::Behavior #Drawable
2
- #declared_methods :draw
3
- PhysVector = Java::net::phys2d::math::Vector2f
4
- SlickVector = Java::org::newdawn::slick::geom::Vector2f
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?(Java::net::phys2d::raw::shapes::Box)
21
- Java::org.newdawn.slick.geom.Polygon.new(physics_shape.get_points(body.position, body.rotation).map{|point| [point.x, point.y]}.flatten.to_java(:float))
22
- elsif physics_shape.kind_of?(Java::net.phys2d.raw.shapes.Polygon)
23
- Java::org.newdawn.slick.geom.Polygon.new(physics_shape.get_vertices(body.position, body.rotation).map{|point| [point.x, point.y]}.flatten.to_java(:float))
24
- elsif physics_shape.kind_of? Java::net.phys2d.raw.shapes.Circle
25
- Java::org.newdawn.slick.geom.Circle.new(body.position.x, body.position.y, physics_shape.radius)
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
@@ -1,3 +1,4 @@
1
+ #Makes an object draw its collisions on the screen.
1
2
  class DebugTangible < Gemini::Behavior #Drawable
2
3
  #declared_methods :draw
3
4
  PhysVector = Java::net::phys2d::math::Vector2f
@@ -1,3 +1,4 @@
1
+ #Makes an object draw itself to the screen.
1
2
  class Drawable < Gemini::Behavior
2
3
  depends_on_kind_of :Spatial
3
4
  wrap_with_callbacks :draw
@@ -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,3 +1,4 @@
1
+ #Makes an object create other objects.
1
2
  class GameObjectEmittable < Gemini::Behavior
2
3
  attr_accessor :emitting_game_object_name
3
4
  alias_method :set_emitting_game_object_name, :emitting_game_object_name=
@@ -1,7 +1,8 @@
1
- # Enables game objects to attract other Physical game objects towards it
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
@@ -1,5 +1,8 @@
1
+ #Makes an object resist changes in velocity.
1
2
  class Inertial < Gemini::Behavior
2
3
  depends_on :UpdatesAtConsistantRate
4
+
5
+ #A 2-element array with x/y inertial values. 0 means no resistance to acceleration.
3
6
  attr_accessor :inertia
4
7
 
5
8
  def load
@@ -1,3 +1,4 @@
1
+ #Makes an object move within a 2D plane.
1
2
  class Movable2d < Gemini::Behavior
2
3
  depends_on :Spatial2d
3
4
  wrap_with_callbacks :move
@@ -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]
@@ -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
- # TODO: Move to Math?
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 {|delta| set_angular_velocity(angular_velocity - (angular_velocity * (@angular_damping * (1.0/mass) ))) }
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
- # what do we do with the joint? Just let it die when the objects die? Makes sense to me.
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
- when :angle
68
- 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])
69
- when :basic
70
- BasicJoint.new(@body, other_body, options[:anchor].to_phys2d_vector)
71
- else
72
- raise "Joint type #{options[:joint].inspect} not supported."
73
- end
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
- def physical_rotation=(rotation)
130
- @body.rotation = Gemini::Math.degrees_to_radians(rotation)
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
- # numbers or :infinite
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
- def physical_debug_mode=(mode)
262
- if mode
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=(movable)
271
- @body.moveable = movable
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, :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
- def gravity_effected=(effected)
304
- @body.gravity_effected = effected
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