chingu 0.7.0 → 0.7.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/History.txt +1 -1
  2. data/README.rdoc +110 -49
  3. data/benchmarks/game_objects_benchmark.rb +50 -0
  4. data/chingu.gemspec +24 -12
  5. data/examples/example10_traits_retrofy.rb +6 -4
  6. data/examples/example11_animation.rb +14 -23
  7. data/examples/example12_trait_timer.rb +1 -1
  8. data/examples/example13_high_scores.rb +1 -1
  9. data/examples/example14_bounding_box_circle.rb +6 -10
  10. data/examples/example15_trait_timer2.rb +1 -1
  11. data/examples/example16_online_high_scores.rb +1 -1
  12. data/examples/example17_gosu_tutorial.rb +4 -4
  13. data/examples/example18_animation_trait.rb +98 -0
  14. data/examples/example19_edit_viewport.rb +223 -0
  15. data/examples/example19_game_objects.yml +1591 -0
  16. data/examples/example20_trait_inheritence_test.rb +58 -0
  17. data/examples/example3_parallax.rb +1 -1
  18. data/examples/example4_gamestates.rb +1 -1
  19. data/examples/example7_gfx_helpers.rb +14 -9
  20. data/examples/example8_traits.rb +4 -4
  21. data/examples/example9_collision_detection.rb +1 -1
  22. data/examples/game1.rb +33 -38
  23. data/examples/game_of_life.rb +291 -0
  24. data/examples/media/droid_11x15.bmp +0 -0
  25. data/examples/media/droid_11x15.gal +0 -0
  26. data/examples/media/heli.bmp +0 -0
  27. data/examples/media/heli.gal +0 -0
  28. data/examples/media/star_25x25_default.png +0 -0
  29. data/examples/media/star_25x25_explode.gal +0 -0
  30. data/examples/media/star_25x25_explode.png +0 -0
  31. data/examples/media/stone_wall.bmp +0 -0
  32. data/lib/chingu.rb +1 -1
  33. data/lib/chingu/animation.rb +78 -9
  34. data/lib/chingu/basic_game_object.rb +16 -8
  35. data/lib/chingu/game_object.rb +36 -7
  36. data/lib/chingu/game_object_list.rb +20 -3
  37. data/lib/chingu/game_state.rb +8 -7
  38. data/lib/chingu/game_states/edit.rb +177 -90
  39. data/lib/chingu/helpers/class_inheritable_accessor.rb +12 -5
  40. data/lib/chingu/helpers/game_object.rb +45 -4
  41. data/lib/chingu/helpers/gfx.rb +150 -172
  42. data/lib/chingu/helpers/input_client.rb +7 -0
  43. data/lib/chingu/inflector.rb +16 -2
  44. data/lib/chingu/traits/animation.rb +84 -0
  45. data/lib/chingu/traits/bounding_box.rb +16 -3
  46. data/lib/chingu/traits/bounding_circle.rb +18 -4
  47. data/lib/chingu/traits/collision_detection.rb +10 -1
  48. data/lib/chingu/traits/velocity.rb +26 -3
  49. data/lib/chingu/traits/viewport.rb +10 -9
  50. data/lib/chingu/viewport.rb +103 -22
  51. data/lib/chingu/window.rb +8 -2
  52. metadata +46 -16
  53. data/examples/example18_viewport.rb +0 -173
  54. data/examples/media/city1.csv +0 -2
  55. data/examples/media/plane.csv +0 -2
  56. data/examples/media/saucer.csv +0 -4
  57. data/examples/media/stickfigure.bmp +0 -0
  58. data/examples/media/stickfigure.png +0 -0
