moonpxi-nimo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/.document +5 -0
  2. data/.gitignore +5 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +15 -0
  5. data/Rakefile +47 -0
  6. data/VERSION +1 -0
  7. data/examples/bouncy.rb +101 -0
  8. data/examples/eventy.rb +19 -0
  9. data/examples/images/dg_classm32.png +0 -0
  10. data/examples/images/dg_dungeon32.png +0 -0
  11. data/examples/images/jeeklabs.png +0 -0
  12. data/examples/images/tcheco.png +0 -0
  13. data/examples/imagey.rb +82 -0
  14. data/examples/screeny.rb +80 -0
  15. data/examples/simplest.rb +23 -0
  16. data/examples/sounds/KDE-Sys-Log-Out.ogg +0 -0
  17. data/examples/sounds/k3b_error1.wav +0 -0
  18. data/examples/soundy.rb +40 -0
  19. data/examples/spritey.rb +90 -0
  20. data/lib/nimo.rb +35 -0
  21. data/lib/nimo/behavior/deflector.rb +39 -0
  22. data/lib/nimo/behavior/moveable.rb +33 -0
  23. data/lib/nimo/behavior/projectile.rb +23 -0
  24. data/lib/nimo/behavior/with_velocity.rb +28 -0
  25. data/lib/nimo/game_object.rb +58 -0
  26. data/lib/nimo/game_window.rb +59 -0
  27. data/lib/nimo/listeners/event_listener.rb +27 -0
  28. data/lib/nimo/listeners/input_listener.rb +70 -0
  29. data/lib/nimo/representations/image_representation.rb +18 -0
  30. data/lib/nimo/representations/object_representation.rb +67 -0
  31. data/lib/nimo/representations/quad_representation.rb +23 -0
  32. data/lib/nimo/representations/sprite_representation.rb +78 -0
  33. data/lib/nimo/representations/text_representation.rb +27 -0
  34. data/lib/nimo/screen.rb +103 -0
  35. data/lib/nimo/utils/game.rb +63 -0
  36. data/lib/nimo/utils/intersection.rb +30 -0
  37. data/lib/nimo/utils/object_extension.rb +17 -0
  38. data/lib/nimo/utils/resources.rb +51 -0
  39. data/nimo.gemspec +117 -0
  40. data/spec/nimo/behavior/deflector_spec.rb +51 -0
  41. data/spec/nimo/behavior/moveable_spec.rb +81 -0
  42. data/spec/nimo/behavior/projectile_spec.rb +34 -0
  43. data/spec/nimo/behavior/with_velocity_spec.rb +18 -0
  44. data/spec/nimo/game_object_spec.rb +169 -0
  45. data/spec/nimo/game_window_spec.rb +88 -0
  46. data/spec/nimo/listeners/event_listener_spec.rb +34 -0
  47. data/spec/nimo/listeners/input_listener_spec.rb +87 -0
  48. data/spec/nimo/representations/image_representation_spec.rb +41 -0
  49. data/spec/nimo/representations/object_representation_spec.rb +74 -0
  50. data/spec/nimo/representations/sprite_representation_spec.rb +73 -0
  51. data/spec/nimo/representations/text_representation_spec.rb +21 -0
  52. data/spec/nimo/screen_spec.rb +114 -0
  53. data/spec/nimo/utils/game_spec.rb +44 -0
  54. data/spec/nimo/utils/object_extension_spec.rb +21 -0
  55. data/spec/nimo/utils/resources_spec.rb +59 -0
  56. data/spec/spec_helper.rb +9 -0
  57. metadata +133 -0
