lotu 0.1.7 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
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