moonpxi-nimo 0.1.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/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +15 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/examples/bouncy.rb +101 -0
- data/examples/eventy.rb +19 -0
- data/examples/images/dg_classm32.png +0 -0
- data/examples/images/dg_dungeon32.png +0 -0
- data/examples/images/jeeklabs.png +0 -0
- data/examples/images/tcheco.png +0 -0
- data/examples/imagey.rb +82 -0
- data/examples/screeny.rb +80 -0
- data/examples/simplest.rb +23 -0
- data/examples/sounds/KDE-Sys-Log-Out.ogg +0 -0
- data/examples/sounds/k3b_error1.wav +0 -0
- data/examples/soundy.rb +40 -0
- data/examples/spritey.rb +90 -0
- data/lib/nimo.rb +35 -0
- data/lib/nimo/behavior/deflector.rb +39 -0
- data/lib/nimo/behavior/moveable.rb +33 -0
- data/lib/nimo/behavior/projectile.rb +23 -0
- data/lib/nimo/behavior/with_velocity.rb +28 -0
- data/lib/nimo/game_object.rb +58 -0
- data/lib/nimo/game_window.rb +59 -0
- data/lib/nimo/listeners/event_listener.rb +27 -0
- data/lib/nimo/listeners/input_listener.rb +70 -0
- data/lib/nimo/representations/image_representation.rb +18 -0
- data/lib/nimo/representations/object_representation.rb +67 -0
- data/lib/nimo/representations/quad_representation.rb +23 -0
- data/lib/nimo/representations/sprite_representation.rb +78 -0
- data/lib/nimo/representations/text_representation.rb +27 -0
- data/lib/nimo/screen.rb +103 -0
- data/lib/nimo/utils/game.rb +63 -0
- data/lib/nimo/utils/intersection.rb +30 -0
- data/lib/nimo/utils/object_extension.rb +17 -0
- data/lib/nimo/utils/resources.rb +51 -0
- data/nimo.gemspec +117 -0
- data/spec/nimo/behavior/deflector_spec.rb +51 -0
- data/spec/nimo/behavior/moveable_spec.rb +81 -0
- data/spec/nimo/behavior/projectile_spec.rb +34 -0
- data/spec/nimo/behavior/with_velocity_spec.rb +18 -0
- data/spec/nimo/game_object_spec.rb +169 -0
- data/spec/nimo/game_window_spec.rb +88 -0
- data/spec/nimo/listeners/event_listener_spec.rb +34 -0
- data/spec/nimo/listeners/input_listener_spec.rb +87 -0
- data/spec/nimo/representations/image_representation_spec.rb +41 -0
- data/spec/nimo/representations/object_representation_spec.rb +74 -0
- data/spec/nimo/representations/sprite_representation_spec.rb +73 -0
- data/spec/nimo/representations/text_representation_spec.rb +21 -0
- data/spec/nimo/screen_spec.rb +114 -0
- data/spec/nimo/utils/game_spec.rb +44 -0
- data/spec/nimo/utils/object_extension_spec.rb +21 -0
- data/spec/nimo/utils/resources_spec.rb +59 -0
- data/spec/spec_helper.rb +9 -0
- metadata +133 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
module Nimo
|
2
|
+
|
3
|
+
class ImageRepresentation < ObjectRepresentation
|
4
|
+
represent :image
|
5
|
+
|
6
|
+
def load(resources, params)
|
7
|
+
raise "Must provide :image param for image loading" unless params.has_key?(:image)
|
8
|
+
|
9
|
+
@image = resources.image(params[:image])
|
10
|
+
@image = @image[params[:index]] if params.has_key?(:index)
|
11
|
+
end
|
12
|
+
|
13
|
+
def draw
|
14
|
+
@image.draw(@game_object.x, @game_object.y, 0)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Nimo
|
2
|
+
|
3
|
+
# Nimo::GameObject's view. It holds actions to be executed on every update or when a key is pressed.
|
4
|
+
#
|
5
|
+
class ObjectRepresentation
|
6
|
+
include InputListener, EventListener
|
7
|
+
|
8
|
+
attr_reader :game_object
|
9
|
+
attr_accessor :game_window
|
10
|
+
|
11
|
+
def initialize(game_window, game_object)
|
12
|
+
@game_window, @game_object = game_window, game_object
|
13
|
+
@always_actions = []
|
14
|
+
end
|
15
|
+
|
16
|
+
# Hook to load data (i.e. images and fonts) from the game window.
|
17
|
+
def load(resources, params)
|
18
|
+
raise "Load should be overriden by representation"
|
19
|
+
end
|
20
|
+
|
21
|
+
# Register and action that always execute on a game update.
|
22
|
+
def always(&action)
|
23
|
+
@always_actions << action
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
# Register an action that will execute when the game object sends a notification. Overrides EventListener to
|
28
|
+
# add default registration to game_object.
|
29
|
+
#
|
30
|
+
def listen_to(event_type, &action)
|
31
|
+
super
|
32
|
+
game_object.register_listener(event_type, self)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Register an observer to be invoked every update, after all actions runned. This could be useful when a more complex behavior
|
36
|
+
# is required from the representation, and there is a need to inspect the game object to change some state.
|
37
|
+
#
|
38
|
+
def with_observer(&observer)
|
39
|
+
@observer = observer
|
40
|
+
end
|
41
|
+
|
42
|
+
def update
|
43
|
+
@always_actions.each { |action| @game_object.instance_eval(&action) }
|
44
|
+
process_inputs(@game_window)
|
45
|
+
@observer.call(self, game_object) unless @observer.nil?
|
46
|
+
end
|
47
|
+
|
48
|
+
# Should be overriden by childs.
|
49
|
+
def draw
|
50
|
+
raise "Draw should be overriden by representation"
|
51
|
+
end
|
52
|
+
|
53
|
+
def act_upon
|
54
|
+
@game_object
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# Register itself as a object representation with the shortcut name.
|
60
|
+
#
|
61
|
+
def self.represent(representation_name)
|
62
|
+
Screen.register_representation(representation_name, self)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Nimo
|
2
|
+
|
3
|
+
class QuadRepresentation < ObjectRepresentation
|
4
|
+
represent :quad
|
5
|
+
attr_accessor :color
|
6
|
+
|
7
|
+
def load(resources, params)
|
8
|
+
raise "Must provide :color param for quad loading" unless params.has_key?(:color)
|
9
|
+
|
10
|
+
@color = params[:color]
|
11
|
+
end
|
12
|
+
|
13
|
+
def draw
|
14
|
+
@game_window.draw_quad(
|
15
|
+
@game_object.x, @game_object.y, @color,
|
16
|
+
@game_object.x + @game_object.width, @game_object.y, @color,
|
17
|
+
@game_object.x, @game_object.y + @game_object.height, @color,
|
18
|
+
@game_object.x + @game_object.width, @game_object.y + @game_object.height, @color)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Nimo
|
2
|
+
|
3
|
+
class SpriteRepresentation < ObjectRepresentation
|
4
|
+
represent :sprite
|
5
|
+
attr_accessor :flip
|
6
|
+
|
7
|
+
def initialize(game_window, game_object)
|
8
|
+
super(game_window, game_object)
|
9
|
+
@animations = {}
|
10
|
+
@current_animation = nil
|
11
|
+
@next_animation = nil
|
12
|
+
unflip
|
13
|
+
end
|
14
|
+
|
15
|
+
def load(resources, params)
|
16
|
+
raise "Must provide :image param for sprite loading" unless params.has_key?(:image)
|
17
|
+
@sprite_tiles = resources.image(params[:image])
|
18
|
+
end
|
19
|
+
|
20
|
+
def flip
|
21
|
+
@drawing = lambda { |sprite| sprite.draw(@game_object.x + @game_object.width, @game_object.y, 0, -1) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def unflip
|
25
|
+
@drawing = lambda { |sprite| sprite.draw(@game_object.x, @game_object.y, 0) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def change_to(animation_name)
|
29
|
+
@next_animation = animation_name
|
30
|
+
end
|
31
|
+
|
32
|
+
def draw
|
33
|
+
if @current_animation.name != @next_animation && @animations.has_key?(@next_animation)
|
34
|
+
@current_animation = @animations[@next_animation]
|
35
|
+
@current_animation.reset_animation
|
36
|
+
end
|
37
|
+
@drawing.call(@sprite_tiles[@current_animation.frame_index])
|
38
|
+
end
|
39
|
+
|
40
|
+
# Adds a new animation to the sprite. The first animation will be set as the current.
|
41
|
+
# A list of options can be specified to customise the behavior:
|
42
|
+
# - :timeout (default is 0.1): time to wait between frames
|
43
|
+
# - :loop (default is true): true if the animation should start from the beginning, after the last element,
|
44
|
+
# otherwise it will draw the last frame
|
45
|
+
def with_animation(name, frame_indexes, options = {})
|
46
|
+
@animations[name] = Animation.new(name, frame_indexes, options)
|
47
|
+
@current_animation = @animations[name] if @current_animation.nil?
|
48
|
+
self
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class Animation
|
53
|
+
attr_reader :name
|
54
|
+
|
55
|
+
def initialize(name, frame_indexes, options)
|
56
|
+
@name = name
|
57
|
+
@frame_indexes = frame_indexes
|
58
|
+
@options = {:timeout => 0.1, :loop => true}.merge(options)
|
59
|
+
reset_animation
|
60
|
+
end
|
61
|
+
|
62
|
+
def reset_animation
|
63
|
+
@current_index = 0
|
64
|
+
@time_since_last_draw = Time.now
|
65
|
+
end
|
66
|
+
|
67
|
+
def frame_index
|
68
|
+
if (Time.now - @time_since_last_draw) > @options[:timeout]
|
69
|
+
@time_since_last_draw = Time.now
|
70
|
+
@current_index += 1
|
71
|
+
if @current_index == @frame_indexes.size
|
72
|
+
@current_index = @options[:loop] ? 0 : @frame_indexes.size - 1
|
73
|
+
end
|
74
|
+
end
|
75
|
+
@frame_indexes[@current_index]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Nimo
|
2
|
+
|
3
|
+
class TextRepresentation < ObjectRepresentation
|
4
|
+
represent :text
|
5
|
+
attr_accessor :color
|
6
|
+
|
7
|
+
def load(resources, params)
|
8
|
+
validate(params)
|
9
|
+
|
10
|
+
@font = resources.font(params[:font])
|
11
|
+
@color = params[:color]
|
12
|
+
@text = params[:text] # TODO Or should it get it from the game_object?
|
13
|
+
end
|
14
|
+
|
15
|
+
def draw
|
16
|
+
@font.draw(@text, @game_object.x, @game_object.y, 0, 1, 1, @color)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def validate(params)
|
22
|
+
[:font, :color, :text].each { |param| raise "Must provide :#{param} param for font loading" unless params.has_key?(param) }
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/lib/nimo/screen.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
module Nimo
|
4
|
+
|
5
|
+
# Represent a game's screen, holding representations of domain objects for updates and draws.
|
6
|
+
# TODO: Add info on how to add 'quad', 'image' (whatever) methods
|
7
|
+
#
|
8
|
+
class Screen
|
9
|
+
include InputListener, EventListener
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
attr_reader :id
|
13
|
+
|
14
|
+
def_delegators :@game_window, :go_to, :open_menu, :close_menu
|
15
|
+
def_delegators :@resources, :images, :sounds, :fonts
|
16
|
+
|
17
|
+
def initialize(id, game_window, resources)
|
18
|
+
@id, @game_window, @resources = id, game_window, resources
|
19
|
+
|
20
|
+
@representations = []
|
21
|
+
@events = {}
|
22
|
+
@timers = []
|
23
|
+
end
|
24
|
+
|
25
|
+
# Register a representation to be called as a method.
|
26
|
+
# Example:
|
27
|
+
# Screen.register_representation(:blah, Blah)
|
28
|
+
# a_screen.blah :for => obj, :with => {:attr => 42} do
|
29
|
+
# # Initialising representation
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
def self.register_representation(representation_name, representation_class)
|
33
|
+
define_method representation_name do |options, &blk|
|
34
|
+
representation = add(representation_class, options)
|
35
|
+
representation.instance_eval &blk if blk
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Add a representation to the screen. The options params is used to specify a GameObject and params. It returns the
|
40
|
+
# constructed representation.
|
41
|
+
# Examples of usage:
|
42
|
+
# screen.add(SomeRepresentation, :for => object, :with => { :attr => "something"}) # will construct SomeRepresentation with the supplied object and :with params
|
43
|
+
# screen.add(SomeRepresentation, :with => { :attr => "something"}) # will construct SomeRepresentation with a vanilla Nimo::GameObject
|
44
|
+
#
|
45
|
+
def add(representation_class, options)
|
46
|
+
params = options.has_key?(:with) ? options[:with] : {}
|
47
|
+
game_object = options.has_key?(:for) ? options[:for] : Nimo::GameObject.new(params)
|
48
|
+
|
49
|
+
representation = representation_class.new(@game_window, game_object)
|
50
|
+
representation.load(@resources, params)
|
51
|
+
|
52
|
+
@representations << representation
|
53
|
+
representation
|
54
|
+
end
|
55
|
+
|
56
|
+
# Defines an <tt>action</tt> to be executed after some <tt>seconds</tt>.
|
57
|
+
#
|
58
|
+
def timer_for(seconds, &action)
|
59
|
+
@timers << Timer.new(Time.now.to_f, seconds, action)
|
60
|
+
end
|
61
|
+
|
62
|
+
# :section: Gosu::Window hooks
|
63
|
+
|
64
|
+
# Updates all representations.
|
65
|
+
#
|
66
|
+
def update
|
67
|
+
process_timers
|
68
|
+
process_inputs(@game_window)
|
69
|
+
@representations.each { |representation| representation.update }
|
70
|
+
end
|
71
|
+
|
72
|
+
# Draws all representations.
|
73
|
+
#
|
74
|
+
def draw
|
75
|
+
@representations.each { |representation| representation.draw }
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def process_timers
|
81
|
+
@timers.find_all { |timer| timer.time_is_up? }.each do |timer|
|
82
|
+
timer.run_action
|
83
|
+
@timers.delete(timer)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
class Timer
|
90
|
+
def initialize(start_time, delay_in_seconds, action)
|
91
|
+
@start_time, @delay_in_seconds, @action = start_time, delay_in_seconds, action
|
92
|
+
end
|
93
|
+
|
94
|
+
def time_is_up?
|
95
|
+
(Time.now.to_f - @start_time) > @delay_in_seconds
|
96
|
+
end
|
97
|
+
|
98
|
+
def run_action
|
99
|
+
@action.call
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Nimo
|
2
|
+
|
3
|
+
# Helper method to create and show a new GameWindow with a construction block.
|
4
|
+
#
|
5
|
+
# Example, from simplest.rb:
|
6
|
+
#
|
7
|
+
# Nimo::Game("Simplest", 640, 480) do
|
8
|
+
# screen :game do
|
9
|
+
# quad :with => { :width => 20, :height => 20, :color => Gosu::white } do
|
10
|
+
# # Code ommited.
|
11
|
+
# end
|
12
|
+
# when_key(Gosu::Button::KbEscape) { exit }
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
def self.Game(title, width, height, &blk)
|
17
|
+
window = Nimo::GameWindow.new(title, width, height)
|
18
|
+
GameBuilder.new(window).instance_eval(&blk) if block_given?
|
19
|
+
window.show
|
20
|
+
return window
|
21
|
+
end
|
22
|
+
|
23
|
+
class GameBuilder
|
24
|
+
def initialize(game_window)
|
25
|
+
@game_window = game_window
|
26
|
+
@resources = Nimo::Resources.new(@game_window)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Load images that can be referenced by a tag.
|
30
|
+
# Example of <tt>image_definitions</tt>:
|
31
|
+
# images :some_tag => { :filename => "path_to_image.png" } # Load path_to_image.png to be used by the tag :some_tag
|
32
|
+
# images :tile_tag => { :filename => "path_to_tile.png", :tile_dimension => [32, 50] } # Load path_to_tile.png as a tile of width 32 and height 50
|
33
|
+
#
|
34
|
+
def images(image_definitions)
|
35
|
+
@resources.load_images(image_definitions)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Load fonts that can be referenced by a tag.
|
39
|
+
# Example of <tt>font_definitions</tt>:
|
40
|
+
# fonts :some_tag => { :type => "font_type", :size => 20 } # Load font of type 'font_type'
|
41
|
+
#
|
42
|
+
def fonts(font_definitions)
|
43
|
+
@resources.load_fonts(font_definitions)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Load sounds that can be referenced by a tag.
|
47
|
+
# Example of <tt>sound_definitions</tt>:
|
48
|
+
# sounds :some_tag => { :filename => "some_file.wav", :size => 20 }
|
49
|
+
#
|
50
|
+
def sounds(sound_definitions)
|
51
|
+
@resources.load_sounds(sound_definitions)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Register a new screen with the <tt>name</tt>, using the supplied block as the Screen constructor.
|
55
|
+
#
|
56
|
+
def screen(screen_id, &blk)
|
57
|
+
screen = Nimo::Screen.new(screen_id, @game_window, @resources)
|
58
|
+
screen.instance_eval(&blk) if block_given?
|
59
|
+
@game_window.add_screen(screen_id, screen)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Nimo
|
2
|
+
class Intersection
|
3
|
+
|
4
|
+
attr_reader :x, :y, :width, :height
|
5
|
+
|
6
|
+
def initialize(x, y, width, height)
|
7
|
+
@x = x
|
8
|
+
@y = y
|
9
|
+
@width = width
|
10
|
+
@height = height
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.between(obj1, obj2)
|
14
|
+
x = [obj1.x, obj2.x].max
|
15
|
+
y = [obj1.y, obj2.y].max
|
16
|
+
width = [obj1.x + obj1.width, obj2.x + obj2.width].min - x
|
17
|
+
height = [obj1.y + obj1.height, obj2.y + obj2.height].min - y
|
18
|
+
return Intersection.new(x, y, width, height)
|
19
|
+
end
|
20
|
+
|
21
|
+
def collistion_side_for(obj)
|
22
|
+
if @width >= @height
|
23
|
+
return obj.center.y > (@y + (@height/2)) ? :top : :bottom
|
24
|
+
else
|
25
|
+
return obj.center.x > (@x + (@width/2)) ? :left : :right
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "ostruct"
|
2
|
+
|
3
|
+
class Object
|
4
|
+
|
5
|
+
# Creates a new object from a hash. The hash keys will became attribute names, and
|
6
|
+
# the respective values will be the attribute's value.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
# obj = Object.from_hash({:a => 1, :b => 42})
|
10
|
+
# puts obj.a # 1
|
11
|
+
# puts obj.b # 42
|
12
|
+
#
|
13
|
+
def self.from_hash(hash)
|
14
|
+
OpenStruct.new(hash)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Nimo
|
2
|
+
|
3
|
+
# Resource loader.
|
4
|
+
#
|
5
|
+
class Resources
|
6
|
+
|
7
|
+
attr_reader :images, :fonts, :sounds
|
8
|
+
|
9
|
+
def initialize(game_window)
|
10
|
+
@game_window = game_window
|
11
|
+
@images = {}
|
12
|
+
@fonts = {}
|
13
|
+
@sounds = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def load_images(image_definitions)
|
17
|
+
image_definitions.each { |tag, definition| @images[tag] ||= create_image_from(definition) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def load_fonts(font_definitions)
|
21
|
+
font_definitions.each { |tag, definition| @fonts[tag] ||= Gosu::Font.new(@game_window, definition[:type], definition[:size]) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def load_sounds(sound_definitions)
|
25
|
+
sound_definitions.each { |tag, definition| @sounds[tag] ||= Gosu::Song.new(@game_window, definition[:filename]) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def method_missing(meth, *args, &blk)
|
29
|
+
super unless [:image, :sound, :font].include?(meth)
|
30
|
+
validate_and_return_resource(meth, args.shift)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def create_image_from(definition)
|
36
|
+
if definition.has_key?(:tile_dimension)
|
37
|
+
width = definition[:tile_dimension][0]
|
38
|
+
height = definition[:tile_dimension][1]
|
39
|
+
return Gosu::Image.load_tiles(@game_window, definition[:filename], width, height, false)
|
40
|
+
end
|
41
|
+
return Gosu::Image.new(@game_window, definition[:filename], 0)
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate_and_return_resource(resource_type, tag)
|
45
|
+
resource_list = self.send("#{resource_type.to_s}s")
|
46
|
+
raise "No #{resource_type.to_s} resource named '#{tag}'" unless resource_list.has_key?(tag)
|
47
|
+
resource_list[tag]
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|