lotu 0.1.11 → 0.1.12

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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.11
1
+ 0.1.12
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Hello world for lotu
4
+ # Here you will learn about:
5
+ # * Loading image resources
6
+ # * Binding keys to window and actors
7
+ # * How to display text on screen using a text box
8
+
9
+
10
+ LIB_PATH = File.join(File.dirname(__FILE__), '..', '..', 'lib', 'lotu.rb')
11
+ require File.expand_path(LIB_PATH)
12
+
13
+ include Gosu
14
+ include Lotu
15
+
16
+ # We want to move something around the screen so we create a new Actor
17
+ # subclass, set the image all instances of it are going to use and
18
+ # define the movement methods
19
+ class Lobo < Actor
20
+
21
+ def initialize(opts={})
22
+ # It's important to call super so we take advantage of automatic
23
+ # management for this object (being added to draw and update queue)
24
+ super
25
+ # Use the image which filename is "lobo_tuerto.png", and scale
26
+ # it's appearance to half width and half height
27
+ set_image 'lobo_tuerto.png', :factor_x => 0.5, :factor_y => 0.5
28
+ end
29
+
30
+ # Let's define some basic movement methods
31
+ def move_right; @x += 1 end
32
+ def move_left; @x -= 1 end
33
+ def move_up; @y -= 1 end
34
+ def move_down; @y += 1 end
35
+
36
+ end
37
+
38
+ # Let's subclass the Game class and write some code!
39
+ class Portraits < Game
40
+
41
+ def initialize
42
+ # This will call the hooks:
43
+ # load_resources, setup_systems, setup_input and setup_actors
44
+ # declared in the parent class
45
+ super
46
+ # When the Escape key is pressed, call the close method on
47
+ # instance of class Portraits, when D key is pressed turn on
48
+ # debug, we pass false into the array to tell the input system we
49
+ # don't want autofire on, else pass the number of milliseconds
50
+ # (zero and no array is the same)
51
+ set_keys(KbEscape => :close,
52
+ KbD => [:debug!, false])
53
+ end
54
+
55
+ # This method is called when we call super inside initialize
56
+ def load_resources
57
+ # From this file,
58
+ with_path_from_file(__FILE__) do
59
+ # go back one dir and search inside media/images
60
+ load_images '../media/images'
61
+ end
62
+ end
63
+
64
+ # This method is called when we call super inside initialize
65
+ def setup_actors
66
+ # Create a lobo in the middle of the screen
67
+ @lobo1 = Lobo.new(:x => width/2, :y => height/2)
68
+ # Map keys to some methods
69
+ @lobo1.set_keys(KbRight => :move_right,
70
+ KbLeft => :move_left,
71
+ KbUp => :move_up,
72
+ KbDown => :move_down)
73
+
74
+ # Rinse and repeat... but invert some keys
75
+ @lobo2 = Lobo.new(:x => width/2, :y => height/2)
76
+ @lobo2.set_keys(KbRight => :move_left,
77
+ KbLeft => :move_right,
78
+ KbUp => :move_up,
79
+ KbDown => :move_down)
80
+
81
+ # Create a TextBox so we can display a message on screen
82
+ @info = TextBox.new
83
+ # Add some text
84
+ @info.text("Hello world!")
85
+ # Add more text, but specify the color and size in pixels
86
+ @info.text("Move the portraits around with arrow keys", :size => 16, :color => 0xff33ccff)
87
+ end
88
+
89
+ end
90
+
91
+ # Create and show the app
92
+ Portraits.new.show
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # lotu's cursors
4
+ # Here you will learn about:
5
+ # * The Cursor class included in lotu
6
+ # * How to register events
7
+ # * How to use the systems provided by lotu
8
+
9
+ LIB_PATH = File.join(File.dirname(__FILE__), '..', '..', 'lib', 'lotu.rb')
10
+ require File.expand_path(LIB_PATH)
11
+
12
+ include Gosu
13
+ include Lotu
14
+
15
+ # Create a class for displaying something on screen
16
+ class Lobo < Actor
17
+ def initialize(opts={})
18
+ super
19
+ set_image 'lobo_tuerto.png', :width => 100
20
+ end
21
+
22
+ # Define a method for teleporting our instances around
23
+ def teleport(x, y)
24
+ @x, @y = x, y
25
+ end
26
+ end
27
+
28
+ class Cursors < Game
29
+ def initialize
30
+ # This will call the hooks:
31
+ # load_resources, setup_systems, setup_input and setup_actors
32
+ # declared in the parent class
33
+ super
34
+ # Custom setup methods for this class
35
+ setup_events
36
+ end
37
+
38
+ def load_resources
39
+ with_path_from_file(__FILE__) do
40
+ load_images '../media/images'
41
+ end
42
+ end
43
+
44
+ def setup_systems
45
+ # It's important to call super here to setup the InputSystem in
46
+ # the parent class
47
+ super
48
+ # To use the systems lotu provides, you just "use" them
49
+ # Let's activate the FPS system, so we can track the frames per
50
+ #second our app is pushing out
51
+ use(FpsSystem)
52
+ # Activate the stalker system to track how many objects of these
53
+ # classes are around
54
+ use(StalkerSystem, :stalk => [Actor, Cursor, TextBox, Lobo, Object])
55
+ end
56
+
57
+ # Setup some input handling for our Cursors app
58
+ def setup_input
59
+ set_keys(KbEscape => :close,
60
+ KbD => [:debug!, false],
61
+ KbT => [:toggle_text, false])
62
+ end
63
+
64
+ # Now onto define some events
65
+ def setup_events
66
+ # When the left mouse button is clicked (as defined below in
67
+ # setup_actors) call the teleport method in @lobo
68
+ @cursor1.on(:click) do |x,y|
69
+ @lobo.teleport(x,y)
70
+ end
71
+ # When the space bar is pressed (see below in setup_actors) call
72
+ # the teleport method in @lobo
73
+ @cursor2.on(:click) do |x,y|
74
+ @lobo.teleport(x,y)
75
+ end
76
+ end
77
+
78
+ def setup_actors
79
+ # Create a portrait to teleport around with "clicks"
80
+ @lobo = Lobo.new(:x => width/2, :y => height/2)
81
+
82
+ # The cursor class defines a method named (you guessed) "click",
83
+ # when this method is called, it fires an event named (again)
84
+ # "click", that's why we attached some code to that event in
85
+ # setup_events
86
+ @cursor1 = Cursor.new(:image => 'crosshair-1.png',
87
+ :keys => {MsLeft => [:click, false]},
88
+ :width => 100,
89
+ :rand_color => true,
90
+ :mode => :additive)
91
+ @cursor1.transform_angle(:init => 0, :end => 359, :duration => 10, :loop => true)
92
+
93
+ @cursor2 = Cursor.new(:image => 'crosshair-2.png',
94
+ :use_mouse => false,
95
+ :keys => {
96
+ KbSpace => [:click, false],
97
+ KbUp => :up,
98
+ KbDown => :down,
99
+ KbLeft => :left,
100
+ KbRight => :right},
101
+ :width => 100,
102
+ :rand_color => true,
103
+ :mode => :additive)
104
+ @cursor2.transform_angle(:init => 359, :end => 0, :duration => 1, :loop => true)
105
+
106
+ # Center @cursor2 vertically and move it to the right 3/4 of the
107
+ # screen
108
+ @cursor2.x = width*3/4
109
+ @cursor2.y = height/2
110
+
111
+ # Here we tell the TextBox to watch some objects, the TextBox will
112
+ # call the to_s method on the objects it's watching
113
+ # Create a TextBox with default option :size => 15
114
+ @info = TextBox.new(:size => 15)
115
+ @info.text("Press T to hide this text", :size => 24)
116
+ # Watch the FPS, so we get a nice FPS report on the screen
117
+ @info.watch(@systems[FpsSystem], :size => 20)
118
+ # Watch the Stalker system, so we know how many objects of the
119
+ # classes we specified up in setup_systems are around
120
+ @info.watch(@systems[StalkerSystem], :color => 0xff3ffccf)
121
+ # We can change the size for a specific line of text
122
+ @info.text("@cursor1 data:", :size => 20)
123
+ @info.text("move with Mouse | click with LeftMouseButton")
124
+ # And color
125
+ @info.watch(@cursor1, :color => @cursor1.color)
126
+
127
+ # Lets watch @cursor2 too
128
+ @info.text("@cursor2 data:", :size => 20)
129
+ @info.text("move with Arrow keys | click with Space")
130
+ @info.watch(@cursor2, :color => @cursor2.color)
131
+ @info.text("click to teleport the portrait!")
132
+ end
133
+
134
+ def toggle_text
135
+ @info.toggle!
136
+ end
137
+
138
+ end
139
+
140
+ # Create and start the game loop by showing the app
141
+ Cursors.new.show
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Here you will learn about:
4
+ # * The steering system
5
+ # * How to play animations
6
+ # * How to attach a text box to an actor
7
+
8
+ LIB_PATH = File.join(File.dirname(__FILE__), '..', '..', 'lib', 'lotu.rb')
9
+ require File.expand_path(LIB_PATH)
10
+
11
+ include Gosu
12
+ include Lotu
13
+
14
+ # Let's define a Missile class that will use a Steering system to
15
+ # control it's movement
16
+ class Missile < Actor
17
+ def initialize(opts={})
18
+ super
19
+ # Activate the steering system and pass the opts, since they might
20
+ # have some config info for the system
21
+ use(SteeringSystem, opts)
22
+ end
23
+
24
+ def teleport(x, y)
25
+ @pos.x, @pos.y = x, y
26
+ end
27
+ end
28
+
29
+ # The main app class
30
+ class SteeringMissiles < Game
31
+ def initialize
32
+ # This will call the hooks:
33
+ # load_resources, setup_systems, setup_input and setup_actors
34
+ # declared in the parent class
35
+ super
36
+ # Custom setup methods for this class
37
+ setup_events
38
+ end
39
+
40
+ # Let's load some images and animations, check out the animations
41
+ # directory, the animation there was created with:
42
+ # {Sprite Sheet Packer}[http://spritesheetpacker.codeplex.com/]
43
+ def load_resources
44
+ with_path_from_file(__FILE__) do
45
+ load_images '../media/images'
46
+ load_animations '../media/animations'
47
+ end
48
+ end
49
+
50
+ def setup_input
51
+ set_keys(KbEscape => :close,
52
+ MsRight => :teleport_big_missile_to_midscreen,
53
+ KbD => [:debug!, false],
54
+ KbT => [:toggle_missile_info, false])
55
+ end
56
+
57
+ def setup_systems
58
+ # It's important to call super here to setup the InputSystem
59
+ super
60
+ use(FpsSystem)
61
+ use(StalkerSystem, :stalk => [Actor, Missile, Vector2d, Object])
62
+ end
63
+
64
+ def setup_actors
65
+ @big_missile = Missile.new(:mass => 0.3, :max_speed => 100, :max_turn_rate => 140)
66
+ @big_missile.teleport(width/2, height/2)
67
+ @big_missile.activate(:evade)
68
+ @big_missile.play_animation('missile.png')
69
+
70
+ @little_missile = Missile.new
71
+ @little_missile.activate(:pursuit)
72
+ @little_missile.play_animation('missile.png', :fps => 60, :height => 20)
73
+
74
+ @cursor = Cursor.new(:image => 'crosshair-3.png',
75
+ :keys => {MsLeft => [:click, false]},
76
+ :rand_color => true)
77
+
78
+ @window_info = TextBox.new(:size => 15)
79
+ @window_info.text("Press T to hide this text", :size => 24)
80
+ @window_info.watch(@systems[FpsSystem], :size => 20)
81
+ @window_info.watch(@systems[StalkerSystem], :color => 0xff33ccff)
82
+ @window_info.watch(@cursor, :color => @cursor.color)
83
+ @window_info.text("Click to start the simulation", :color => 0xffffff00)
84
+ @window_info.text("One will pursuit while the other evades, right click to center evader on screen")
85
+
86
+ @missile_info = TextBox.new(:attach_to => @big_missile, :size => 14)
87
+ @missile_info.watch(@big_missile)
88
+ end
89
+
90
+ def setup_events
91
+ @cursor.on(:click) do |x,y|
92
+ @big_missile.pursuer = @little_missile
93
+ @little_missile.evader = @big_missile
94
+ end
95
+ end
96
+
97
+ def teleport_big_missile_to_midscreen
98
+ @big_missile.pos.x = width/2
99
+ @big_missile.pos.y = height/2
100
+ end
101
+
102
+ def toggle_missile_info
103
+ @missile_info.toggle!
104
+ @window_info.toggle!
105
+ end
106
+ end
107
+
108
+ # Create and start the application
109
+ SteeringMissiles.new.show
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Here you will learn about:
4
+ # * The steering system pursuit, and evade_multiple behaviors
5
+ # * How to play animations
6
+ # * How to attach a text box to an actor
7
+
8
+ LIB_PATH = File.join(File.dirname(__FILE__), '..', '..', 'lib', 'lotu.rb')
9
+ require File.expand_path(LIB_PATH)
10
+
11
+ include Gosu
12
+ include Lotu
13
+
14
+ # Let's define a Missile class that will use a Steering system to
15
+ # control it's movement
16
+ class Missile < Actor
17
+ def initialize(opts={})
18
+ super
19
+ # Activate the steering system and pass the opts, since they might
20
+ # have some config info for the system
21
+ use(SteeringSystem, opts)
22
+ end
23
+
24
+ def teleport(x, y)
25
+ @pos.x, @pos.y = x, y
26
+ end
27
+ end
28
+
29
+ # The main app class
30
+ class EvadeMultiple < Game
31
+ def initialize
32
+ # This will call the hooks:
33
+ # load_resources, setup_systems, setup_input and setup_actors
34
+ # declared in the parent class
35
+ super
36
+ # Custom setup methods for this class
37
+ setup_events
38
+ end
39
+
40
+ # Let's load some images and animations, check out the animations
41
+ # directory, the animation there was created with:
42
+ # {Sprite Sheet Packer}[http://spritesheetpacker.codeplex.com/]
43
+ def load_resources
44
+ with_path_from_file(__FILE__) do
45
+ load_images '../media/images'
46
+ load_animations '../media/animations'
47
+ end
48
+ end
49
+
50
+ def setup_input
51
+ set_keys(KbEscape => :close,
52
+ MsRight => :teleport_big_missile_to_midscreen,
53
+ KbD => [:debug!, false],
54
+ KbT => [:toggle_info, false],
55
+ KbSpace => [:pause!, false])
56
+ end
57
+
58
+ def setup_systems
59
+ # It's important to call super here to setup the InputSystem
60
+ super
61
+ use(FpsSystem)
62
+ use(StalkerSystem, :stalk => [Actor, Missile, Vector2d, Object])
63
+ end
64
+
65
+ def setup_actors
66
+ @big_missile = Missile.new(:mass => 0.3, :max_speed => 100, :max_turn_rate => 140)
67
+ @big_missile.teleport(width/2, height/2)
68
+ @big_missile.activate(:evade_multiple)
69
+ @big_missile.play_animation('missile.png')
70
+
71
+ @little_missiles = []
72
+ 5.times do |i|
73
+ @little_missiles << Missile.new(:x => 200 - rand(400), :y => 200 - rand(400), :rand_color => true)
74
+ @little_missiles[i].activate(:pursuit)
75
+ @little_missiles[i].play_animation('missile.png', :fps => 60+rand(60), :height => 10+rand(30))
76
+ end
77
+
78
+ @cursor = Cursor.new(:image => 'crosshair-3.png',
79
+ :keys => {MsLeft => [:click, false]},
80
+ :rand_color => true)
81
+
82
+ @window_info = TextBox.new(:size => 15)
83
+ @window_info.text("Press T to hide this text", :size => 24)
84
+ @window_info.watch(@systems[FpsSystem], :size => 20)
85
+ @window_info.watch(@systems[StalkerSystem], :color => 0xff33ccff)
86
+ @window_info.watch(@cursor, :color => @cursor.color)
87
+ @window_info.text("Click to start the simulation", :color => 0xffffff00)
88
+ @window_info.text("Little missiles will pursuit while the big one evades, right click to center big one on screen")
89
+
90
+ @missile_info = TextBox.new(:attach_to => @big_missile, :size => 14)
91
+ @missile_info.watch(@big_missile)
92
+ end
93
+
94
+ def setup_events
95
+ @cursor.on(:click) do |x,y|
96
+ @big_missile.pursuers = @little_missiles
97
+ @little_missiles.each do |lil_missile|
98
+ lil_missile.evader = @big_missile
99
+ end
100
+ end
101
+ end
102
+
103
+ def teleport_big_missile_to_midscreen
104
+ @big_missile.pos.x = width/2
105
+ @big_missile.pos.y = height/2
106
+ end
107
+
108
+ def toggle_info
109
+ @missile_info.toggle!
110
+ @window_info.toggle!
111
+ end
112
+
113
+ end
114
+
115
+ # Create and start the application
116
+ EvadeMultiple.new.show
data/lib/lotu/actor.rb CHANGED
@@ -27,12 +27,16 @@ module Lotu
27
27
  @parent.manage_me(self)
