lotu 0.1.7 → 0.1.9

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.7
1
+ 0.1.9
@@ -7,13 +7,16 @@ class MovingRuby < Lotu::Actor
7
7
 
8
8
  def initialize(opts={})
9
9
  super
10
+ # Use the image which filename is CptnRuby Gem.png
10
11
  set_image 'CptnRuby Gem.png'
12
+ # Map keys to some methods
11
13
  set_keys(KbRight => :move_right,
12
14
  KbLeft => :move_left,
13
15
  KbUp => :move_up,
14
16
  KbDown => :move_down)
15
17
  end
16
18
 
19
+ # Let's define some basic movement methods
17
20
  def move_right
18
21
  @x += 1
19
22
  end
@@ -35,14 +38,25 @@ end
35
38
  class Example < Lotu::Window
36
39
 
37
40
  def initialize
41
+ # This will call the hooks:
42
+ # load_resources, setup_systems and setup_actors
43
+ # declared in the parent class
38
44
  super
45
+ # When the Escape key is pressed, call the close method on class Example
39
46
  set_keys(KbEscape => :close)
47
+ end
40
48
 
49
+ def load_resources
50
+ # From this file,
41
51
  with_path_from_file(__FILE__) do
42
- load_images '../media'
52
+ # go back one dir and search inside media/
53
+ load_images '../media/'
43
54
  end
55
+ end
44
56
 
57
+ def setup_actors
45
58
  @ruby = MovingRuby.new(:x => width/2, :y => height/2)
59
+ # Create a TextBox so we can display a message on screen
46
60
  @info = Lotu::TextBox.new
47
61
  @info.text("Move around with arrow keys")
48
62
  end
@@ -16,13 +16,27 @@ end
16
16
 
17
17
  class Example < Lotu::Window
18
18
  def initialize
19
+ # This will call the hooks:
20
+ # load_resources, setup_systems and setup_actors
21
+ # declared in the parent class
19
22
  super
20
- set_keys KbEscape => :close
23
+ # Custom setup methods for this class
24
+ setup_input
25
+ setup_events
26
+ end
21
27
 
28
+ def load_resources
22
29
  with_path_from_file(__FILE__) do
23
30
  load_images '../media'
24
31
  end
32
+ end
33
+
34
+ def setup_systems
35
+ use(Lotu::FpsSystem)
36
+ use(Lotu::StalkerSystem, :stalk => [Lotu::Actor, Lotu::InputController, Object])
37
+ end
25
38
 
39
+ def setup_actors
26
40
  @ruby = WarpingRuby.new(:x => width/2, :y => height/2)
27
41
  @cursor1 = Lotu::Cursor.new(:image => 'crosshair.png',
28
42
  :keys => {MsLeft => [:click, false]},
@@ -36,25 +50,33 @@ class Example < Lotu::Window
36
50
  KbLeft => :left,
37
51
  KbRight => :right},
38
52
  :color => 0xff99ff00)
53
+ @cursor2.x = width*3/4
54
+ @cursor2.y = height/2
55
+
56
+ # Create a TextBox with default option :size => 15
57
+ @info = Lotu::TextBox.new(:size => 15)
58
+ @info.watch(@systems[Lotu::FpsSystem])
59
+ @info.watch(@systems[Lotu::StalkerSystem])
60
+ # We can change the size for a specific line of text
61
+ @info.watch("@cursor1 data:", :size => 20)
62
+ # Color too
63
+ @info.watch(@cursor1, :color => 0xff0099ff)
64
+ @info.watch("@cursor2 data:", :size => 20)
65
+ @info.watch(@cursor2, :color => 0xff99ff00)
66
+ @info.text("Move @cursor1 with mouse and @cursor2 with arrow keys (click with space!)")
67
+ end
68
+
69
+ def setup_input
70
+ set_keys KbEscape => :close
71
+ end
39
72
 
73
+ def setup_events
40
74
  @cursor1.on(:click) do |x,y|
41
75
  @ruby.warp(x,y)
42
76
  end
43
77
  @cursor2.on(:click) do |x,y|
44
78
  @ruby.warp(x,y)
45
79
  end
46
-
47
- @cursor2.x = width*3/4
48
- @cursor2.y = height/2
49
-
50
- @info = Lotu::TextBox.new
51
- @info.watch(@fps_counter)
52
- @info.watch("@cursor1 data:")
53
- @info.watch(@cursor1, :color => 0xff0099ff, :font_size => 15)
54
- @info.watch("@cursor2 data:")
55
- @info.watch(@cursor2, :color => 0xff99ff00, :font_size => 15)
56
- @info.text("")
57
- @info.text("Move @cursor1 with mouse and @cursor2 with arrow keys (click with space!)", :font_size => 15)
58
80
  end
59
81
 
60
82
  end
@@ -7,7 +7,7 @@ class SteeringRuby < Lotu::Actor
7
7
  def initialize(opts={})
8
8
  super
9
9
  set_image 'CptnRuby Gem.png'