@@ -0,0 +1,90 @@
1
+ #
2
+ # spritey.rb
3
+ #
4
+ # Demonstrate how to create sprites and animations.
5
+ #
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
8
+ require 'nimo'
9
+ include Gosu::Button
10
+
11
+ WINDOW_WIDTH = 512
12
+ WINDOW_HEIGHT = 480
13
+ WINDOW = {:x => 0, :y => 0, :width => WINDOW_WIDTH, :height => WINDOW_HEIGHT}
14
+
15
+ class Player < Nimo::GameObject
16
+ include Nimo::Behavior::WithVelocity
17
+
18
+ def initialize
19
+ super(:x => 0, :y => WINDOW_HEIGHT - 62, :width => 48, :height => 62, :speed => 5, :boundary => Object.from_hash(WINDOW))
20
+ end
21
+
22
+ def move_left
23
+ @velocity.x = -1
24
+ end
25
+
26
+ def move_right
27
+ @velocity.x = 1
28
+ end
29
+
30
+ def move
31
+ super
32
+ if y < @boundary.height - @height
33
+ @velocity.y += 0.01
34
+ else
35
+ @velocity.y = 0
36
+ end
37
+
38
+ @velocity.x = 0
39
+ end
40
+
41
+ def jump
42
+ # Cannot jump twice
43
+ if @y >= @boundary.height - @height
44
+ @velocity.y = -1
45
+ @y += @speed * @velocity.y
46
+ end
47
+ end
48
+ end
49
+
50
+
51
+ if __FILE__ == $PROGRAM_NAME
52
+ Nimo::Game("Spritey", WINDOW_WIDTH, WINDOW_HEIGHT) do
53
+ images :jeeklabs => { :filename => "examples/images/jeeklabs.png" },
54
+ :tcheco => { :filename => "examples/images/tcheco.png", :tile_dimension => [48, 62] }
55
+
56
+ screen :title do
57
+ quad :with => WINDOW.merge(:color => Gosu::white)
58
+ image :with => {:x => 116, :y => 190, :image => :jeeklabs}
59
+
60
+ any_key { go_to :game }
61
+ end
62
+
63
+ screen :game do
64
+ player_observer = Proc.new do |rep, obj|
65
+ rep.flip if obj.velocity.x < 0
66
+ rep.unflip if obj.velocity.x > 0
67
+
68
+ rep.change_to(:stopped) if obj.velocity.x == 0
69
+ rep.change_to(:walking) if obj.velocity.x != 0
70
+ rep.change_to(:jumping) if obj.velocity.y < 0
71
+ rep.change_to(:falling) if obj.velocity.y > 0
72
+ end
73
+
74
+ quad :with => WINDOW.merge(:color => Gosu::white)
75
+ sprite :for => Player.new, :with => {:image => :tcheco} do
76
+ always { move }
77
+ when_key(KbLeft) { move_left }
78
+ when_key(KbRight) { move_right }
79
+ when_key(KbUp) { jump }
80
+ with_animation(:stopped, [0])
81
+ with_animation(:walking, [1, 2, 3, 4])
82
+ with_animation(:jumping, [5, 6, 7], :loop => false)
83
+ with_animation(:falling, [8, 9], :loop => false)
84
+ with_observer(&player_observer)
85
+ end
86
+
87
+ when_key(KbEscape) { exit }
88
+ end
89
+ end
90
+ end
data/lib/nimo.rb ADDED
@@ -0,0 +1,35 @@
1
+ # Libraries
2
+ begin
3
+ # In case you use Gosu via RubyGems.
4
+ require "rubygems"
5
+ rescue LoadError
6
+ # In case you don't.
7
+ end
8
+
9
+ require "gosu"
10
+
11
+ # NIMO_DIR = File.expand_path(File.dirname(__FILE__) + "/nimo")
12
+ NIMO_DIR = "nimo"
13
+
14
+ require NIMO_DIR + "/utils/object_extension"
15
+ require NIMO_DIR + "/utils/intersection"
16
+ require NIMO_DIR + "/utils/resources"
17
+ require NIMO_DIR + "/utils/game"
18
+
19
+ require NIMO_DIR + "/listeners/input_listener"
20
+ require NIMO_DIR + "/listeners/event_listener"
21
+
22
+ require NIMO_DIR + "/game_object"
23
+ require NIMO_DIR + "/game_window"
24
+ require NIMO_DIR + "/screen"
25
+
26
+ require NIMO_DIR + "/behavior/with_velocity"
27
+ require NIMO_DIR + "/behavior/deflector"
28
+ require NIMO_DIR + "/behavior/projectile"
29
+ require NIMO_DIR + "/behavior/moveable"
30
+
31
+ require NIMO_DIR + "/representations/object_representation"
32
+ require NIMO_DIR + "/representations/quad_representation"
33
+ require NIMO_DIR + "/representations/image_representation"
34
+ require NIMO_DIR + "/representations/text_representation"
35
+ require NIMO_DIR + "/representations/sprite_representation"
@@ -0,0 +1,39 @@
1
+ module Nimo::Behavior
2
+
3
+ module Deflector
4
+
5
+ def deflect(projectile)
6
+ @collision_timeout ||= 0
7
+ @collision_timeout -= 1 if @collision_timeout > 0
8
+ return unless @collision_timeout.zero? && collide?(projectile)
9
+
10
+ case intersection(projectile).collistion_side_for(self)
11
+ when :top
12
+ projectile.velocity.y = -projectile.velocity.y.abs
13
+ when :bottom
14
+ projectile.velocity.y = projectile.velocity.y.abs
15
+ when :left
16
+ projectile.velocity.x = -projectile.velocity.x.abs
17
+ when :right
18
+ projectile.velocity.x = projectile.velocity.x.abs
19
+ end
20
+
21
+ projectile.velocity.adjust(deflection_modifier(projectile))
22
+ @deflect_action.call(projectile) if @deflect_action
23
+ @collision_timeout = 5
24
+ end
25
+
26
+ def when_deflect(&action)
27
+ @deflect_action = action
28
+ self
29
+ end
30
+
31
+ protected
32
+
33
+ def deflection_modifier(projectile)
34
+ 0
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,33 @@
1
+ module Nimo::Behavior
2
+
3
+ module Moveable
4
+
5
+ def initialize(*params)
6
+ @speed = 0
7
+ @boundary = nil
8
+ super(*params)
9
+ end
10
+
11
+ def move_left
12
+ @x -= @speed
13
+ @x = @boundary.x if @boundary && @x < @boundary.x
14
+ end
15
+
16
+ def move_right
17
+ @x += @speed
18
+ @x = @boundary.width - @width if @boundary && (@x + @width) > @boundary.width
19
+ end
20
+
21
+ def move_up
22
+ @y -= @speed
23
+ @y = @boundary.y if @boundary && @y < @boundary.y
24
+ end
25
+
26
+ def move_down
27
+ @y += @speed
28
+ @y = @boundary.height - @height if @boundary && (@y + @height) > @boundary.height
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,23 @@
1
+ module Nimo::Behavior
2
+
3
+ module Projectile
4
+ include WithVelocity
5
+
6
+ def initialize(*params)
7
+ @deflectors = []
8
+ super(*params)
9
+ end
10
+
11
+ def move
12
+ @deflectors.each { |deflector| deflector.deflect(self) }
13
+ super
14
+ end
15
+
16
+ def with_deflectors(*new_deflectors)
17
+ @deflectors += new_deflectors
18
+ @deflectors.flatten!
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,28 @@
1
+ module Nimo::Behavior
2
+
3
+ Velocity = Struct.new(:x, :y) do
4
+ def adjust(radian)
5
+ xl = Math::cos(radian)*self.x - Math::sin(radian)*self.y
6
+ yl = Math::sin(radian)*self.x + Math::cos(radian)*self.y
7
+ self.x = xl
8
+ self.y = yl
9
+ end
10
+ end
11
+
12
+ module WithVelocity
13
+ attr_reader :velocity
14
+
15
+ def initialize(*params)
16
+ @speed = 0
17
+ @velocity = Velocity.new(0.0, 0.0)
18
+ super(*params)
19
+ end
20
+
21
+ def move
22
+ @x += @speed * @velocity.x
23
+ @y += @speed * @velocity.y
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,58 @@
1
+ module Nimo
2
+
3
+ #
4
+ # Base game domain object containing position, dimension and state information. Any object that represents
5
+ # a game entity should extend this class.
6
+ #
7
+ # The position (:x and :y) and dimension (:width and :height) are used for visual representations and any
8
+ # useful game behavior.
9
+ #
10
+ class GameObject
11
+ attr_accessor :x, :y, :width, :height
12
+
13
+ def initialize(config_options = {})
14
+ configure_with({:x => 0, :y => 0, :width => 0, :height => 0}.merge(config_options))
15
+ @listeners = {}
16
+ end
17
+
18
+ def at(x, y)
19
+ @x = x
20
+ @y = y
21
+ end
22
+
23
+ def dimension(width, height)
24
+ @width = width
25
+ @height = height
26
+ end
27
+
28
+ # config_options is a hash that can take the following keys: :x, :y, :width, :height.
29
+ # The key restriction is not being enforced.
30
+ def configure_with(config_options)
31
+ config_options.each { |attribute, value| instance_variable_set("@#{attribute}", value) }
32
+ end
33
+
34
+ def collide?(obj)
35
+ !(obj.x > (@x + @width) || @x > (obj.x + obj.width) ||
36
+ obj.y > (@y + @height) || @y > (obj.y + obj.height))
37
+ end
38
+
39
+ def intersection(obj)
40
+ collide?(obj) ? Intersection.between(self, obj) : nil
41
+ end
42
+
43
+ def center
44
+ Object.from_hash(:x => @x + (@width/2), :y => @y + (@height/2))
45
+ end
46
+
47
+ def register_listener(event_type, listener)
48
+ @listeners[event_type] ||= []
49
+ @listeners[event_type] << listener
50
+ end
51
+
52
+ def notify(event_type)
53
+ @listeners[event_type].each { |listener| listener.notify(event_type) } if @listeners.has_key? event_type
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,59 @@
1
+ module Nimo
2
+
3
+ # Represents a game instance and provides access to the game screen and screen transition.
4
+ # It is an extension of Gosu::Window, thus implementing the update, draw and button_down hooks.
5
+ #
6
+ class GameWindow < Gosu::Window
7
+
8
+ attr_reader :current_screen
9
+
10
+ def initialize(name, width, height)
11
+ super(width, height, false)
12
+ self.caption = name
13
+
14
+ @screens = {}
15
+ @background_screens = []
16
+ end
17
+
18
+ def add_screen(name, screen)
19
+ @screens[name] = screen
20
+ go_to(name) if @screens.size == 1
21
+ end
22
+
23
+ # Switch to a Screen registered with the <tt>screen_name</tt>, notifying listeners of the :on_enter event.
24
+ #
25
+ def go_to(screen_name)
26
+ raise "There is no screen named #{screen_name}" unless @screens.has_key? screen_name
27
+ @current_screen = @screens[screen_name]
28
+ @current_screen.notify(:on_enter)
29
+ end
30
+
31
+ # Open a Screen registered with the <tt>screen_name</tt> as a menu. To dismiss the menu, use close_menu.
32
+ #
33
+ def open_menu(screen_name)
34
+ @background_screens << @current_screen
35
+ go_to(screen_name)
36
+ end
37
+
38
+ def close_menu
39
+ @current_screen = @background_screens.pop
40
+ end
41
+
42
+ # :section: Gosu::Window hooks
43
+
44
+ def update
45
+ @current_screen.update
46
+ end
47
+
48
+ def draw
49
+ @background_screens.each { |screen| screen.draw }
50
+ @current_screen.draw
51
+ end
52
+
53
+ def button_down(id)
54
+ @current_screen.button_down(id)
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,27 @@
1
+ module Nimo
2
+
3
+ # Listen for events and execute for actions on notification.
4
+ #
5
+ module EventListener
6
+
7
+ # Register an action to be executed when an event is notified.
8
+ #
9
+ def listen_to(event, &action)
10
+ events[event] = action
11
+ end
12
+
13
+ # Execute registered action for notified event.
14
+ #
15
+ def notify(event)
16
+ events[event].call if events.has_key?(event)
17
+ end
18
+
19
+ private
20
+
21
+ def events
22
+ @events ||= {}
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,70 @@
1
+ module Nimo
2
+
3
+ # Listen for inputs (key presses) and execute the corresponding actions on update.
4
+ #
5
+ module InputListener
6
+
7
+ # Register an action that will execute when the key is pressed.
8
+ # An options hash can be specified to customise the behavior. The options are:
9
+ # - <tt>:repeatable</tt> (defaults to true) - execute the action every update regardless if the key was already pressed in the previous update.
10
+ #
11
+ def when_key(key, options = {}, &action)
12
+ key_actions[key] = KeyAction.new(action, options)
13
+ end
14
+
15
+ # Register an action to be executed everytime a key is pressed.
16
+ #
17
+ def any_key(&action)
18
+ @any_key_action = action
19
+ end
20
+
21
+ # Gosu hook invoked anytime a button is pressed
22
+ #
23
+ def button_down(id) #:nodoc:
24
+ act_upon.instance_eval(&@any_key_action) if @any_key_action
25
+ end
26
+
27
+ def process_inputs(game_window)
28
+ key_actions.each do |key, key_action|
29
+ act_upon.instance_eval(&key_action.action) if key_action.should_execute?(game_window.button_down?(key))
30
+ end
31
+ end
32
+
33
+ protected
34
+
35
+ # Defines the object to be the context of the action. By default it is <tt>self</tt>.
36
+ #
37
+ def act_upon
38
+ self
39
+ end
40
+
41
+ private
42
+
43
+ def key_actions
44
+ @key_actions ||= {}
45
+ end
46
+
47
+
48
+ class KeyAction
49
+ attr_reader :action
50
+
51
+ def initialize(action, options)
52
+ @action = action
53
+ @options = {:repeatable => true}.merge(options)
54
+ @pressed_since = nil
55
+ end
56
+
57
+ def should_execute?(is_button_down)
58
+ should_execute = false
59
+ if is_button_down
60
+ should_execute = @pressed_since.nil? || @options[:repeatable]
61
+ @pressed_since = Time.now if @pressed_since.nil?
62
+ else
63
+ @pressed_since = nil
64
+ end
65
+ return should_execute
66
+ end
67
+
68
+ end
69
+ end
70
+ end