28
28
  set_image(opts[:image], opts) if opts[:image]
29
29
  parse_options(opts)
30
+ @color = rand_color if opts[:rand_color]
30
31
  @systems = {}
31
32
 
33
+ set_keys(opts[:keys]) unless opts[:keys].nil?
34
+
32
35
  # Add extra functionality
33
36
  self.extend Eventful
34
37
  self.extend Collidable
35
38
  use(AnimationSystem)
39
+ use(TransformationSystem)
36
40
  end
37
41
 
38
42
  # Easy access to delta-time
@@ -50,36 +54,60 @@ module Lotu
50
54
  @factor_x = opts[:factor_x] || @factor_x
51
55
  @factor_y = opts[:factor_y] || @factor_y
52
56
  @color = opts[:color] || @color
57
+ if @color.kind_of?(Integer)
58
+ @color = Gosu::Color.new(opts[:color])
59
+ end
53
60
  @mode = opts[:mode] || @mode
54
61
  end
55
62
 
63
+ def rand_color
64
+ Gosu::Color.from_hsv(rand(360), 1, 1)
65
+ end
66
+
56
67
  def set_image(image, opts={})
57
68
  @image = @parent.image(image)
58
- puts "Image \"#{image}\" not found".red if @image.nil?
69
+ if @image.nil?
70
+ puts "Image \"#{image}\" not found".red
71
+ return
72
+ end
59
73
  parse_options(opts)
