gemini 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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