ippa-chingu 0.2.0 → 0.3.0

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/lib/chingu/assets.rb CHANGED
@@ -30,7 +30,15 @@ module Gosu
30
30
  (path = find_file(name)) ? Gosu::Image.new($window, path, true) : nil
31
31
  end
32
32
  end
33
+
34
+ class Song
35
+ include Chingu::NamedResource
33
36
 
37
+ def self.autoload(name)
38
+ (path = find_file(name)) ? Gosu::Song.new($window, path) : nil
39
+ end
40
+ end
41
+
34
42
  class Sample
35
43
  include Chingu::NamedResource
36
44
 
@@ -1,21 +1,41 @@
1
1
  module Chingu
2
+ #
3
+ # Calculates a fps and a tick-time for use in update-calls
4
+ # register_tick() must be called every game loop iteration
5
+ #
2
6
  class FPSCounter
3
- attr_reader :fps
7
+ attr_reader :fps, :milliseconds_since_last_tick, :ticks
4
8
 
5
9
  def initialize
6
10
  @current_second = Gosu::milliseconds / 1000
7
11
  @accum_fps = 0
8
12
  @fps = 0
13
+ @ticks = 0
14
+
15
+ @milliseconds_since_last_tick = 0
16
+ @last_value = Gosu::milliseconds
9
17
  end
10
18
 
19
+ #
20
+ # This should be called once every game-iteration, preferable in update()
21
+ #
11
22
  def register_tick
12
23
  @accum_fps += 1
24
+ @ticks += 1
13
25
  current_second = Gosu::milliseconds / 1000
14
26
  if current_second != @current_second
15
27
  @current_second = current_second
16
28
  @fps = @accum_fps
17
29
  @accum_fps = 0
18
30
  end
31
+
32
+ #
33
+ # Calculate how many milliseconds passed since last game loop iteration.
34
+ # useful in update()-calls
35
+ #
36
+ @milliseconds_since_last_tick = Gosu::milliseconds - @last_value
37
+ @last_value = Gosu::milliseconds
38
+ return @milliseconds_since_last_tick
19
39
  end
20
40
  end
21
41
  end
@@ -6,10 +6,12 @@ module Chingu
6
6
  # All objects that inherits from this class will automaticly be updated and drawn.
7
7
  #
8
8
  class GameObject
9
- attr_accessor :image, :x, :y, :angle, :center_x, :center_y, :factor_x, :factor_y, :mode
10
- attr_accessor :update, :draw, :input
9
+ attr_accessor :image, :x, :y, :angle, :center_x, :center_y, :factor_x, :factor_y, :color, :mode
10
+ attr_accessor :update, :draw
11
11
  attr_reader :options
12
12
 
13
+ include Chingu::InputClient
14
+
13
15
  #
14
16
  # Class-level default values.
15
17
  # This allows you to set default-values that affect all created GameObjects after that.
@@ -17,12 +19,20 @@ module Chingu
17
19
  #
18
20
  # in Gosu::Window#initialize: GameObject.center_x = GameObject.center_y = 0
19
21
  #
22
+ @@x = nil
23
+ @@y = nil
20
24
  @@zorder = 100
21
25
  @@center_x = 0.5
22
26
  @@center_y = 0.5
23
27
  @@factor_x = 1.0
24
28
  @@factor_y = 1.0
25
29
 
30
+ def self.x; @@x; end
31
+ def self.x=(value); @@x = value; end
32
+
33
+ def self.y; @@y; end
34
+ def self.y=(value); @@y = value; end
35
+
26
36
  def self.zorder; @@zorder; end
27
37
  def self.zorder=(value); @@zorder = value; end
28
38
 
@@ -41,18 +51,18 @@ module Chingu
41
51
  #
42
52
  # Create a new GameObject. Arguments are given in hash-format:
43
53
  #