60
- @width = opts[:width] || @image.width
61
- @height = opts[:height] || @image.height
74
+ adjust_width_and_height(opts)
62
75
  calc_zoom
63
76
  end
64
77
 
65
78
  def set_gosu_image(image, opts={})
66
79
  @image = image
67
80
  parse_options(opts)
68
- @width = opts[:width] || @image.width
69
- @height = opts[:height] || @image.height
81
+ adjust_width_and_height(opts)
70
82
  calc_zoom
71
83
  end
72
84
 
73
85
  def width=(width)
74
- @width = width
86
+ @width = Float(width)
75
87
  calc_zoom
76
88
  end
77
89
 
78
90
  def height=(height)
79
- @height = height
91
+ @height = Float(height)
80
92
  calc_zoom
81
93
  end
82
94
 
95
+ def adjust_width_and_height(opts)
96
+ if(opts[:width] && opts[:height])
97
+ @width = Float(opts[:width])
98
+ @height = Float(opts[:height])
99
+ elsif(opts[:width])
100
+ @width = Float(opts[:width])
101
+ @height = @width * @image.height / @image.width
102
+ elsif(opts[:height])
103
+ @height = Float(opts[:height])
104
+ @width = @height * @image.width / @image.height
105
+ else
106
+ @width = Float(@image.width)
107
+ @height = Float(@image.height)
108
+ end
109
+ end
110
+
83
111
  def calc_zoom