10
- activate_system(Lotu::Steering, opts)
10
+ use(Lotu::SteeringSystem, opts)
11
11
  end
12
12
 
13
13
  def warp(x, y)
@@ -17,14 +17,31 @@ end
17
17
 
18
18
  class Example < Lotu::Window
19
19
  def initialize
20
+ # This will call the hooks:
21
+ # load_resources, setup_systems and setup_actors
22
+ # declared in the parent class
20
23
  super
21
- set_keys(KbEscape => :close,
22
- MsRight => :reset_ruby)
24
+ # Custom setup methods for this class
25
+ setup_input
26
+ setup_events
27
+ end
23
28
 
29
+ def load_resources
24
30
  with_path_from_file(__FILE__) do
25
31
  load_images '../media'
26
32
  end
33
+ end
34
+
35
+ def setup_input
36
+ set_keys(KbEscape => :close,
37
+ MsRight => :reset_ruby)
38
+ end
39
+
40
+ def setup_systems
41
+ use(Lotu::FpsSystem)
42
+ end
27
43
 
44
+ def setup_actors
28
45
  @ruby = SteeringRuby.new(:mass => 0.3, :max_speed => 100, :max_turn_rate => 140)
29
46
  @ruby.warp(width/2, height/2)
30
47
  @ruby.activate(:evade)
@@ -34,21 +51,24 @@ class Example < Lotu::Window
34
51
 
35
52
  @cursor = Lotu::Cursor.new(:image => 'crosshair.png',
36
53
  :keys => {MsLeft => [:click, false]})
37
- @cursor.on(:click) do |x,y|
38
- @ruby.pursuer = @ruby2
39
- @ruby2.evader = @ruby
40
- end
41
54
 
42
- @window_info = Lotu::TextBox.new(:font_size => 15)
43
- @window_info.watch(@fps_counter, :font_size => 20)
55
+ @window_info = Lotu::TextBox.new(:size => 15)
56
+ @window_info.watch(@systems[Lotu::FpsSystem])
44
57
  @window_info.watch(@cursor, :color => 0xffff0000)
45
58
  @window_info.text("Click to start the simulation")
46
59
  @window_info.text("One will pursuit while the other evades, right click to center evader on screen")
47
60
 
48
- @ruby_info = Lotu::TextBox.new(:attach_to => @ruby, :font_size => 14)
61
+ @ruby_info = Lotu::TextBox.new(:attach_to => @ruby, :size => 14)
49
62
  @ruby_info.watch(@ruby)
50
63
  end
51
64
 
65
+ def setup_events
66
+ @cursor.on(:click) do |x,y|
67
+ @ruby.pursuer = @ruby2
68
+ @ruby2.evader = @ruby
69
+ end
70
+ end
71
+
52
72
  def reset_ruby
53
73
  @ruby.pos.x = width/2
54
74
  @ruby.pos.y = height/2
data/lib/lotu/actor.rb CHANGED
@@ -2,6 +2,8 @@ module Lotu
2
2
  class Actor
3
3
  attr_accessor :parent, :x, :y, :systems
4
4
 
5
+ include SystemUser
6
+
5
7
  def initialize(opts={})