44
- # :x screen x-coordinate (default 0, to the left)
45
- # :y screen y-coordinate (default 0, top of screen)
46
- # :angle angle of object, used in draw_rot, (default 0, no rotation)
47
- # :zorder a gameclass "foo" with higher zorder then gameclass "bar" is drawn on top of "foo".
48
- # :center_x relative horizontal position of the rotation center on the image.
49
- # 0 is the left border, 1 is the right border, 0.5 is the center (default 0.5)
50
- # :center_y see center_x. (default 0.5)
51
- # :factor_x horizontal zoom-factor, use >1.0 to zoom in. (default 1.0, no zoom).
52
- # :factor_y vertical zoom-factor, use >1.0 to zoom in. (default 1.0, no zoom).
54
+ # :x screen x-coordinate (default 0, to the left)
55
+ # :y screen y-coordinate (default 0, top of screen)
56
+ # :angle angle of object, used in draw_rot, (default 0, no rotation)
57
+ # :zorder a gameclass "foo" with higher zorder then gameclass "bar" is drawn on top of "foo".
58
+ # :center_x relative horizontal position of the rotation center on the image.
59
+ # 0 is the left border, 1 is the right border, 0.5 is the center (default 0.5)
60
+ # :center_y see center_x. (default 0.5)
61
+ # :factor_x horizontal zoom-factor, use >1.0 to zoom in. (default 1.0, no zoom).
62
+ # :factor_y vertical zoom-factor, use >1.0 to zoom in. (default 1.0, no zoom).
53
63
  #
54
- # :update [true|false] Automaticly call #update on object each gameloop. Default +true+.
55
- # :draw [true|false] Automaticly call #update on object each gameloop. Default +true+.
64
+ # :update [true|false] Automaticly call #update on object each gameloop. Default +true+.
65
+ # :draw [true|false] Automaticly call #update on object each gameloop. Default +true+.
56
66
  #
57
67
  def initialize(options = {})
58
68
  @options = options
@@ -61,8 +71,8 @@ module Chingu
61
71
  @image = options[:image] if options[:image].is_a? Gosu::Image
62
72
  @image = Image[options[:image]] if options[:image].is_a? String
63
73
 
64
- @x = options[:x] || 0
65
- @y = options[:y] || 0
74
+ @x = options[:x] || @@x || 0
75
+ @y = options[:y] || @@y || 0
66
76
  @angle = options[:angle] || 0
67
77
  @zorder = options[:zorder] || @@zorder
68
78
  @center_x = options[:center_x] || options[:center] || @@center_x
@@ -72,6 +82,9 @@ module Chingu
72
82
  @color = options[:color] || 0xFFFFFFFF
73
83
  @mode = options[:mode] || :default # :additive is also available.
74
84
 
85
+ # Shortcuts for draw_rot arguments
86
+ @factor = 1
87
+
75
88
  # gameloop/framework logic
76
89
  @update = options[:update] || true
77
90
  @draw = options[:draw] || true
@@ -85,9 +98,32 @@ module Chingu
85
98
  @parent.add_game_object(self) if @parent
86
99
  end
87
100
 
101
+ def factor=(factor)
102
+ @factor = factor
103
+ @factor_x = @factor_y = @factor
104
+ end
105
+ alias :zoom= :factor=
106
+ def factor
107
+ @factor
108
+ end
109
+ alias :zoom :factor
88
110
 
89
- def update
90
- # Objects gamelogic here
111
+ #
112
+ # Returns true if game object is inside the game window, false if outside
113
+ #
114
+ def inside_window?(x = @x, y = @y)
115
+ x >= 0 && x <= $window.width && y >= 0 && y <= $window.height
116
+ end
117
+
118
+ #
119
+ #
120
+ #
121
+ def outside_window?(x = @x, y = @y)
122
+ not inside_window?(x,y)
123
+ end
124
+
125
+ def update(time = 1)
126
+ # Objects gamelogic here, 'time' is the time passed between 2 iterations of the main game loop
91
127
  end
92
128
 
93
129
  #
@@ -1,39 +1,115 @@
1
1
  module Chingu
2
+ #
3
+ # Chingu incorporates a basic push/pop game state system (as discussed here: http://www.gamedev.net/community/forums/topic.asp?topic_id=477320).
4
+ # Game states is a way of organizing your intros, menus, levels.
5
+ # Game states aren't complicated. In Chingu a GameState is a class that behaves mostly like your default Gosu::Window (or in our case Chingu::Window) game loop.
6
+ #
7
+ # # A simple GameState-example
8
+ # class Intro < Chingu::GameState
9
+ # def update
10
+ # # game logic here
11
+ # end
12
+ #
13
+ # def draw
14
+ # # screen manipulation here
15
+ # end
16
+ #
17
+ # # Called when we enter the game state
18
+ # def setup
19
+ # @player.angle = 0 # point player upwards
20
+ # end
21
+ #
22
+ # # Called when we leave the current game state
23
+ # def finalize
24
+ # push_game_state(Menu) # switch to game state "Menu"
25
+ # end
26
+ # end
27
+ #
28
+
2
29
  class GameState
