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.
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