84
112
  @zoom_x = Float(@width)/@image.width
85
113
  @zoom_y = Float(@height)/@image.height
@@ -97,8 +125,10 @@ module Lotu
97
125
  end
98
126
 
99
127
  def draw
100
- @image.draw_rot(@x, @y, @z, @angle, @center_x, @center_y, @factor_x*@zoom_x, @factor_y*@zoom_y, @color, @mode) unless @image.nil?
101
- draw_debug if $lotu.debug? unless @image.nil?
128
+ unless @image.nil?
129
+ @image.draw_rot(@x, @y, @z, @angle, @center_x, @center_y, @factor_x*@zoom_x, @factor_y*@zoom_y, @color, @mode)
130
+ draw_debug if $lotu.debug?
131
+ end
102
132
  end
103
133
 
104
134
  def draw_debug
data/lib/lotu/cursor.rb CHANGED
@@ -7,7 +7,7 @@ module Lotu
7
7
  def initialize(opts={})
8
8
  default_opts = {
9
9
  :use_mouse => true,
10
- :speed => 100,
10
+ :speed => 300,
11
11
  :x => $lotu.width/2,
12
12
  :y => $lotu.height/2
13
13
  }
@@ -18,10 +18,10 @@ module Lotu
18
18
  @clicked_x = @clicked_y = 0
19
19
  @speed = opts[:speed]
20
20
  @use_mouse = opts[:use_mouse]
21
- set_keys(opts[:keys]) unless opts[:keys].nil?
22
21
  end
23
22
 
24
23
  def update
24
+ super
25
25
  if @use_mouse
26
26
  @x = $lotu.mouse_x
27
27
  @y = $lotu.mouse_y
data/lib/lotu/game.rb CHANGED
@@ -7,12 +7,18 @@ module Lotu
7
7
 
8
8
  include SystemUser
9
9
 
10
- def initialize(params={})
11
- super(800, 600, false)
10
+ def initialize(opts={})
11
+ default_opts = {
12
+ :width => 1024,
13
+ :height => 768,
14
+ :fullscreen => false
15
+ }
16
+ opts = default_opts.merge!(opts)
17
+ super(opts[:width], opts[:height], opts[:fullscreen])
12
18
 
13
19
  # Handy global window variable
14
20
  $lotu = self
15
- @debug = false
21
+ @debug = opts[:debug] || false
16
22
  setup_containers
17
23
 
18
24
  # For timer initialization
@@ -27,6 +33,14 @@ module Lotu
27
33
  setup_input
28
34
  end
29
35
 
36
+ def pause!
37
+ @pause = !@pause
38
+ end
39
+
40
+ def paused?
41
+ @pause
42
+ end
43
+
30
44
  def debug!
31
45
  @debug = !@debug
32
46
  end
@@ -75,7 +89,7 @@ module Lotu
75
89
  # Update each actor
76
90
  @update_queue.each do |actor|
77
91
  actor.update
78
- end
92
+ end unless paused?
79
93
  end
80
94
 
81
95
  # Main draw loop
@@ -140,24 +154,34 @@ module Lotu
140
154
  end
141
155
 
142
156
  def load_images(path)
157
+ count = 0
143
158
  with_files(/\.png|\.jpg|\.bmp/, path) do |file_name, file_path|
159
+ count += 1
144
160
  @images[file_name] = Gosu::Image.new($lotu, file_path)
145
161
  end
162
+ puts "\n#{count} image(s) loaded."
146
163
  end
147
164
 
148
165
  def load_sounds(path)
166
+ count = 0
149
167
  with_files(/\.ogg|\.mp3|\.wav/, path) do |file_name, file_path|
168
+ count += 1
150
169
  @sounds[file_name] = Gosu::Sample.new($lotu, file_path)
151
170
  end
171
+ puts "\n#{count} sounds(s) loaded."
152
172
  end
153
173
 
154
174
  def load_songs(path)
175
+ count = 0
155
176
  with_files(/\.ogg|\.mp3|\.wav/, path) do |file_name, file_path|
177
+ count += 1
156
178
  @songs[file_name] = Gosu::Song.new($lotu, file_path)
157
179
  end
180
+ puts "\n#{count} song(s) loaded."
158
181
  end
159
182
 
160
183
  def load_animations(path)
184
+ count = 0
161
185
  coords = Hash.new{|h,k| h[k] = []}
162
186
 