3
- include Chingu::GameStateHelpers # Easy access to the global game_state-queue
4
- include Chingu::DrawHelpers # Adds fill(), fade() etc to each gamestate.
5
-
6
- attr_reader :options # so jac can access his :level-number
7
- attr_reader :game_objects
8
- attr_accessor :input
30
+ include Chingu::GameStateHelpers # Easy access to the global game state-queue
31
+ include Chingu::DrawHelpers # Adds fill(), fade() etc to each game state
32
+ include Chingu::GameObjectHelpers # adds game_objects_of_class etc ...
33
+ include Chingu::InputDispatcher # dispatch-helpers
34
+ include Chingu::InputClient
35
+
36
+ attr_reader :options # so jlnr can access his :level-number
37
+ attr_reader :game_objects, :do_setup
9
38
 
10
39
  def initialize(options = {})
11
40
  @options = options
12
- @game_objects = Array.new
13
- @input = options[:input]
41
+ @do_setup = options[:setup] || true
42
+
43
+ @game_objects = Set.new
44
+ @input_clients = Set.new # Set is like a unique Array with Hash lookupspeed
45
+
14
46
  $window.game_state_manager.inside_state = self
15
- setup
16
47
  end
17
48
 
18
- def add_game_object(game_object)
19
- @game_objects.push(game_object) unless @game_objects.include?(game_object)
49
+ #
50
+ # An unique identifier for the GameState-class,
51
+ # Used in game state manager to keep track of created states.
52
+ #
53
+ def to_sym
54
+ self.class.to_s.to_sym
55
+ end
56
+
57
+ def add_game_object(object)
58
+ @game_objects << object
59
+ end
60
+ def remove_game_object(object)
61
+ @input_clients.delete(object)
20
62
  end
21
63
 
22
64
  def setup
65
+ # Your game state setup logic here.
23
66
  end
24
67
 
68
+ #
69
+ # Called when a button is pressed and a game state is active
70
+ #
25
71
  def button_down(id)
72
+ dispatch_button_down(id, self)
73
+ @input_clients.each { |object| dispatch_button_down(id, object) }
26
74
  end
27
75
 
76
+ #
77
+ # Called when a button is released and a game state active
78
+ #
28
79
  def button_up(id)
80
+ dispatch_button_up(id, self)
81
+ @input_clients.each { |object| dispatch_button_up(id, object) }
29
82
  end
30
-
31
- def update
32
- @game_objects.each { |object| object.update }
83
+
84
+ #
85
+ # Calls update on each game object that has current game state as parent (created inside that game state)
86
+ #
87
+ def update(time = 1)
88
+ dispatch_input_for(self)
89
+ @input_clients.each { |game_object| dispatch_input_for(game_object) }
90
+
91
+ @game_objects.each { |object| object.update(time) }
33
92
  end
34
93
 
94
+ #
95
+ # Calls Draw on each game object that has current game state as parent (created inside that game state)
96
+ #
35
97
  def draw
36
98
  @game_objects.each { |object| object.draw }
37
99
  end
100
+
101
+ #
102
+ # Closes game state by poping it off the stack (and activating the game state below)
103
+ #
104
+ def close
105
+ pop_game_state
106
+ end
107
+
108
+ #
109
+ # Closes main window and terminates the application
110
+ #
111
+ def close_game
112
+ $window.close
113
+ end
38
114
  end
39
115
  end
@@ -1,47 +1,103 @@
1
- module Chingu
1
+ module Chingu
2
+ #
3
+ # GameStateManger is responsible for keeping track of game states with a simple pop/push stack.
4
+ #
5
+ # Chingu::Window automatically creates a @game_state_manager and makes it accessible in our game loop.
6
+ # By default the game loop calls update() / draw() on @game_state_manager
7
+ #
2
8
  class GameStateManager