@@ -0,0 +1,84 @@
1
+ #--
2
+ #
3
+ # Chingu -- OpenGL accelerated 2D game framework for Ruby
4
+ # Copyright (C) 2009 ippa / ippa@rubylicio.us
5
+ #
6
+ # This library is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU Lesser General Public
8
+ # License as published by the Free Software Foundation; either
9
+ # version 2.1 of the License, or (at your option) any later version.
10
+ #
11
+ # This library is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
+ # Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public
17
+ # License along with this library; if not, write to the Free Software
18
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
+ #
20
+ #++
21
+
22
+ module Chingu
23
+ module Traits
24
+ #
25
+ # A chingu trait providing automatic loading of tile-animations
26
+ #
27
+ # For example:
28
+ # class FireBall; has_traits :animation; end;
29
+ #
30
+ # Will automatically load:
31
+ # - fire_ball.png into self.animations[:default]
32
+ # - fire_ball_exploding.png into self.animations[:exploding]
33
+ #
34
+ # Adds accessors animations -> Hash with all animations, hash-key is the name of the animation
35
+ #
36
+ module Animation
37
+
38
+ module ClassMethods
39
+
40
+ def initialize_trait(options = {})
41
+ trait_options[:animation] = {:directory => "media", :play => true, :delay => 100}.merge(options)
42
+ end
43
+
44
+ end
45
+
46
+ def setup_trait(options)
47
+ @animation_options = {:debug => false}.merge(options)
48
+ @animations = load_animations
49
+ super
50
+ end
51
+
52
+ #
53
+ # Try loading animation from class-name
54
+ #
55
+ def load_animations
56
+ animations = {}
57
+ glob = "#{trait_options[:animation][:directory]}/#{self.filename}_*"
58
+ puts "Animations? #{glob}" if trait_options[:animation][:debug]
59
+ Dir[glob].each do |tile_file|
60
+ #state = :default
61
+ if tile_file =~ /[a-zA-Z\_+]_*(\d+)x(\d+)_*([a-zA-Z]*)\.(bmp|png)/
62
+ #if tile_file =~ /_*([a-zA-Z]*)\.(bmp|png)\Z/
63
+ #if tile_file =~ /#{self.filename}\.(bmp|png)/
64
+ state = $3.length > 0 ? $3 : "default"
65
+ animations[state.to_sym] = Chingu::Animation.new(trait_options[:animation].merge(:file => tile_file))
66
+ end
67
+ end
68
+ return animations
69
+ end
70
+
71
+ def animation
72
+ @animations[:default]
73
+ end
74
+
75
+ #
76
+ # Returns all animations, then access invidual states with animations[:explode] etc.
77
+ #
78
+ def animations
79
+ @animations
80
+ end
81
+
82
+ end
83
+ end
84
+ end
@@ -38,6 +38,10 @@ module Chingu
38
38
  super
39
39
  end
40
40
 
41
+ def collision_at?(x, y)
42
+ bounding_box.collide_point?(x,y)
43
+ end
44
+
41
45
  #
42
46
  # Returns an instance of class Rect
43
47
  #
@@ -77,12 +81,21 @@ module Chingu
77
81
  #end
78
82
 
79
83
  def draw_trait
80
- if trait_options[:bounding_box][:debug]
81
- $window.draw_rect(self.bounding_box, Chingu::DEBUG_COLOR, Chingu::DEBUG_ZORDER)
82
- end
84
+ draw_debug if trait_options[:bounding_box][:debug]
83
85
  super
84
86
  end
85
87
 
88
+ #
89
+ # Visualises the bounding box as a red rectangle.
90
+ #
91
+ def draw_debug
92
+ if defined?(parent.viewport)
93
+ $window.draw_rect(self.bounding_box.move(-parent.viewport.x, -parent.viewport.y), Chingu::DEBUG_COLOR, Chingu::DEBUG_ZORDER)
94
+ else
95
+ $window.draw_rect(self.bounding_box, Chingu::DEBUG_COLOR, Chingu::DEBUG_ZORDER)
96
+ end
97
+ end
98
+
86
99
  end
87
100
  end
88
101
  end
@@ -40,6 +40,10 @@ module Chingu
40
40
  super
41
41
  end
42
42
 
43
+ def collision_at?(x, y)
44
+ Gosu.distance(self.x, self.y, x, y) < radius
45
+ end
46
+
43
47
  def radius
44
48
  return @cached_radius if @cached_radius
45
49
 
@@ -70,12 +74,22 @@ module Chingu
70
74
  def circle_bottom; self.y + self.radius; end