163
187
  with_files(/\.txt/, path) do |file_name, file_path|
@@ -167,14 +191,17 @@ module Lotu
167
191
  coords[name] << line.scan(/\d+/).map!(&:to_i)
168
192
  end
169
193
  end
194
+ false
170
195
  end
171
196
 
172
197
  with_files(/\.png|\.jpg|\.bmp/, path) do |file_name, file_path|
173
198
  name, extension = file_name.split('.')
199
+ count += 1 if coords[name]
174
200
  coords[name].each do |index, x, y, width, height|
175
201
  @animations[file_name] << Gosu::Image.new($lotu, file_path, true, x, y, width, height)
176
202
  end
177
203
  end
204
+ puts "\n#{count} animation(s) loaded."
178
205
  end
179
206
 
180
207
  def with_path_from_file(path, &blk)
@@ -184,20 +211,17 @@ module Lotu
184
211
 
185
212
  def with_files(regexp, path)
186
213
  path = File.expand_path(File.join(@path, path))
187
- puts "\nLoading from: #{path}"
214
+ puts "\nLoading from: #{path}".blue if @debug
188
215
 
189
- count = 0
190
216
  Dir.entries(path).grep(regexp).each do |entry|
191
217
  begin
192
- yield(entry, File.join(path, entry))
193
- count += 1
194
- print '.'.green
218
+ report = yield(entry, File.join(path, entry))
219
+ print '.'.green if report
195
220
  rescue Exception => e
196
221
  print '.'.red
197
222
  puts e, File.join(path, entry) if @debug
198
223
  end
199
224
  end
200
- puts "\n#{count} file(s) loaded."
201
225
  end
202
226
 
203
227
  end
@@ -135,6 +135,23 @@ module Lotu
135
135
  return flee
136
136
  end
137
137
 
138
+ def evade_multiple
139
+ return @zero if @user.pursuers.empty?
140
+ combined_velocities = Vector2d.new
141
+ combined_positions = Vector2d.new
142
+ @user.pursuers.each do |p|
143
+ combined_velocities += p.vel
144
+ combined_positions += p.pos
145
+ end
146
+ combined_velocities /= @user.pursuers.length
147
+ combined_positions /= @user.pursuers.length
148
+ to_pursuers = combined_positions - @user.pos
149
+ look_ahead_time = to_pursuers.length / (@user.max_speed + combined_velocities.length)
150
+ predicted_position = combined_positions + combined_velocities * look_ahead_time
151
+ @user.target = combined_positions
152
+ return flee
153
+ end
154
+
138
155
  # TODO: Fix wander
139
156
  def wander
140
157
  wander_jitter = 10
@@ -166,9 +183,11 @@ module Lotu
166
183
  attr_accessor :mass, :pos, :heading, :vel, :accel,
167
184
  :max_speed, :max_turn_rate, :max_force,
168
185
  :wander_radius, :wander_distance, :wander_target,
169
- :target, :evader, :pursuer
186
+ :target, :evader, :pursuer, :pursuers
170
187
  end
171
188
 
189
+ # TODO: move these inside the SteeringSystem?
190
+ # and just delegate with accessors?
172
191
  # Some defaults
173
192
  @pos = Vector2d.new(@x, @y)
174
193
  offset_x = Gosu.offset_x(@angle, 1)
@@ -177,6 +196,13 @@ module Lotu
177
196
  @vel = Vector2d.new
178
197
  @accel = Vector2d.new
179
198
  @wander_target = Vector2d.new
199
+ @pursuers = []
200
+
201
+ @colors = {
202
+ :position => 0xff666666,
203
+ :heading => 0xffff0000,
204
+ :target => rand_color
205
+ }
180
206
  end
181
207
 
182
208
  def activate(behavior)
@@ -191,15 +217,11 @@ module Lotu
191
217
  @heading.facing_to?(@target - @pos)
192
218
  end
193
219
 
194
- def draw
195
- super
196
- draw_debug if $lotu.debug?
197
- end
198
-
199
220
  def draw_debug
200
- $lotu.draw_line(0, 0, 0xff999999, @pos.x, @pos.y, 0xff333333)
201
- $lotu.draw_line(@pos.x, @pos.y, 0xffffffff, (@pos + @heading*50).x, (@pos+@heading*50).y, 0xffff0000)
202
- $lotu.draw_line(@pos.x, @pos.y, 0xffffffff, @target.x, @target.y, 0xff00ff00) if @target
221
+ super
222
+ $lotu.draw_line(0, 0, @colors[:position], @pos.x, @pos.y, @colors[:position])
223
+ $lotu.draw_line(@pos.x, @pos.y, @colors[:heading], (@pos + @heading*50).x, (@pos+@heading*50).y, @colors[:heading])
224
+ $lotu.draw_line(@pos.x, @pos.y, @colors[:target], @target.x, @target.y, @colors[:target]) if @target
203
225
  end
204
226
 
205
227
  # to_s utility methods