3
9
  attr_accessor :inside_state
4
- attr_reader :states
10
+ attr_reader :states, :created_states
5
11
 
6
12
  def initialize
7
13
  @inside_state = nil
8
14
  @states = []
15
+ @created_states = {}
9
16
  end
10
17
 
11
18
  #
12
19
  # Gets the currently active gamestate (top of stack)
13
20
  #
14
- def state
21
+ def current_state
15
22
  @states.last
16
23
  end
17
24
 
18
25
  #
19
- # Adds a state to the gamestate-stack
26
+ # Adds a state to the game state-stack and activates it
20
27
  #
21
- def push_state(state)
22
- @states.push(state)
28
+ def push_state(state, options = {})
29
+ new_state = nil
30
+
31
+ #
32
+ # If state is a GameState-instance, just queue it
33
+ #
34
+ if state.is_a? Chingu::GameState
35
+ new_state = state
36
+ #
37
+ # If state is a GameState-class, create/initialize it once (@created_states keeps track of this)
38
+ #
39
+ elsif state.superclass == Chingu::GameState
40
+
41
+ if @created_states[state.to_s]
42
+ new_state = @created_states[state.to_s]
43
+ else
44
+ new_state = state.new(options)
45
+ @created_states[state.class.to_s] = new_state
46
+ end
47
+ end
48
+
49
+ #
50
+ # If the new state is all good
51
+ #
52
+ if new_state
53
+ # Give the soon-to-be-disabled state a chance to clean up by calling finalize() on it.
54
+ current_state.finalize if current_state.respond_to? :finalize
55
+
56
+ # Call setup
57
+ new_state.setup if new_state.do_setup
58
+
59
+ # Push new state on top of stack and therefore making it active
60
+ @states.push(new_state)
61
+ end
23
62
  end
24
63
 
25
64
  #
26
- # Pops a state off the gamestate-stack
27
- #
28
- def pop_state
65
+ # Pops a state off the game state-stack, activating the previous one.
66
+ #
67
+ def pop_state(options = {})
68
+ #
69
+ # Give the soon-to-be-disabled state a chance to clean up by calling finalize() on it.
70
+ #
71
+ current_state.finalize if current_state.respond_to? :finalize
72
+
73
+ #
74
+ # Activate the game state "bellow" current one with a simple Array.pop
75
+ #
29
76
  @states.pop
77
+
78
+ # Call setup on the new current state
79
+ current_state.setup unless options[:setup] == false
30
80
  end
31
81
 
32
82
  #
33
- # Returns the previous gamestate
83
+ # Returns the previous game state
34
84
  #
35
85
  def previous_state
36
- @states[@states.index(state)-1]
86
+ @states[@states.index(current_state)-1]
37
87
  end
38
-
39
88
  alias :prev_state previous_state
40
89
 
41
90
  #
42
- # Pops through all gamestates until matching a given gamestate
91
+ # Remove all game states from stack
92
+ #
93
+ def clear_states
94
+ @states.clear
95
+ end
96
+
97
+ #
98
+ # Pops through all game states until matching a given game state
43
99
  #
44
- def switch_state(new_state)
100
+ def pop_until_game_state(new_state)
45
101
  while (state = @states.pop)
46
102
  break if state == new_state
47
103
  end
@@ -58,27 +114,27 @@ module Chingu
58
114
  # Called before #update when the user pressed a button while the window had the focus.
59
115
  #
60
116
  def button_down(id)
61
- state.button_down(id) if state
117
+ current_state.button_down(id) if current_state
62
118
  end
63
119
 
64
120
  #
65
121
  # Called when the user released a button.
66
122
  #
67
123
  def button_up(id)
68
- state.button_up(id) if state
124
+ current_state.button_up(id) if current_state
69
125
  end
70
126
 
71
127
  #
72
128
  # Calls #update on the current gamestate, if there is one.
73
129
  #
74
- def update
75
- state.update if state
130
+ def update(time = 1)
131
+ current_state.update(time) if current_state
76
132
  end
77
133
  #
78
134
  # Calls draw() on the current gamestate, if there is one.
79
135
  #
80
136
  def draw
81
- state.draw if state
137
+ current_state.draw if current_state
82
138
  end
83
139
  end
84
140
  end