71
75
 
72
76
  def draw_trait
73
- if trait_options[:bounding_circle][:debug]
74
- $window.draw_circle(self.x, self.y, self.radius, Chingu::DEBUG_COLOR)
75
- end
77
+ draw_debug if trait_options[:bounding_circle][:debug]
76
78
  super
77
79
  end
78
-
80
+
81
+ #
82
+ # Visualises the bounding circle as a red circle.
83
+ #
84
+ def draw_debug
85
+ x,y = self.x, self.y # Do we need to take center_x/center_y into consideration here?
86
+ if defined?(parent.viewport)
87
+ $window.draw_circle(x - parent.viewport.x, y - parent.viewport.y, self.radius, Chingu::DEBUG_COLOR)
88
+ else
89
+ $window.draw_circle(x, y, self.radius, Chingu::DEBUG_COLOR)
90
+ end
91
+ end
92
+
79
93
  end
80
94
  end
81
95
  end
@@ -106,7 +106,16 @@ module Chingu
106
106
  end
107
107
  end
108
108
  end
109
-
109
+
110
+ def first_collision(*klasses)
111
+ Array(klasses).each do |klass|
112
+ klass.all.each do |object|
113
+ return object if collides?(object)
114
+ end
115
+ end
116
+ return nil
117
+ end
118
+
110
119
  #
111
120
  # Explicit radius-collision
112
121
  # Works like each_collsion but with inline-code for speedups
@@ -38,20 +38,42 @@ module Chingu
38
38
  end
39
39
 
40
40
  def setup_trait(options)
41
- @velocity_options = {:debug => false}.merge(options)
41
+ @velocity_options = {:debug => false}.merge(options)
42
42
 
43
43
  @velocity_x = options[:velocity_x] || 0
44
44
  @velocity_y = options[:velocity_y] || 0
45
+ self.velocity = options[:velocity] if options[:velocity]
46
+
45
47
  @acceleration_x = options[:acceleration_x] || 0
46
48
  @acceleration_y = options[:acceleration_y] || 0
49
+ self.acceleration = options[:acceleration] if options[:acceleration]
50
+
47
51
  @max_velocity = options[:max_velocity] || 1000
48
52
  super
49
53
  end
50
54
 
55
+ #
56
+ # Sets X and Y velocity with one single call. Takes an Array-argument with 2 values.
57
+ #
58
+ def velocity=(velocity)
59
+ @velocity_x, @velocity_y = velocity
60
+ end
61
+
62
+ def velocity; [@velocity_x, @velocity_y]; end
63
+
64
+ #
65
+ # Sets X and Y acceleration with one single call. Takes an Array-argument with 2 values.
66
+ #
67
+ def acceleration=(acceleration)
68
+ @acceleration_x, @acceleration_y = acceleration
69
+ end
70
+
71
+ def acceleration; [@acceleration_x, @acceleration_y]; end
72
+
51
73
  #
52
74
  # Modifies X & Y of parent
53
75
  #
54
- def update_trait
76
+ def update_trait
55
77
  @velocity_y += @acceleration_y if (@velocity_y + @acceleration_y).abs < @max_velocity
56
78
  @velocity_x += @acceleration_x if (@velocity_x + @acceleration_x).abs < @max_velocity
57
79
 
@@ -61,10 +83,11 @@ module Chingu
61
83
  #
62
84
  # if option :apply is false, just calculate velocities, don't apply them to x/y
63
85
  #
64
- if trait_options[:velocity][:apply]
86
+ unless trait_options[:velocity][:apply] == false
65
87
  self.y += @velocity_y
66
88
  self.x += @velocity_x
67
89
  end
90
+
68
91
  super
69
92
  end
70
93
 
@@ -45,24 +45,25 @@ module Chingu
45
45
 
46
46
  super
47
47
  end
48
-
49
- #
50
- # Returns true if object is inside view port, false if outside
51
- # TODO: add view port height and width! (and use clip_to when painting?)
52
- #
53
- # This is a very flawed implementation, it Should take inte account objects
54
- # height,width,factor_x,factor_y,center_x,center_y as well...
55
- #
48
+
56
49
  def inside_viewport?(object)