@@ -0,0 +1,122 @@
1
+ module Lotu
2
+ class TransformationSystem < System
3
+
4
+ def initialize(user, opts={})
5
+ super
6
+ user.extend(UserMethods)
7
+ @transformations = []
8
+ @tagged_for_deletion = []
9
+ end
10
+
11
+ def transform(object, property, opts)
12
+ transformation = {
13
+ :object => object,
14
+ :property_getter => property,
15
+ :property_setter => "#{property}=",
16
+ :accum_time => 0,
17
+ :calc => 0,
18
+ :init => opts[:init],
19
+ :end => opts[:end],
20
+ :duration => opts[:duration] || 1,
21
+ :start_in => opts[:start_in] || 0,
22
+ :on_result => opts[:on_result],
23
+ :loop => opts[:loop]
24
+ }
25
+ @transformations << transformation
26
+ end
27
+
28
+ def update
29
+ @transformations.each do |t|
30
+ t[:accum_time] += dt
31
+ if t[:accum_time] > t[:start_in]
32
+ step = (t[:end] - t[:init])/t[:duration] * dt
33
+ t[:calc] += step
34
+ if step > 0
35
+ if t[:init] + t[:calc] > t[:end]
36
+ if t[:loop]
37
+ t[:calc] = 0
38
+ else
39
+ t[:calc] = t[:end] - t[:init]
40
+ tag_for_deletion(t)
41
+ end
42
+ end
43
+ else
44
+ if t[:init] + t[:calc] < t[:end]
45
+ if t[:loop]
46
+ t[:calc] = 0
47
+ else
48
+ t[:calc] = t[:end] - t[:init]
49
+ tag_for_deletion(t)
50
+ end
51
+ end
52
+ end
53
+ value = t[:init] + t[:calc]
54
+ value = value.send(t[:on_result]) if t[:on_result]
55
+ t[:object].send(t[:property_setter], value)
56
+ end
57
+ end
58
+
59
+ @tagged_for_deletion.each do |to_delete|
60
+ @transformations.delete(to_delete)
61
+ end.clear
62
+ end
63
+
64
+ def tag_for_deletion(transform)
65
+ @tagged_for_deletion << transform
66
+ end
67
+
68
+ def to_s
69
+ ["@transformations.length #{@transformations.length}",
70
+ "@tagged_for_deletion.length #{@tagged_for_deletion.length}"]
71
+ end
72
+
73
+ module UserMethods
74
+ def transform(object, property, opts)
75
+ @systems[TransformationSystem].transform(object, property, opts)
76
+ end
77
+
78
+ # Image helpers
79
+ def transform_angle(opts)
80
+ transform(self, :angle, opts)
81
+ end
82
+
83
+ def transform_width(opts)
84
+ transform(self, :width, opts)
85
+ end
86
+
87
+ def transform_height(opts)
88
+ transform(self, :height, opts)
89
+ end
90
+
91
+ # Color helpers
92
+ def transform_alpha(opts)
93
+ transform(@color, :alpha, opts.merge!(:on_result => :to_i))
94
+ end
95
+
96
+ def transform_red(opts)
97
+ transform(@color, :red, opts.merge!(:on_result => :to_i))
98
+ end
99
+
100
+ def transform_green(opts)
101
+ transform(@color, :green, opts.merge!(:on_result => :to_i))
102
+ end
103
+
104
+ def transform_blue(opts)
105
+ transform(@color, :blue, opts.merge!(:on_result => :to_i))
106
+ end
107
+
108
+ def transform_hue(opts)
109
+ transform(@color, :hue, opts)
110
+ end
111
+
112
+ def transform_saturation(opts)
113
+ transform(@color, :saturation, opts)
114
+ end
115
+
116
+ def transform_value(opts)
117
+ transform(@color, :value, opts)
118
+ end
119
+ end
120
+
121
+ end
122
+ end
data/lib/lotu/text_box.rb CHANGED
@@ -13,21 +13,32 @@ module Lotu
13
13
  @watch_list = []
14
14
  @size = opts[:size]
15
15
  @attached_to = opts[:attach_to]
16
+ @hiding = false
16
17
  end
17
18
 
18
- def text(text, opts={})
19
- watch(text, opts)
19
+ def hide!
20
+ @hiding = true
21
+ end
22
+
23
+ def show!
24
+ @hiding = false
25
+ end
26
+
27
+ def toggle!
28
+ @hiding = !@hiding
20
29
  end
21
30
 
22
31
  def watch(subject, opts={})
23
32
  @watch_list << [subject, opts]
24
33
  end
34
+ alias :text :watch
25
35
 
26
36
  def attach_to(actor)
27
37
  @attached_to = actor
28
38
  end
29
39
 
30
40
  def update
41
+ return if @hiding
31
42
  unless @attached_to.nil?
32
43
  @x = @attached_to.x + @attached_to.image.width / 2
33
44
  @y = @attached_to.y - @attached_to.image.height / 2
@@ -35,6 +46,7 @@ module Lotu
35
46
  end
36
47
 
37
48
  def draw
49
+ return if @hiding
38
50
  pos_y = 0
39
51
  @watch_list.each do |watched, opts|
40
52
  my_size = opts[:size] || @size
data/lib/lotu.rb CHANGED
@@ -5,4 +5,4 @@ require 'gosu'
5
5
  %w{vector2d string}.each{|file| require "misc/#{file}"}
6
6
  %w{system_user collidable controllable eventful}.each{|file| require "behaviors/#{file}"}
7
7
  %w{game system actor cursor text_box}.each{|file| require file}
8
- %w{animation_system input_system stalker_system fps_system collision_system steering_system}.each{|file| require "systems/#{file}"}
8
+ %w{transformation_system animation_system input_system stalker_system fps_system collision_system steering_system}.each{|file| require "systems/#{file}"}
data/lotu.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{lotu}
8
- s.version = "0.1.11"
8
+ s.version = "0.1.12"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["lobo_tuerto"]
12
- s.date = %q{2010-03-27}
12
+ s.date = %q{2010-03-28}
13
13
  s.description = %q{lotu aims to bring an agile and simple game development framework to life. It provides useful abstractions so you can concentrate on developing your game.}
14
14
  s.email = %q{dev@lobotuerto.com}
