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/History.txt +4 -1
- data/README.rdoc +120 -30
- data/chingu.gemspec +5 -5
- data/examples/example1.rb +7 -1
- data/examples/example2.rb +17 -8
- data/examples/example3.rb +1 -1
- data/examples/example4.rb +74 -26
- data/lib/chingu/assets.rb +8 -0
- data/lib/chingu/fpscounter.rb +21 -1
- data/lib/chingu/game_object.rb +53 -17
- data/lib/chingu/game_state.rb +90 -14
- data/lib/chingu/game_state_manager.rb +75 -19
- data/lib/chingu/helpers.rb +127 -19
- data/lib/chingu/input.rb +4 -4
- data/lib/chingu/parallax.rb +4 -1
- data/lib/chingu/text.rb +16 -6
- data/lib/chingu/window.rb +104 -104
- metadata +3 -3
data/lib/chingu/helpers.rb
CHANGED
@@ -1,41 +1,149 @@
|
|
1
1
|
module Chingu
|
2
|
-
|
2
|
+
|
3
|
+
module InputClient
|
4
|
+
def input=(input_map)
|
5
|
+
@input = input_map
|
6
|
+
@parent.add_input_client(self) if @parent
|
7
|
+
end
|
8
|
+
|
9
|
+
def input
|
10
|
+
@input
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module InputDispatcher
|
15
|
+
def add_input_client(object)
|
16
|
+
@input_clients << object
|
17
|
+
end
|
18
|
+
|
19
|
+
def remove_input_client(object)
|
20
|
+
@input_clients.delete(object)
|
21
|
+
end
|
22
|
+
|
23
|
+
def dispatch_button_down(id, object)
|
24
|
+
return if object.nil? || object.input.nil?
|
25
|
+
|
26
|
+
object.input.each do |symbol, action|
|
27
|
+
if Input::SYMBOL_TO_CONSTANT[symbol] == id
|
28
|
+
dispatch_action(action, object)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def dispatch_button_up(id, object)
|
34
|
+
return if object.nil? || object.input.nil?
|
35
|
+
|
36
|
+
object.input.each do |symbol, action|
|
37
|
+
if symbol.to_s.include? "released_"
|
38
|
+
symbol = symbol.to_s.sub("released_", "").to_sym
|
39
|
+
if Input::SYMBOL_TO_CONSTANT[symbol] == id
|
40
|
+
dispatch_action(action, object)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Dispatches input-mapper for any given object
|
48
|
+
#
|
49
|
+
def dispatch_input_for(object, prefix = "holding_")
|
50
|
+
return if object.nil? || object.input.nil?
|
51
|
+
|
52
|
+
object.input.each do |symbol, action|
|
53
|
+
if symbol.to_s.include? prefix
|
54
|
+
symbol = symbol.to_s.sub(prefix, "").to_sym
|
55
|
+
if $window.button_down?(Input::SYMBOL_TO_CONSTANT[symbol])
|
56
|
+
dispatch_action(action, object)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
3
61
|
|
4
62
|
#
|
5
|
-
#
|
63
|
+
# For a given object, dispatch "action".
|
64
|
+
# An action can be:
|
6
65
|
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
66
|
+
# * Symbol (:p, :space), translates into a method-call
|
67
|
+
# * Proc/Lambda, call() it
|
68
|
+
# * GameState-instance, push it on top of stack
|
69
|
+
# * GameState-inherited class, create a new instance, cache it and push it on top of stack
|
11
70
|
#
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
71
|
+
def dispatch_action(action, object)
|
72
|
+
# puts "Dispatch Action: #{action} - Objects class: #{object.class.to_s}"
|
73
|
+
if action.is_a? Symbol
|
74
|
+
object.send(action)
|
75
|
+
elsif action.is_a? Proc
|
76
|
+
action.call
|
77
|
+
elsif action.is_a? Chingu::GameState
|
78
|
+
push_game_state(action)
|
79
|
+
elsif action.superclass == Chingu::GameState
|
80
|
+
push_game_state(action)
|
17
81
|
end
|
18
82
|
end
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# push_game_state accepts either a class inherited from GameState or an object-instance from such a class.
|
87
|
+
#
|
88
|
+
# push_game_state(Intro):
|
89
|
+
# game state mananger will create a new Intro-object first time called and cache it.
|
90
|
+
#
|
91
|
+
# push_game_state(Intro.new):
|
92
|
+
# The first line ends up calling "new" to Intro before activating the newly created game state.
|
93
|
+
# Each time 'push_game_state(Intro.new)' is called a new Intro-object will be created.
|
94
|
+
# Usefull for stuff like: push_game_state(Level.new(:level_nr => 11))
|
95
|
+
#
|
96
|
+
module GameStateHelpers
|
97
|
+
def push_game_state(state, options = {})
|
98
|
+
$window.game_state_manager.push_state(state, options)
|
99
|
+
end
|
19
100
|
|
20
|
-
def
|
21
|
-
$window.game_state_manager.pop_state
|
101
|
+
def pop_game_state(options = {})
|
102
|
+
$window.game_state_manager.pop_state(options)
|
22
103
|
end
|
23
104
|
|
24
|
-
def
|
25
|
-
$window.game_state_manager.
|
105
|
+
def current_game_state
|
106
|
+
$window.game_state_manager.current_state
|
26
107
|
end
|
27
108
|
|
28
|
-
def
|
109
|
+
def previous_game_state
|
29
110
|
$window.game_state_manager.previous_state
|
30
111
|
end
|
112
|
+
|
113
|
+
def clear_game_states
|
114
|
+
$window.game_state_manager.clear_states
|
115
|
+
end
|
31
116
|
end
|
32
117
|
|
118
|
+
#
|
119
|
+
# Various helper-methods to manipulate the screen
|
120
|
+
#
|
33
121
|
module DrawHelpers
|
34
|
-
|
35
|
-
|
36
|
-
|
122
|
+
#
|
123
|
+
# Fills whole window with color 'c'
|
124
|
+
#
|
125
|
+
def fill(c)
|
126
|
+
$window.draw_quad(0,0,c,$window.width,0,c,$window.width,$window.width,c,0,$window.height,c,0,:default)
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# Fills a given Rect 'r' with color 'c'
|
131
|
+
#
|
132
|
+
def fill_rect(r, c)
|
133
|
+
$window.draw_quad(r.x,r.y,c, r.right,r.y,c, r.right,r.bottom,c, r.x,r.bottom,c,0,:default)
|
134
|
+
end
|
37
135
|
|
38
136
|
def fade(options = {})
|
39
137
|
end
|
40
138
|
end
|
139
|
+
|
140
|
+
module GameObjectHelpers
|
141
|
+
#
|
142
|
+
# Fetch game objects of a certain type/class
|
143
|
+
#
|
144
|
+
def game_objects_of_class(klass)
|
145
|
+
@game_objects.select { |game_object| game_object.is_a? klass }
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
41
149
|
end
|
data/lib/chingu/input.rb
CHANGED
@@ -19,14 +19,14 @@ module Chingu
|
|
19
19
|
|
20
20
|
KbBackspace => [:backspace],
|
21
21
|
KbDelete => [:delete, :del],
|
22
|
-
KbDown => [:down],
|
22
|
+
KbDown => [:down_arrow, :down],
|
23
23
|
KbEnd => [:end],
|
24
24
|
KbEnter => [:enter],
|
25
25
|
KbEscape => [:escape, :esc],
|
26
26
|
|
27
27
|
KbHome => [:home],
|
28
28
|
KbInsert => [:insert, :ins],
|
29
|
-
KbLeft => [:left],
|
29
|
+
KbLeft => [:left_arrow, :left],
|
30
30
|
KbLeftAlt => [:left_alt, :lalt],
|
31
31
|
KbLeftControl => [:left_control, :left_ctrl, :lctrl],
|
32
32
|
KbLeftShift => [:left_shift, :lshift],
|
@@ -40,13 +40,13 @@ module Chingu
|
|
40
40
|
KbPageUp => [:page_up],
|
41
41
|
# KbPause => [:pause],
|
42
42
|
KbReturn => [:return],
|
43
|
-
KbRight => [:right],
|
43
|
+
KbRight => [:right_arrow, :right],
|
44
44
|
KbRightAlt => [:right_alt, :ralt],
|
45
45
|
KbRightControl => [:right_control, :right_ctrl, :rctrl],
|
46
46
|
KbRightShift => [:right_shift, :rshift],
|
47
47
|
KbSpace => [:" ", :space],
|
48
48
|
KbTab => [:tabulator, :tab],
|
49
|
-
KbUp => [:up],
|
49
|
+
KbUp => [:up_arrow, :up],
|
50
50
|
|
51
51
|
MsLeft => [:left_mouse_button, :mouse_left],
|
52
52
|
MsMiddle => [:middle_mouse_button, :mouse_middle],
|
data/lib/chingu/parallax.rb
CHANGED
data/lib/chingu/text.rb
CHANGED
@@ -13,7 +13,17 @@ module Chingu
|
|
13
13
|
#
|
14
14
|
class Text < Chingu::GameObject
|
15
15
|
attr_accessor :text
|
16
|
-
attr_reader :height, :
|
16
|
+
attr_reader :height, :gosu_font
|
17
|
+
|
18
|
+
@@size = nil
|
19
|
+
@@font = nil
|
20
|
+
def self.font; @@font; end
|
21
|
+
def self.font=(value); @@font = value; end
|
22
|
+
|
23
|
+
def self.size; @@size; end
|
24
|
+
def self.size=(value); @@size = value; end
|
25
|
+
def self.height; @@size; end
|
26
|
+
def self.height=(value); @@size = value; end
|
17
27
|
|
18
28
|
#
|
19
29
|
# Takes the standard GameObject-hash-arguments but also:
|
@@ -24,21 +34,21 @@ module Chingu
|
|
24
34
|
def initialize(options)
|
25
35
|
super(options)
|
26
36
|
@text = options[:text] || "-No text specified-"
|
27
|
-
@
|
28
|
-
@height = options[:height] || options[:size] ||
|
37
|
+
@font = options[:font] || @@font || "verdana"
|
38
|
+
@height = options[:height] || options[:size] || @@size || default_font_name()
|
29
39
|
|
30
|
-
@
|
40
|
+
@gosu_font = Gosu::Font.new($window, @font, @height)
|
31
41
|
end
|
32
42
|
|
33
43
|
def draw
|
34
|
-
@
|
44
|
+
@gosu_font.draw_rot(@text, @x.to_i, @y.to_i, @zorder, @angle, @factor_x, @factor_y, @color, @mode)
|
35
45
|
end
|
36
46
|
|
37
47
|
#
|
38
48
|
# Returns the width, in pixels, the given text would occupy if drawn.
|
39
49
|
#
|
40
50
|
def width
|
41
|
-
@
|
51
|
+
@gosu_font.text_width(@text, @factor_x)
|
42
52
|
end
|
43
53
|
|
44
54
|
end
|
data/lib/chingu/window.rb
CHANGED
@@ -1,11 +1,22 @@
|
|
1
1
|
module Chingu
|
2
2
|
class Window < Gosu::Window
|
3
|
-
|
4
|
-
include Chingu::
|
3
|
+
# adds push_game_state, pop_game_state, current_game_state and previous_game_state
|
4
|
+
include Chingu::GameStateHelpers
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
# adds fill() etc...
|
7
|
+
include Chingu::DrawHelpers
|
8
|
+
|
9
|
+
# adds game_objects_of_class etc ...
|
10
|
+
include Chingu::GameObjectHelpers
|
11
|
+
|
12
|
+
# Input dispatch helpers
|
13
|
+
include Chingu::InputDispatcher
|
14
|
+
|
15
|
+
# input= and input
|
16
|
+
include Chingu::InputClient
|
17
|
+
|
18
|
+
attr_reader :root, :game_state_manager, :game_objects, :milliseconds_since_last_tick
|
19
|
+
|
9
20
|
|
10
21
|
#
|
11
22
|
# See http://www.libgosu.org/rdoc/classes/Gosu/Window.html
|
@@ -26,136 +37,125 @@ module Chingu
|
|
26
37
|
Gosu::Image.autoload_dirs = [".", File.join(@root, "gfx"), File.join(@root, "media")]
|
27
38
|
Gosu::Sample.autoload_dirs = [".", File.join(@root, "sound"), File.join(@root, "media")]
|
28
39
|
Gosu::Tile.autoload_dirs = [".", File.join(@root, "gfx"), File.join(@root, "media")]
|
40
|
+
Gosu::Song.autoload_dirs = [".", File.join(@root, "sfx"), File.join(@root, "media")]
|
29
41
|
|
30
|
-
@game_objects =
|
31
|
-
@
|
32
|
-
|
33
|
-
@ticks = 0
|
34
|
-
@last_tick = Gosu::milliseconds
|
42
|
+
@game_objects = Set.new
|
43
|
+
@input_clients = Set.new # Set is like a unique Array with Hash lookupspeed
|
35
44
|
|
36
45
|
@fps_counter = FPSCounter.new
|
37
46
|
@game_state_manager = GameStateManager.new
|
38
47
|
|
39
|
-
@update_list = []
|
40
|
-
@draw_list = []
|
41
|
-
|
42
48
|
self.input = { :escape => close }
|
43
49
|
end
|
44
50
|
|
45
|
-
def add_game_object(
|
46
|
-
@game_objects
|
51
|
+
def add_game_object(object)
|
52
|
+
@game_objects << object
|
47
53
|
end
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
54
|
+
def remove_game_object(object)
|
55
|
+
@input_clients.delete(object)
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# Frames per second
|
60
|
+
#
|
55
61
|
def fps
|
56
62
|
@fps_counter.fps
|
57
63
|
end
|
58
|
-
|
59
|
-
#
|
60
|
-
# By default button_up sends the keyevent to the GameStateManager
|
61
|
-
# .. Which then is responsible to send it to the right GameState(s)
|
62
|
-
#
|
63
|
-
def button_up(id)
|
64
|
-
@game_state_manager.button_up(id)
|
65
|
-
end
|
66
|
-
|
64
|
+
alias :framerate :fps
|
67
65
|
|
68
66
|
#
|
69
|
-
#
|
70
|
-
# .. Which then is responsible to send it to the right GameState(s)
|
67
|
+
# Total amount of game iterations (ticks)
|
71
68
|
#
|
72
|
-
|
73
|
-
|
74
|
-
|
69
|
+
def ticks
|
70
|
+
@fps_counter.ticks
|
71
|
+
end
|
75
72
|
|
76
|
-
|
77
73
|
#
|
78
|
-
#
|
74
|
+
# Chingus core-logic / loop. Gosu will call this each game-iteration.
|
79
75
|
#
|
80
76
|
def update
|
81
|
-
@fps_counter.register_tick
|
82
|
-
update_tick
|
83
|
-
|
84
77
|
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
# - ... and all GameObjects connected to it
|
78
|
+
# Register a tick with our rather standard tick/framerate counter.
|
79
|
+
# Returns the amount of milliseconds since last tick. This number is used in all update()-calls.
|
80
|
+
# Without this self.fps would return an incorrect value.
|
81
|
+
# If you override this in your Chingu::Window class, make sure to call super.
|
90
82
|
#
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
@
|
102
|
-
|
83
|
+
@milliseconds_since_last_tick = @fps_counter.register_tick
|
84
|
+
|
85
|
+
#
|
86
|
+
# Dispatch inputmap for main window
|
87
|
+
#
|
88
|
+
dispatch_input_for(self)
|
89
|
+
|
90
|
+
#
|
91
|
+
# Dispatch input for all input-clients handled by to main window (game objects with input created in main win)
|
92
|
+
#
|
93
|
+
@input_clients.each { |game_object| dispatch_input_for(game_object) }
|
94
|
+
|
95
|
+
|
96
|
+
#
|
97
|
+
# Call update(milliseconds_since_last_tick) on all game objects belonging to the main window.
|
98
|
+
#
|
99
|
+
update_game_objects
|
100
|
+
|
101
|
+
#
|
102
|
+
# Call update(milliseconds_since_last_tick) on all game objects belonging to the current game state.
|
103
|
+
#
|
104
|
+
update_game_state_manager
|
103
105
|
end
|
104
106
|
|
107
|
+
#
|
108
|
+
# Chingus main screen manupulation method.
|
109
|
+
# If you override this in your Chingu::Window class, make sure to call super.
|
110
|
+
# Gosu will call this each game-iteration just after #update
|
111
|
+
#
|
105
112
|
def draw
|
113
|
+
#
|
114
|
+
# Draw all game objects associated with the main window.
|
115
|
+
#
|
106
116
|
@game_objects.each { |object| object.draw }
|
117
|
+
|
118
|
+
#
|
119
|
+
# Let the game state manager call draw on the active game state (if any)
|
120
|
+
#
|
107
121
|
@game_state_manager.draw
|
108
122
|
end
|
109
|
-
|
110
|
-
private
|
111
123
|
|
112
124
|
#
|
113
|
-
#
|
125
|
+
# Call update() on all game objects in main game window.
|
114
126
|
#
|
115
|
-
def
|
116
|
-
|
117
|
-
|
118
|
-
object.input.each do |symbol, action|
|
119
|
-
if button_down?(Input::SYMBOL_TO_CONSTANT[symbol])
|
120
|
-
#puts "#{object.to_s} :#{symbol.to_s} => #{action.to_s}"
|
121
|
-
#puts "[#{action.class.to_s} - #{action.class.superclass.to_s}]"
|
122
|
-
if action.is_a? Symbol
|
123
|
-
object.send(action)
|
124
|
-
elsif action.is_a? Proc
|
125
|
-
action.call
|
126
|
-
elsif action.is_a? Chingu::GameState
|
127
|
-
push_gamestate(action)
|
128
|
-
elsif action.superclass == Chingu::GameState
|
129
|
-
push_gamestate(action)
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
127
|
+
def update_game_objects
|
128
|
+
@game_objects.each { |object| object.update(@milliseconds_since_last_tick) }
|
133
129
|
end
|
134
130
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
131
|
+
#
|
132
|
+
# Call update() on our game_state_manger
|
133
|
+
# -> call update on active state
|
134
|
+
# -> call update on all game objects in that state
|
135
|
+
#
|
136
|
+
def update_game_state_manager
|
137
|
+
@game_state_manager.update(@milliseconds_since_last_tick)
|
138
|
+
end
|
139
139
|
|
140
140
|
|
141
|
-
#
|
142
|
-
#
|
143
|
-
#
|
144
|
-
#
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
#
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
141
|
+
#
|
142
|
+
# By default button_up sends the keyevent to the GameStateManager
|
143
|
+
# .. Which then is responsible to send it to the right GameState(s)
|
144
|
+
#
|
145
|
+
def button_up(id)
|
146
|
+
dispatch_button_up(id, self)
|
147
|
+
@input_clients.each { |object| dispatch_button_up(id, object) }
|
148
|
+
@game_state_manager.button_up(id)
|
149
|
+
end
|
150
|
+
|
151
|
+
#
|
152
|
+
# By default button_down sends the keyevent to the GameStateManager
|
153
|
+
# .. Which then is responsible to send it to the right GameState(s)
|
154
|
+
#
|
155
|
+
def button_down(id)
|
156
|
+
dispatch_button_down(id, self)
|
157
|
+
@input_clients.each { |object| dispatch_button_down(id, object) }
|
158
|
+
@game_state_manager.button_down(id)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|