6
8
  default_opts = {
7
9
  :x => 0,
@@ -32,10 +34,6 @@ module Lotu
32
34
  @parent.update_queue.delete(self)
33
35
  end
34
36
 
35
- def activate_system(klass, opts={})
36
- @systems[klass] = klass.new(self, opts)
37
- end
38
-
39
37
  def update
40
38
  @systems.each_pair do |klass, system|
41
39
  system.update
@@ -11,7 +11,7 @@ module Lotu
11
11
 
12
12
  def collides_as(tag)
13
13
  @collision_tag = tag
14
- @parent.systems[:collision].add_entity(self, tag)
14
+ @parent.systems[CollisionSystem].add_entity(self, tag)
15
15
  end
16
16
 
17
17
  def collides_with(other)
@@ -20,7 +20,7 @@ module Lotu
20
20
 
21
21
  def die
22
22
  super
23
- @parent.systems[:collision].remove_entity(self, @collision_tag)
23
+ @parent.systems[CollisionSystem].remove_entity(self, @collision_tag)
24
24
  end
25
25
 
26
26
  end
@@ -0,0 +1,10 @@
1
+ module Lotu
2
+ module SystemUser
3
+
4
+ # Allows to activate a system in the host
5
+ def use(klass, opts={})
6
+ @systems[klass] = klass.new(self, opts)
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ # As saw in a russian comment by ZARATUSTR at:
2
+ # http://kpumuk.info/ruby-on-rails/colorizing-console-ruby-script-output/
3
+ class String
4
+
5
+ def red; colorize(self, "\e[1m\e[31m") end
6
+ def green; colorize(self, "\e[1m\e[32m") end
7
+ def dark_green; colorize(self, "\e[32m") end
8
+ def yellow; colorize(self, "\e[1m\e[33m") end
9
+ def blue; colorize(self, "\e[1m\e[34m") end
10
+ def dark_blue; colorize(self, "\e[34m") end
11
+ def pur; colorize(self, "\e[1m\e[35m") end
12
+ def colorize(text, color_code) "#{color_code}#{text}\e[0m" end
13
+
14
+ end
@@ -0,0 +1,41 @@
1
+ module Lotu
2
+ class CollisionSystem
3
+
4
+ def initialize(user, opts={})
5
+ user.extend UserMethods
6
+ @entities = Hash.new{ |h,k| h[k] = [] }
7
+ @actions = {}
8
+ end
9
+
10
+ def add_entity(obj, tag)
11
+ @entities[tag] << obj
12
+ end
13
+
14
+ def remove_entity(obj, tag)
15
+ @entities[tag].delete(obj)
16
+ end
17
+
18
+ def when_colliding(type1, type2, &blk)
19
+ @actions[[type1, type2]] = blk
20
+ end
21
+
22
+ def update
23
+ @actions.each do |tags, blk|
24
+ @entities[tags[0]].each do |ent1|
25
+ @entities[tags[1]].each do |ent2|
26
+ blk.call(ent1, ent2) if ent1.collides_with(ent2)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ def draw;end
33
+
34
+ module UserMethods
35
+ def when_colliding(*args, &blk)
36
+ systems[CollisionSystem].when_colliding(*args, &blk)
37
+ end
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,32 @@
1
+ module Lotu
2
+ class FpsSystem
3
+
4
+ def initialize(user, opts={})
5
+ default_opts = {
6
+ :samples => 10
7
+ }
8
+ opts = default_opts.merge!(opts)
9
+ @accum = 0.0
10
+ @ticks = 0
11
+ @fps = 0.0
12
+ @samples = opts[:samples]
13
+ end
14
+
15
+ def update
16
+ @ticks += 1
17
+ @accum += $window.dt
18
+ if @ticks >= @samples
19
+ @fps = @ticks/@accum
20
+ @ticks = 0
21
+ @accum = 0.0
22
+ end
23
+ end
24
+
25
+ def to_s
26
+ "Samples per second: #{@samples} | FPS: #{format("%.2f",@fps)}"
27
+ end
28
+
29
+ def draw;end
30
+
31
+ end
32
+ end
@@ -0,0 +1,30 @@
1
+ module Lotu
2
+ class StalkerSystem
3
+
4
+ def initialize(user, opts={})
5
+ default_opts = {
6
+ :stalk => [Actor]
7
+ }
8
+ opts = default_opts.merge!(opts)
9
+ @stalked = {}
10
+ opts[:stalk].each do |type|
11
+ @stalked[type] = 0
12
+ end
13
+ end
14
+
15
+ def update
16
+ @stalked.each_key do |type|
17
+ @stalked[type] = ObjectSpace.each_object(type).count
18
+ end
19
+ end
20
+
21
+ def to_s
22
+ @stalked.map do |type, count|
23
+ "#{type}: #{count}"
24
+ end
25
+ end
26
+
27
+ def draw;end
28
+
29
+ end
30
+ end
@@ -1,11 +1,10 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  module Lotu
3
- class Steering
4
- attr_reader :force
3
+ class SteeringSystem
5
4
 
6
- def initialize(actor, opts={})
5
+ def initialize(user, opts={})
7
6
  # Add new functionality to Actor
8
- actor.extend ActorMethods
7
+ user.extend UserMethods
9
8
 
10
9
  # Initialize attributes
11
10
  default_opts = {
@@ -18,20 +17,20 @@ module Lotu
18
17
  }
19
18
  opts = default_opts.merge!(opts)
20
19
 
21
- actor.mass = opts[:mass]
22
- actor.max_speed = opts[:max_speed]
23
- actor.max_turn_rate = opts[:max_turn_rate]
24
- actor.max_force = opts[:max_force]
25
- actor.wander_radius = opts[:wander_radius]
26
- actor.wander_distance = opts[:wander_distance]
20
+ user.mass = opts[:mass]
21
+ user.max_speed = opts[:max_speed]
22
+ user.max_turn_rate = opts[:max_turn_rate]
23
+ user.max_force = opts[:max_force]
24
+ user.wander_radius = opts[:wander_radius]
25
+ user.wander_distance = opts[:wander_distance]
27
26
 
28
27
  # More attributes
29
- @actor = actor
28
+ @user = user
30
29
  @behaviors = {}
31
30
  @force = Vector2d.new
32
31
  @zero = Vector2d.new
33
- actor.pos.x = actor.x
34
- actor.pos.y = actor.y
32
+ user.pos.x = user.x
33
+ user.pos.y = user.y
35
34
  end
36
35
 
37
36
  def update
@@ -40,32 +39,32 @@ module Lotu
40
39
  @force += send(behavior) if active
41
40
  end
42
41
 
43
- @actor.accel = @force / @actor.mass
44
- @actor.accel.truncate!(@actor.max_force)
42
+ @user.accel = @force / @user.mass
43
+ @user.accel.truncate!(@user.max_force)
45
44
 
46
- max_angle = @actor.max_turn_rate * @actor.dt
47
- new_velocity = @actor.vel + @actor.accel * @actor.dt
48
- angle_to_new_velocity = @actor.heading.angle_to(new_velocity)
45
+ max_angle = @user.max_turn_rate * @user.dt
46
+ new_velocity = @user.vel + @user.accel * @user.dt
47
+ angle_to_new_velocity = @user.heading.angle_to(new_velocity)
49
48
 
50
49
  if angle_to_new_velocity.abs > max_angle
51
- sign = @actor.heading.sign_to(new_velocity)
52
- corrected_angle = @actor.heading.angle + max_angle * sign
53
- @actor.vel.x = Gosu.offset_x(corrected_angle, new_velocity.length)
54
- @actor.vel.y = Gosu.offset_y(corrected_angle, new_velocity.length)
50
+ sign = @user.heading.sign_to(new_velocity)
51
+ corrected_angle = @user.heading.angle + max_angle * sign
52
+ @user.vel.x = Gosu.offset_x(corrected_angle, new_velocity.length)
53
+ @user.vel.y = Gosu.offset_y(corrected_angle, new_velocity.length)
55
54
  else
56
- @actor.vel = new_velocity
55
+ @user.vel = new_velocity
57
56
  end
58
57
 
59
- @actor.vel.truncate!(@actor.max_speed)
60
- @actor.pos += @actor.vel * @actor.dt
58
+ @user.vel.truncate!(@user.max_speed)
59
+ @user.pos += @user.vel * @user.dt
61
60
 
62
- if @actor.vel.length > 0.0001
63
- @actor.heading = @actor.vel.normalize
61
+ if @user.vel.length > 0.0001
62
+ @user.heading = @user.vel.normalize
64
63
  end
65
64
 
66
- @actor.x = @actor.pos.x
67
- @actor.y = @actor.pos.y
68
- @actor.angle = @actor.heading.angle
65
+ @user.x = @user.pos.x
66
+ @user.y = @user.pos.y
67
+ @user.angle = @user.heading.angle
69
68
  end
70
69
 
71
70
  def activate(behavior)
@@ -78,61 +77,61 @@ module Lotu
78
77
 
79
78
  # The steering behaviors themselves
80
79
  def seek
81
- return @zero if @actor.target.nil?
82
- desired_velocity = (@actor.target - @actor.pos).normalize * @actor.max_speed
83
- return desired_velocity - @actor.vel
80
+ return @zero if @user.target.nil?
81
+ desired_velocity = (@user.target - @user.pos).normalize * @user.max_speed
82
+ return desired_velocity - @user.vel
84
83
  end
85
84
 
86
85
  def flee
87
- return @zero if @actor.target.nil?
88
- desired_velocity = (@actor.pos - @actor.target).normalize * @actor.max_speed
89
- return desired_velocity - @actor.vel
86
+ return @zero if @user.target.nil?
87
+ desired_velocity = (@user.pos - @user.target).normalize * @user.max_speed
88
+ return desired_velocity - @user.vel
90
89
  end
91
90
 
92
91
  def arrive(deceleration = :normal)
93
- return @zero if @actor.target.nil?
92
+ return @zero if @user.target.nil?
94
93
  deceleration_values = {
95
94
  :fast => 0.5,
96
95
  :normal => 1,
97
96
  :slow => 2
98
97
  }
99
98
  deceleration_tweaker = 1.0
100
- to_target = @actor.target - @actor.pos
99
+ to_target = @user.target - @user.pos
101
100
  distance_to_target = to_target.length
102
101
 
103
102
  if distance_to_target > 10
104
103
  speed = distance_to_target / (deceleration_tweaker * deceleration_values[deceleration])
105
- speed = [speed, @actor.max_speed].min
104
+ speed = [speed, @user.max_speed].min
106
105
  desired_velocity = to_target * speed / distance_to_target
107
- return desired_velocity - @actor.vel
106
+ return desired_velocity - @user.vel
108
107
  else
109
- @actor.vel /= 1.15
110
- @actor.accel /= 1.15
108
+ @user.vel /= 1.15
109
+ @user.accel /= 1.15
111
110
  end
112
111
  return @zero
113
112
  end
114
113
 
115
114
  def pursuit
116
- return @zero if @actor.evader.nil?
117
- to_evader = @actor.evader.pos - @actor.pos
118
- relative_heading = @actor.heading.dot(@actor.evader.heading)
119
- if to_evader.dot(@actor.heading) > 0 && relative_heading < -0.95
120
- @actor.target = @actor.evader.pos
115
+ return @zero if @user.evader.nil?
116
+ to_evader = @user.evader.pos - @user.pos
117
+ relative_heading = @user.heading.dot(@user.evader.heading)
118
+ if to_evader.dot(@user.heading) > 0 && relative_heading < -0.95
119
+ @user.target = @user.evader.pos
121
120
  return seek
122
121
  end
123
122
 
124
- look_ahead_time = to_evader.length / (@actor.max_speed + @actor.evader.vel.length)
125
- predicted_position = @actor.evader.pos + @actor.evader.vel * look_ahead_time
126
- @actor.target = predicted_position
123
+ look_ahead_time = to_evader.length / (@user.max_speed + @user.evader.vel.length)
124
+ predicted_position = @user.evader.pos + @user.evader.vel * look_ahead_time
125
+ @user.target = predicted_position
127
126
  return seek
128
127
  end
129
128
 
130
129
  def evade
131
- return @zero if @actor.pursuer.nil?
132
- to_pursuer = @actor.pursuer.pos - @actor.pos
133
- look_ahead_time = to_pursuer.length / (@actor.max_speed + @actor.pursuer.vel.length)
134
- predicted_position = @actor.pursuer.pos + @actor.pursuer.vel * look_ahead_time
135
- @actor.target = @actor.pursuer.pos
130
+ return @zero if @user.pursuer.nil?
131
+ to_pursuer = @user.pursuer.pos - @user.pos
132
+ look_ahead_time = to_pursuer.length / (@user.max_speed + @user.pursuer.vel.length)
133
+ predicted_position = @user.pursuer.pos + @user.pursuer.vel * look_ahead_time
134
+ @user.target = @user.pursuer.pos
136
135
  return flee
137
136
  end
138
137
 
@@ -140,12 +139,12 @@ module Lotu
140
139
  def wander
141
140
  wander_jitter = 10
142
141
 
143
- @actor.wander_target += Vector2d.new(Gosu.random(-1,1), Gosu.random(-1,1))
144
- @actor.wander_target.normalize!
145
- @actor.wander_target *= @actor.wander_radius
146
- target_local = @actor.wander_target + Vector2d.new(0, @actor.wander_distance)
147
- target_world = local_to_world(target_local, @actor.heading, @actor.heading.perp, @actor.pos)
148
- return target_world - @actor.pos
142
+ @user.wander_target += Vector2d.new(Gosu.random(-1,1), Gosu.random(-1,1))
143
+ @user.wander_target.normalize!
144
+ @user.wander_target *= @user.wander_radius
145
+ target_local = @user.wander_target + Vector2d.new(0, @user.wander_distance)
146
+ target_world = local_to_world(target_local, @user.heading, @user.heading.perp, @user.pos)
147
+ return target_world - @user.pos
149
148
  end
150
149
 
151
150
  def local_to_world(local_target, heading, side, pos)
@@ -155,14 +154,14 @@ module Lotu
155
154
  world_point = Vector2d.new(x, y) + pos
156
155
  end
157
156
 
158
- module ActorMethods
157
+ module UserMethods
159
158
 
160
159
  def self.extended(instance)
161
160
  instance.steering_setup
162
161
  end
163
162
 
164
163
  def steering_setup
165
- # Create accessors for the actor
164
+ # Create accessors for the user
166
165
  class << self
167
166
  attr_accessor :mass, :pos, :heading, :vel, :accel,
168
167
  :max_speed, :max_turn_rate, :max_force,
@@ -181,7 +180,7 @@ module Lotu
181
180
  end
182
181
 
183
182
  def activate(behavior)
184
- @systems[Steering].activate(behavior)
183
+ @systems[SteeringSystem].activate(behavior)
185
184
  end
186
185
 
187
186
  def distance_to_target
data/lib/lotu/text_box.rb CHANGED
@@ -4,15 +4,14 @@ module Lotu
4
4
 
5
5
  def initialize(opts={})
6
6
  default_opts = {
7
- :font_size => 20
7
+ :size => 20
8
8
  }
9
9
  opts = default_opts.merge!(opts)
10
10
  super(opts)
11
11
  #TODO puede especificar a quién watchear y sus opciones de
12
12
  #dibujado en los parámetros
13
13
  @watch_list = []
14
- @subject_opts = {}
15
- @font_size = opts[:font_size]
14
+ @size = opts[:size]
16
15
  @attached_to = opts[:attach_to]
17
16
  # Since we aren't setting an image for this, we need to specify
18
17
  # this actor needs to be drawed
@@ -24,8 +23,7 @@ module Lotu
24
23
  end
25
24
 
26
25
  def watch(subject, opts={})
27
- @watch_list << subject
28
- @subject_opts[subject] = opts
26
+ @watch_list << [subject, opts]
29
27
  end
30
28
 
31
29
  def attach_to(actor)
@@ -41,17 +39,17 @@ module Lotu
41
39
 
42
40
  def draw
43
41
  pos_y = 0
44
- @watch_list.each do |watched|
45
- my_font_size = @subject_opts[watched][:font_size] || @font_size
46
- my_color = @subject_opts[watched][:color] || @color
42
+ @watch_list.each do |watched, opts|
43
+ my_size = opts[:size] || @size
44
+ my_color = opts[:color] || @color
47
45
  my_text = watched.to_s
48
46
  if my_text.is_a?(String)
49
- $window.fonts[my_font_size].draw(my_text, @x, @y + pos_y, @z, @factor_x, @factor_y, my_color)
50
- pos_y += my_font_size
47
+ $window.fonts[my_size].draw(my_text, @x, @y + pos_y, @z, @factor_x, @factor_y, my_color)
48
+ pos_y += my_size
51
49
  else
52
50
  my_text.each do |line|
53
- $window.fonts[my_font_size].draw(line, @x, @y + pos_y, @z, @factor_x, @factor_y, my_color)
54
- pos_y += my_font_size
51
+ $window.fonts[my_size].draw(line, @x, @y + pos_y, @z, @factor_x, @factor_y, my_color)
52
+ pos_y += my_size
55
53
  end
56
54
  end
57
55
  end
data/lib/lotu/window.rb CHANGED
@@ -1,52 +1,99 @@
1
1
  module Lotu
2
2
  class Window < Gosu::Window
3
- # delta time
3
+ # Accessors for time delta, systems and fonts
4
4
  attr_reader :dt, :systems, :fonts
5
- attr_accessor :update_queue, :draw_queue, :input_listeners, :font
5
+ # Accessors for queues
6
+ attr_accessor :update_queue, :draw_queue, :input_listeners
7
+
8
+ include SystemUser
6
9
 
7
10
  def initialize(params={})
8
11
  super(800, 600, false)
9
12
 
10
13
  # Handy global window variable
11
14
  $window = self
15
+ @debug = true
16
+ setup_containers
12
17
 
13
- # Systems setup
14
- @systems = {}
15
- @update_queue = []
16
- @draw_queue = []
17
- @input_register = Hash.new{|hash,key| hash[key] = []}
18
-
19
- @fps_counter = FpsCounter.new
18
+ # For timer initialization
20
19
  @last_time = Gosu::milliseconds
20
+ # Memoize fonts by size
21
21
  @fonts = Hash.new{|h,k| h[k] = Gosu::Font.new(self, Gosu::default_font_name, k)}
22
22
 
23
23
  # Add extra functionality
24
24
  extend Controllable
25
- extend Resourceful
26
- extend Systems::Collision
25
+
26
+ # Call hook methods
27
+ load_resources
28
+ setup_systems
29
+ setup_actors
27
30
  end
28
31
 
32
+
33
+ # Hook methods, these are meant to be replaced by subclasses
34
+ def load_resources;end
35
+ def setup_systems;end
36
+ def setup_actors;end
37
+
38
+ # Setup various containers
39
+ def setup_containers
40
+ # For systems
41
+ @systems = {}
42
+
43
+ # For queues
44
+ @update_queue = []
45
+ @draw_queue = []
46
+ @input_register = Hash.new{|hash,key| hash[key] = []}
47
+
48
+ # For resource management
49
+ @images = {}
50
+ @sounds = {}
51
+ @songs = {}
52
+ @animations = {}
53
+ end
54
+
55
+ # Main update loop
29
56
  def update
30
57
  new_time = Gosu::milliseconds
31
58
  @dt = (new_time - @last_time)/1000.0
32
59
  @last_time = new_time
33
- @fps_counter.update(@dt)
34
60
 
61
+ # Update each system
35
62
  @systems.each_value do |system|
36
63
  system.update
37
64
  end
38
65
 
39
- @update_queue.each do |item|
40
- item.update
66
+ # Update each actor
67
+ @update_queue.each do |actor|
68
+ actor.update
41
69
  end
42
70
  end
43
71
 
72
+ # Main draw loop
44
73
  def draw
45
- @draw_queue.each do |item|
46
- item.draw
74
+ # Systems may report interesting stuff
75
+ @systems.each_value do |system|
76
+ system.draw
77
+ end
78
+
79
+ # Draw each actor in queue
80
+ @draw_queue.each do |actor|
81
+ actor.draw
47
82
  end
48
83
  end
49
84
 
85
+ # For actor management
86
+ def manage_me(actor)
87
+ @draw_queue << actor
88
+ @update_queue << actor
89
+ end
90
+
91
+ def kill_me(actor)
92
+ @draw_queue.delete(actor)
93
+ @update_queue.delete(actor)
94
+ end
95
+
96
+ # These are for managing input
50
97
  def button_down(id)
51
98
  @input_register[id].each do |item|
52
99
  item.button_down(id)
@@ -59,7 +106,6 @@ module Lotu
59
106
  end
60
107
  end
61
108
 
62
- # Register controller
63
109
  def register_for_input(controller)
64
110
  controller.keys.each_key do |key|
65
111
  @input_register[key] << controller
@@ -67,8 +113,79 @@ module Lotu
67
113
  @update_queue << controller
68
114
  end
69
115
 
70
- def register_for_draw(object)
71
- @draw_queue << object
116
+ # These are for managing resources
117
+ def image(name)
118
+ @images[name]
119
+ end
120
+
121
+ def sound(name)
122
+ @sounds[name]
123
+ end
124
+
125
+ def song(name)
126
+ @songs[name]
72
127
  end
128
+
129
+ def animation(name)
130
+ @animations[name]
131
+ end
132
+
133
+ def load_images(path)
134
+ with_files(/\.png|\.jpg|\.bmp/, path) do |file_name, file_path|
135
+ @images[file_name] = Gosu::Image.new($window, file_path)
136
+ end
137
+ end
138
+
139
+ def load_sounds(path)
140
+ with_files(/\.ogg|\.mp3|\.wav/, path) do |file_name, file_path|
141
+ @sounds[file_name] = Gosu::Sample.new($window, file_path)
142
+ end
143
+ end
144
+
145
+ def load_songs(path)
146
+ with_files(/\.ogg|\.mp3|\.wav/, path) do |file_name, file_path|
147
+ @songs[file_name] = Gosu::Song.new($window, file_path)
148
+ end
149
+ end
150
+
151
+ def load_animations(path)
152
+ path = File.expand_path(File.join(@path, path))
153
+ puts "Loading from: #{path}"
154
+
155
+ count = 0
156
+ Dir.entries(path).grep(regexp).each do |entry|
157
+ begin
158
+ @animations[entry] = klass.new($window, File.join(path, entry))
159
+ count += 1
160
+ rescue Exception => e
161
+ puts e, File.join(path, entry)
162
+ end
163
+ end
164
+ puts "#{count} #{klass} files loaded."
165
+ end
166
+
167
+ def with_path_from_file(path, &blk)
168
+ @path = File.expand_path(File.dirname path)
169
+ yield
170
+ end
171
+
172
+ def with_files(regexp, path)
173
+ path = File.expand_path(File.join(@path, path))
174
+ puts "\nLoading from: #{path}"
175
+
176
+ count = 0
177
+ Dir.entries(path).grep(regexp).each do |entry|
178
+ begin
179
+ yield(entry, File.join(path, entry))
180
+ count += 1
181
+ print '.'.green
182
+ rescue Exception => e
183
+ print '.'.red
184
+ puts e, File.join(path, entry) if @debug
185
+ end
186
+ end
187
+ puts "\n#{count} file(s) loaded."
188
+ end
189
+
73
190
  end
74
191
  end
data/lib/lotu.rb CHANGED
@@ -2,7 +2,7 @@ LOTU_ROOT = File.expand_path(File.join(File.dirname(__FILE__), 'lotu'))
2
2
  $LOAD_PATH.unshift(LOTU_ROOT)
3
3
 
4
4
  require 'gosu'
5
- %w{fps vector2d}.each{|file| require "misc/#{file}"}
6
- %w{collidable controllable resourceful drawable controllable/input_controller eventful}.each{|file| require "behaviors/#{file}"}
7
- %w{collision steering}.each{|file| require "systems/#{file}"}
5
+ %w{vector2d string}.each{|file| require "misc/#{file}"}
6
+ %w{system_user collidable controllable drawable controllable/input_controller eventful}.each{|file| require "behaviors/#{file}"}
7
+ %w{stalker_system fps_system collision_system steering_system}.each{|file| require "systems/#{file}"}
8
8
  %w{window actor cursor text_box}.each{|file| require 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.7"
8
+ s.version = "0.1.9"
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-22}
12
+ s.date = %q{2010-03-25}
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 = [
@@ -52,12 +52,14 @@ Gem::Specification.new do |s|
52
52
  "lib/lotu/behaviors/controllable/input_controller.rb",
53
53
  "lib/lotu/behaviors/drawable.rb",
54
54
  "lib/lotu/behaviors/eventful.rb",
55
- "lib/lotu/behaviors/resourceful.rb",
55
+ "lib/lotu/behaviors/system_user.rb",
56
56
  "lib/lotu/cursor.rb",
57
- "lib/lotu/misc/fps.rb",
57
+ "lib/lotu/misc/string.rb",
58
58
  "lib/lotu/misc/vector2d.rb",
59
- "lib/lotu/systems/collision.rb",
60
- "lib/lotu/systems/steering.rb",
59
+ "lib/lotu/systems/collision_system.rb",
60
+ "lib/lotu/systems/fps_system.rb",
61
+ "lib/lotu/systems/stalker_system.rb",
62
+ "lib/lotu/systems/steering_system.rb",
61
63
  "lib/lotu/text_box.rb",
62
64
  "lib/lotu/window.rb",
63
65
  "lotu.gemspec",
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 7
9
- version: 0.1.7
8
+ - 9
9
+ version: 0.1.9
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-22 00:00:00 -06:00
17
+ date: 2010-03-25 00:00:00 -06:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -76,12 +76,14 @@ files:
76
76
  - lib/lotu/behaviors/controllable/input_controller.rb
77
77
  - lib/lotu/behaviors/drawable.rb
78
78
  - lib/lotu/behaviors/eventful.rb
79
- - lib/lotu/behaviors/resourceful.rb
79
+ - lib/lotu/behaviors/system_user.rb
80
80
  - lib/lotu/cursor.rb
81
- - lib/lotu/misc/fps.rb
81
+ - lib/lotu/misc/string.rb
82
82
  - lib/lotu/misc/vector2d.rb
83
- - lib/lotu/systems/collision.rb
84
- - lib/lotu/systems/steering.rb
83
+ - lib/lotu/systems/collision_system.rb
84
+ - lib/lotu/systems/fps_system.rb
85
+ - lib/lotu/systems/stalker_system.rb
86
+ - lib/lotu/systems/steering_system.rb
85
87
  - lib/lotu/text_box.rb
86
88
  - lib/lotu/window.rb
87
89
  - lotu.gemspec
@@ -1,61 +0,0 @@
1
- # Add methods to load and access images, sounds & songs
2
- module Lotu
3
- module Resourceful
4
-
5
- def self.extended(instance)
6
- instance.init_behavior
7
- end
8
-
9
- def init_behavior
10
- @images = {}
11
- @sounds = {}
12
- @songs = {}
13
- end
14
-
15
- def image(name)
16
- @images[name]
17
- end
18
-
19
- def sound(name)
20
- @sounds[name]
21
- end
22
-
23
- def song(name)
24
- @songs[name]
25
- end
26
-
27
- def load_images(path)
28
- load_resources(@images, /\.png|\.jpg|\.bmp/, path, Gosu::Image)
29
- end
30
-
31
- def load_sounds(path)
32
- load_resources(@sounds, /\.ogg|\.mp3|\.wav/, path, Gosu::Sample)
33
- end
34
-
35
- def load_songs(path)
36
- load_resources(@songs, /\.ogg|\.mp3|\.wav/, path, Gosu::Song)
37
- end
38
-
39
- def with_path_from_file(path, &blk)
40
- @path = File.expand_path(File.dirname path)
41
- yield
42
- end
43
-
44
- def load_resources(container, regexp, path, klass)
45
- path = File.expand_path(File.join(@path, path))
46
- puts "Loading from: #{path}"
47
-
48
- count = 0
49
- Dir.entries(path).grep(regexp).each do |entry|
50
- begin
51
- container[entry] = klass.new($window, File.join(path, entry))
52
- count += 1
53
- rescue Exception => e
54
- puts e, File.join(path, entry)
55
- end
56
- end
57
- puts "#{count} #{klass} files loaded."
58
- end
59
-
60
- end
61
- end
data/lib/lotu/misc/fps.rb DELETED
@@ -1,28 +0,0 @@
1
- class FpsCounter
2
- attr_reader :fps
3
-
4
- def initialize(samples = 10)
5
- @accum = 0.0
6
- @ticks = 0
7
- @fps = 0.0
8
- @samples = samples
9
- @objs = @actors = @input_controllers = 0
10
- end
11
-
12
- def update(dt)
13
- @ticks += 1
14
- @accum += dt
15
- if @ticks >= @samples
16
- @fps = @ticks/@accum
17
- @ticks = 0
18
- @accum = 0.0
19
- @objs = ObjectSpace.each_object.count
20
- @actors = ObjectSpace.each_object(Lotu::Actor).count
21
- @inputs = ObjectSpace.each_object(Lotu::InputController).count
22
- end
23
- end
24
-
25
- def to_s
26
- "@samples(#{@samples}) @fps(#{format("%.2f",@fps)}) @objs(#{@objs}) @actors(#{@actors}) @inputs(#{@inputs})"
27
- end
28
- end
@@ -1,46 +0,0 @@
1
- module Lotu
2
- module Systems
3
-
4
- module Collision
5
- def self.extended(instance)
6
- instance.systems[:collision] = CollisionSystem.new
7
- end
8
-
9
- def when_colliding(*args, &blk)
10
- systems[:collision].when_colliding(*args, &blk)
11
- end
12
- end
13
-
14
- class CollisionSystem
15
-
16
- def initialize
17
- @entities = Hash.new{ |h,k| h[k] = [] }
18
- @actions = {}
19
- end
20
-
21
- def add_entity(obj, tag)
22
- @entities[tag] << obj
23
- end
24
-
25
- def remove_entity(obj, tag)
26
- @entities[tag].delete(obj)
27
- end
28
-
29
- def when_colliding(type1, type2, &blk)
30
- @actions[[type1, type2]] = blk
31
- end
32
-
33
- def update
34
- @actions.each do |tags, blk|
35
- @entities[tags[0]].each do |ent1|
36
- @entities[tags[1]].each do |ent2|
37
- blk.call(ent1, ent2) if ent1.collides_with(ent2)
38
- end
39
- end
40
- end
41
- end
42
-
43
- end
44
-
45
- end
46
- end