57
- object.x >= @viewport.x && object.x <= (@viewport.x + $window.width) &&
50
+ puts "Deprecated, use self.viewport.inside?() instead"
51
+ object.x >= @viewport.x && object.x <= (@viewport.x + $window.width) &&
58
52
  object.y >= @viewport.y && object.y <= (@viewport.y + $window.height)
59
53
  end
60
54
 
61
55
  # Returns true object is outside the view port
62
56
  def outside_viewport?(object)
57
+ puts "Deprecated, use self.viewport.outside?() instead"
63
58
  not inside_viewport?(object)
64
59
  end
65
60
 
61
+ # Take care of laggy viewport movements
62
+ def update_trait
63
+ @viewport.move_towards_target
64
+ super
65
+ end
66
+
66
67
  #
67
68
  # Override game states default draw that draws objects relative to the viewport.
68
69
  # It only draws game objects inside the viewport. (GOSU does no such optimizations)
@@ -21,47 +21,128 @@
21
21
 
22
22
  module Chingu
23
23
  #
24
- # A basic Viewport class
24
+ # A basic viewport class
25
25
  #
26
26
  # TODO:
27
27
  # Implement use of viewports angle, center_x, center_y, factor_x, factor_y
28
28
  #
29
29
  class Viewport
30
- attr_accessor :x, :y, :x_min, :x_max, :y_min, :y_max
31
- #attr_accessor :angle, :center_x, :center_y, :factor_x, :factor_y
30
+ attr_accessor :x, :y, :x_target, :y_target, :x_lag, :y_lag, :game_area
32
31
 
33
32
  def initialize(options = {})
34
33
  @x = options[:x] || 0
35
- @y = options[:y] || 0
36
- @angle = options[:angle] || 0
37
-
38
- #self.factor = options[:factor] || options[:scale] || 1.0
39
- #@factor_x = options[:factor_x] if options[:factor_x]
40
- #@factor_y = options[:factor_y] if options[:factor_y]
34
+ @y = options[:y] || 0
35
+ @x_target = options[:x_target]
36
+ @y_target = options[:y_target]
37
+ @x_lag = options[:x_lag] || 0
38
+ @y_lag = options[:y_lag] || 0
39
+ @game_area = Chingu::Rect.new(options[:game_area]||[@x, @y, $window.width, $window.height])
40
+ end
41
+
42
+ #
43
+ # Set x_lag and y_lag to value 'lag'
44
+ #
45
+ def lag=(lag)
46
+ @x_lag = @y_lag = lag
47
+ end
48
+
49
+ #
50
+ # Center the viewport around the given object (it must respont to x/y)
51
+ # Center will fail if object is in the corners of the game area
52
+ #
53
+ def center_around(object)
54
+ self.x = object.x - $window.width / 2
55
+ self.y = object.y - $window.height / 2
56
+ end
57
+
58
+ #
59
+ # Set a Rect that represents the borders of the game world.
60
+ # The viewport can only move within this Rect.
61
+ #
62
+ def game_area=(rect)
63
+ @game_area = Rect.new(rect)
64
+ end
65
+
66
+ #
67
+ # Set a game world by giving it a game object
68
+ # The game objects image will be the rectangle the viewport can move within.
69
+ #
70
+ def game_area_object=(game_object)
71
+ image = (game_object.is_a? Gosu::Image) ? game_object : game_object.image
72
+ @game_area = Rect.new(0,0,
73
+ (image.width*$window.factor) - $window.width,
74
+ (image.height*$window.factor) - $window.height
75
+ )
76
+ end
77
+
78
+ #
79
+ # Returns true if object is inside view port, false if outside
80
+ # TODO: add view port height and width! (and use clip_to when painting?)
81
+ #
82
+ # This is a very flawed implementation, it Should take inte account objects
83
+ # height,width,factor_x,factor_y,center_x,center_y as well...
84
+ #
85
+ def inside?(object)
86
+ object.x >= @x && object.x <= (@x + $window.width) &&
87
+ object.y >= @y && object.y <= (@y + $window.height)
88
+ end
89
+
90
+ # Returns true object is outside the view port
91
+ def outside?(object)
92
+ not inside_viewport?(object)
93
+ end
41
94
 