15
15
  s.extra_rdoc_files = [
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
24
24
  "README.rdoc",
25
25
  "Rakefile",
26
26
  "VERSION",
27
- "examples/hello_world/hello_world.rb",
27
+ "examples/hello_world/moving_portraits.rb",
28
28
  "examples/media/animations/missile.png",
29
29
  "examples/media/animations/missile.txt",
30
30
  "examples/media/images/cohete-big.png",
@@ -36,8 +36,9 @@ Gem::Specification.new do |s|
36
36
  "examples/media/images/lobo_tuerto.png",
37
37
  "examples/media/images/missile-med.png",
38
38
  "examples/media/images/silo.png",
39
- "examples/mouse_pointer/mouse_pointer.rb",
40
- "examples/steering_behaviors/steering.rb",
39
+ "examples/screen_cursor/mouse_and_keyboard_cursors.rb",
40
+ "examples/steering_behaviors/pursuit_and_evade.rb",
41
+ "examples/steering_behaviors/pursuit_and_evade_multiple.rb",
41
42
  "lib/lotu.rb",
42
43
  "lib/lotu/actor.rb",
43
44
  "lib/lotu/behaviors/collidable.rb",
@@ -55,6 +56,7 @@ Gem::Specification.new do |s|
55
56
  "lib/lotu/systems/input_system.rb",
56
57
  "lib/lotu/systems/stalker_system.rb",
57
58
  "lib/lotu/systems/steering_system.rb",
59
+ "lib/lotu/systems/transformation_system.rb",
58
60
  "lib/lotu/text_box.rb",
59
61
  "lotu.gemspec",
60
62
  "test/actor_test.rb"
@@ -66,9 +68,10 @@ Gem::Specification.new do |s|
66
68
  s.summary = %q{A simple, agile Ruby game development framework.}
67
69
  s.test_files = [
68
70
  "test/actor_test.rb",
69
- "examples/steering_behaviors/steering.rb",
70
- "examples/hello_world/hello_world.rb",
71
- "examples/mouse_pointer/mouse_pointer.rb"
71
+ "examples/steering_behaviors/pursuit_and_evade.rb",
72
+ "examples/steering_behaviors/pursuit_and_evade_multiple.rb",
73
+ "examples/hello_world/moving_portraits.rb",
74
+ "examples/screen_cursor/mouse_and_keyboard_cursors.rb"
72
75
  ]
73
76
 
74
77
  if s.respond_to? :specification_version then
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 11
9
- version: 0.1.11
8
+ - 12
9
+ version: 0.1.12
10
10
  platform: ruby
11
11
  authors:
12
12
  - lobo_tuerto
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-03-27 00:00:00 -06:00
17
+ date: 2010-03-28 00:00:00 -06:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -48,7 +48,7 @@ files:
48
48
  - README.rdoc
49
49
  - Rakefile
50
50
  - VERSION
51
- - examples/hello_world/hello_world.rb
51
+ - examples/hello_world/moving_portraits.rb
52
52
  - examples/media/animations/missile.png
53
53
  - examples/media/animations/missile.txt
54
54
  - examples/media/images/cohete-big.png
@@ -60,8 +60,9 @@ files:
60
60
  - examples/media/images/lobo_tuerto.png
61
61
  - examples/media/images/missile-med.png
62
62
  - examples/media/images/silo.png
63
- - examples/mouse_pointer/mouse_pointer.rb
64
- - examples/steering_behaviors/steering.rb
63
+ - examples/screen_cursor/mouse_and_keyboard_cursors.rb
64
+ - examples/steering_behaviors/pursuit_and_evade.rb
65
+ - examples/steering_behaviors/pursuit_and_evade_multiple.rb
65
66
  - lib/lotu.rb
66
67
  - lib/lotu/actor.rb
67
68
  - lib/lotu/behaviors/collidable.rb
@@ -79,6 +80,7 @@ files:
79
80
  - lib/lotu/systems/input_system.rb
80
81
  - lib/lotu/systems/stalker_system.rb
81
82
  - lib/lotu/systems/steering_system.rb
83
+ - lib/lotu/systems/transformation_system.rb
82
84
  - lib/lotu/text_box.rb
83
85
  - lotu.gemspec
84
86
  - test/actor_test.rb
@@ -115,6 +117,7 @@ specification_version: 3
115
117
  summary: A simple, agile Ruby game development framework.
116
118
  test_files:
117
119
  - test/actor_test.rb
118
- - examples/steering_behaviors/steering.rb
119
- - examples/hello_world/hello_world.rb
120
- - examples/mouse_pointer/mouse_pointer.rb
120
+ - examples/steering_behaviors/pursuit_and_evade.rb
121
+ - examples/steering_behaviors/pursuit_and_evade_multiple.rb
122
+ - examples/hello_world/moving_portraits.rb
123
+ - examples/screen_cursor/mouse_and_keyboard_cursors.rb
@@ -1,77 +0,0 @@
1
- #!/usr/bin/env ruby
2
- LIB_PATH = File.join(File.dirname(__FILE__), '..', '..', 'lib', 'lotu.rb')
3
- require File.expand_path(LIB_PATH)
4
-
5
- include Gosu::Button
6
- include Lotu
7
-
8
- class MovingRuby < Actor
9
-
10
- def initialize(opts={})
11
- super
12
- # Use the image which filename is CptnRuby Gem.png
13
- set_image 'lobo_tuerto.png', :factor_x => 0.5, :factor_y => 0.5
14
- end
15
-
16
- # Let's define some basic movement methods
17
- def move_right
18
- @x += 1
19
- end
20
-
21
- def move_left
22
- @x -= 1
23
- end
24
-
25
- def move_up
26
- @y -= 1
27
- end
28
-
29
- def move_down
30
- @y += 1
31
- end
32
-
33
- end
34
-
35
- class Example < Game
36
-
37
- def initialize
38
- # This will call the hooks:
39
- # load_resources, setup_systems and setup_actors
40
- # declared in the parent class
41
- super
42
- # When the Escape key is pressed, call the close method on class Example
43
- set_keys(KbEscape => :close,
44
- KbD => [:debug!, false])
45
- end
46
-
47
- def load_resources
48
- # From this file,
49
- with_path_from_file(__FILE__) do
50
- # go back one dir and search inside media/
51
- load_images '../media/images'
52
- end
53
- end
54
-
55
- def setup_actors
56
- # Create a ruby in the middle of the screen
57
- @ruby = MovingRuby.new(:x => width/2, :y => height/2)
58
- # Map keys to some methods
59
- @ruby.set_keys(KbRight => :move_right,
60
- KbLeft => :move_left,
61
- KbUp => :move_up,
62
- KbDown => :move_down)
63
- @ruby2 = MovingRuby.new(:x => width/2, :y => height/2)
64
- # Map keys to some methods
65
- @ruby2.set_keys(KbRight => :move_left,
66
- KbLeft => :move_right,
67
- KbUp => :move_up,
68
- KbDown => :move_down)
69
- # Create a TextBox so we can display a message on screen
70
- @info = TextBox.new
71
- @info.text("Hello world!")
72
- @info.text("Move around with arrow keys", :size => 16, :color => 0xff33ccff)
73
- end
74
-
75
- end
76
-
77
- Example.new.show
@@ -1,92 +0,0 @@
1
- #!/usr/bin/env ruby
2
- LIB_PATH = File.join(File.dirname(__FILE__), '..', '..', 'lib', 'lotu.rb')
3
- require File.expand_path(LIB_PATH)
4
-
5
- include Gosu::Button
6
- include Lotu
7
-
8
- class WarpingRuby < Actor
9
- def initialize(opts={})
10
- super
11
- set_image 'lobo_tuerto.png', :factor_x => 0.3, :factor_y => 0.3
12
- end
13
-
14
- def warp(x, y)
15
- @x, @y = x, y
16
- end
17
- end
18
-
19
- class Example < Game
20
- def initialize
21
- # This will call the hooks:
22
- # load_resources, setup_systems and setup_actors
23
- # declared in the parent class
24
- super
25
- # Custom setup methods for this class
26
- setup_events
27
- end
28
-
29
- def load_resources
30
- with_path_from_file(__FILE__) do
31
- load_images '../media/images'
32
- end
33
- end
34
-
35
- def setup_systems
36
- # It's important to call super here to setup the InputSystem
37
- super
38
- use(FpsSystem)
39
- use(StalkerSystem, :stalk => [Actor, Cursor, TextBox, WarpingRuby, Object])
40
- end
41
-
42
- def setup_input
43
- set_keys(KbEscape => :close,
44
- KbD => [:debug!, false])
45
- end
46
-
47
- def setup_events
48
- @cursor1.on(:click) do |x,y|
49
- @ruby.warp(x,y)
50
- end
51
- @cursor2.on(:click) do |x,y|
52
- @ruby.warp(x,y)
53
- end
54
- end
55
-
56
- def setup_actors
57
- @ruby = WarpingRuby.new(:x => width/2, :y => height/2)
58
- @cursor1 = Cursor.new(:image => 'crosshair-1.png',
59
- :keys => {MsLeft => [:click, false]},
60
- :color => 0xff0099ff,
61
- :width => 100,
62
- :height => 100)
63
- @cursor2 = Cursor.new(:image => 'crosshair-2.png',
64
- :use_mouse => false,
65
- :keys => {
66
- KbSpace => [:click, false],
67
- KbUp => :up,
68
- KbDown => :down,
69
- KbLeft => :left,
70
- KbRight => :right},
71
- :color => 0xff99ff00,
72
- :factor_x => 0.5,
73
- :factor_y => 0.5)
74
- @cursor2.x = width*3/4
75
- @cursor2.y = height/2
76
-
77
- # Create a TextBox with default option :size => 15
78
- @info = TextBox.new(:size => 15)
79
- @info.watch(@systems[FpsSystem])
80
- @info.watch(@systems[StalkerSystem], :color => 0xff3ffccf)
81
- # We can change the size for a specific line of text
82
- @info.watch("@cursor1 data:", :size => 20)
83
- # Color too
84
- @info.watch(@cursor1, :color => 0xff0099ff)
85
- @info.watch("@cursor2 data:", :size => 20)
86
- @info.watch(@cursor2, :color => 0xff99ff00)
87
- @info.text("Move @cursor1 with mouse and @cursor2 with arrow keys (click with space!)")
88
- end
89
-
90
- end
91
-
92
- Example.new.show
@@ -1,86 +0,0 @@
1
- #!/usr/bin/env ruby
2
- LIB_PATH = File.join(File.dirname(__FILE__), '..', '..', 'lib', 'lotu.rb')
3
- require File.expand_path(LIB_PATH)
4
-
5
- include Gosu::Button
6
- include Lotu
7
-
8
- class SteeringRuby < Actor
9
- def initialize(opts={})
10
- super
11
- use(SteeringSystem, opts)
12
- end
13
-
14
- def warp(x, y)
15
- @pos.x, @pos.y = x, y
16
- end
17
- end
18
-
19
- class Example < Game
20
- def initialize
21
- # This will call the hooks:
22
- # load_resources, setup_systems and setup_actors
23
- # declared in the parent class
24
- super
25
- # Custom setup methods for this class
26
- setup_events
27
- end
28
-
29
- def load_resources
30
- with_path_from_file(__FILE__) do
31
- load_images '../media/images'
32
- load_animations '../media/animations'
33
- end
34
- end
35
-
36
- def setup_input
37
- set_keys(KbEscape => :close,
38
- MsRight => :reset_ruby,
39
- KbD => [:debug!, false])
40
- end
41
-
42
- def setup_systems
43
- # It's important to call super here to setup the InputSystem
44
- super
45
- use(FpsSystem)
46
- use(StalkerSystem, :stalk => [Actor, Vector2d, Object])
47
- end
48
-
49
- def setup_actors
50
- @ruby = SteeringRuby.new(:mass => 0.3, :max_speed => 100, :max_turn_rate => 140)
51
- @ruby.warp(width/2, height/2)
52
- @ruby.activate(:evade)
53
- @ruby.play_animation('missile.png')
54
-
55
- @ruby2 = SteeringRuby.new
56
- @ruby2.activate(:pursuit)
57
- @ruby2.play_animation('missile.png', :factor_x => 0.5, :factor_y => 0.5, :fps => 60)
58
-
59
- @cursor = Cursor.new(:image => 'crosshair-1.png',
60
- :keys => {MsLeft => [:click, false]})
61
-
62
- @window_info = TextBox.new(:size => 15)
63
- @window_info.watch(@systems[FpsSystem])
64
- @window_info.watch(@systems[StalkerSystem])
65
- @window_info.watch(@cursor, :color => 0xffff0000)
66
- @window_info.text("Click to start the simulation")
67
- @window_info.text("One will pursuit while the other evades, right click to center evader on screen")
68
-
69
- @ruby_info = TextBox.new(:attach_to => @ruby, :size => 14)
70
- @ruby_info.watch(@ruby)
71
- end
72
-
73
- def setup_events
74
- @cursor.on(:click) do |x,y|
75
- @ruby.pursuer = @ruby2
76
- @ruby2.evader = @ruby
77
- end
78
- end
79
-
80
- def reset_ruby
81
- @ruby.pos.x = width/2
82
- @ruby.pos.y = height/2
83
- end
84
- end
85
-
86
- Example.new.show