42
- #self.center = options[:center] || 0.5
43
- #@rotation_center = options[:rotation_center]
44
- #self.rotation_center(options[:rotation_center]) if options[:rotation_center]
95
+ #
96
+ # Returns true object is inside the game area
97
+ # The "game area" is the full map/world/area from which the viewport shows a slice
98
+ # The viewport can't show anything outside the game area
99
+ #
100
+ def inside_game_area?(object)
101
+ object.x >= @game_area.x && object.x <= (@game_area.width + $window.width) &&
102
+ object.y >= @game_area.x && object.y <= (@game_area.height + $window.height)
103
+ end
45
104
 
46
- #@center_x = options[:center_x] if options[:center_x]
47
- #@center_y = options[:center_y] if options[:center_y]
105
+ # Returns true object is outside the game area
106
+ def outside_game_area?(object)
107
+ not inside_game_area?(object)
108
+ end
109
+
110
+ #
111
+ # Modify viewports x and y from target_x / target_y and x_lag / y_lag
112
+ # Use this to have the viewport "slide" after the player
113
+ #
114
+ def move_towards_target
115
+ if @x_target && @x != @x_target
116
+ x_step = @x_target - @x
117
+ self.x = @x + x_step * (1.0 - @x_lag)
118
+ end
48
119
 
49
- @x_min = nil
50
- @x_max = nil
51
- @y_min = nil
52
- @y_max = nil
120
+ if @y_target && @y != @y_target
121
+ y_step = @y_target - @y
122
+ self.y = @y + y_step * (1.0 - @y_lag)
123
+ end
53
124
  end
54
125
 
126
+ #
127
+ # Viewports X setter with boundschecking
128
+ #
55
129
  def x=(x)
56
130
  @x = x
57
- @x = @x_min if @x_min && @x < @x_min
58
- @x = @x_max if @x_max && @x > @x_max
131
+ if @game_area
132
+ @x = @game_area.x if @x < @game_area.x
133
+ @x = @game_area.width if @x > @game_area.width
134
+ end
59
135
  end
60
136
 
137
+ #
138
+ # Viewports Y setter with boundschecking
139
+ #
61
140
  def y=(y)
62
141
  @y = y
63
- @y = @y_min if @y_min && @y < @y_min
64
- @y = @y_max if @y_max && @y > @y_max
142
+ if @game_area
143
+ @y = @game_area.y if @y < @game_area.y
144
+ @y = @game_area.height if @y > @game_area.height
145
+ end
65
146
  end
66
147
 
67
148
  end
@@ -20,7 +20,6 @@
20
20
  #++
21
21
 
22
22
  module Chingu
23
-
24
23
  #
25
24
  # See http://www.libgosu.org/rdoc/classes/Gosu/Window.html
26
25
  #
@@ -40,6 +39,7 @@ module Chingu
40
39
  include Chingu::Helpers::InputClient # Window has its own inputmap
41
40
 
42
41
  attr_reader :root, :game_state_manager, :game_objects, :milliseconds_since_last_tick
42
+ attr_accessor :factor
43
43
 
44
44
  def initialize(width = 800, height = 600, fullscreen = false, update_interval = 16.666666)
45
45
  fullscreen ||= ARGV.include?("--fullscreen")
@@ -56,9 +56,15 @@ module Chingu
56
56
 
57
57
  @fps_counter = FPSCounter.new
58
58
  @game_state_manager = GameStateManager.new
59
- @milliseconds_since_last_tick = 0
59
+ @milliseconds_since_last_tick = 0
60
+ @factor = 1
61
+
62
+ setup
60
63
  end
61
64
 
65
+ # Placeholder to be overwritten
66
+ def setup; end;
67
+
62
68
  #
63
69
  # Returns self inside GameState.initialize (a game state is not 'active' inside initialize())
64
70
  # Or returns current active game state (as in a switched to or pushed game state)