lotu 0.1.3 → 0.1.4
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/.gitignore +1 -0
- data/README.rdoc +19 -9
- data/TODO +4 -0
- data/VERSION +1 -1
- data/examples/hello_world/hello_world.rb +3 -8
- data/examples/mouse_pointer/mouse_pointer.rb +14 -9
- data/examples/steering_behaviors/steering.rb +56 -0
- data/lib/lotu/actor.rb +15 -2
- data/lib/lotu/behaviors/drawable.rb +15 -4
- data/lib/lotu/behaviors/resourceful.rb +1 -3
- data/lib/lotu/cursor.rb +4 -0
- data/lib/lotu/{fps.rb → misc/fps.rb} +1 -5
- data/lib/lotu/misc/vector2d.rb +124 -0
- data/lib/lotu/systems/steering.rb +198 -0
- data/lib/lotu/text_box.rb +54 -0
- data/lib/lotu/window.rb +3 -3
- data/lib/lotu.rb +3 -2
- data/lotu.gemspec +11 -5
- metadata +11 -4
data/.gitignore
CHANGED
data/README.rdoc
CHANGED
@@ -1,10 +1,13 @@
|
|
1
|
-
== Development just started!
|
2
|
-
So bear with me while this framework is in a primordial state. But fear not, it's shaping up pretty nicely! :D
|
3
|
-
|
4
1
|
= lotu
|
5
2
|
A simple, agile Ruby game development framework.
|
6
3
|
|
7
4
|
== Install
|
5
|
+
The preferred way to try it out is cloning from github:
|
6
|
+
|
7
|
+
git clone git://github.com/lobo-tuerto/lotu.git
|
8
|
+
|
9
|
+
The is a gem right now, but while the API stabilizes it is not updated as often as the github repo.
|
10
|
+
|
8
11
|
gem install lotu
|
9
12
|
|
10
13
|
== Description
|
@@ -23,17 +26,24 @@ So, what can you do if you spot some patters here, a few more there? well, you d
|
|
23
26
|
If you have any questions of suggestions don't hesitate and send me a message!
|
24
27
|
|
25
28
|
|
26
|
-
== Features
|
27
|
-
* Easy way to manage input, rendering, updating, etc.
|
28
|
-
*
|
29
|
-
*
|
29
|
+
== Features
|
30
|
+
* Easy way to manage input, rendering, updating, etc.
|
31
|
+
* Simple access to game resources (images, sounds, songs, etc).
|
32
|
+
* Steering behaviors
|
33
|
+
|
34
|
+
|
35
|
+
== Coming features
|
36
|
+
* Utility classes for viewports, game states, etc.
|
37
|
+
* Pathfinding and more AI stuff
|
30
38
|
|
31
39
|
|
32
40
|
== Other frameworks
|
33
|
-
I saw another framework around called Chingu
|
34
|
-
It's been around for a little while and I recommend you take a look at it:
|
41
|
+
I saw another framework around called *Chingu*, It's been around for a little while and I recommend you take a look at it:
|
35
42
|
http://github.com/ippa/chingu
|
36
43
|
|
44
|
+
There is also *gamebox*, it's based on Rubygame though:
|
45
|
+
http://github.com/shawn42/gamebox
|
46
|
+
|
37
47
|
I did find many of the ideas I have for my framework already implemented in it. But be sure I will take inspiration from many others. ;)
|
38
48
|
Nevertheless I'm doing this because I want to become better at architecturing software. And this seems like fitting practice. :D
|
39
49
|
|
data/TODO
ADDED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.4
|
@@ -5,7 +5,7 @@ include Gosu::Button
|
|
5
5
|
|
6
6
|
class MovingRuby < Lotu::Actor
|
7
7
|
|
8
|
-
def initialize
|
8
|
+
def initialize(opts={})
|
9
9
|
super
|
10
10
|
set_image 'CptnRuby Gem.png'
|
11
11
|
set_keys(KbRight => :move_right,
|
@@ -36,18 +36,13 @@ class Example < Lotu::Window
|
|
36
36
|
|
37
37
|
def initialize
|
38
38
|
super
|
39
|
-
set_keys
|
39
|
+
set_keys(KbEscape => :close)
|
40
40
|
|
41
41
|
with_path(__FILE__) do
|
42
42
|
load_images '../media'
|
43
43
|
end
|
44
44
|
|
45
|
-
@ruby = MovingRuby.new
|
46
|
-
end
|
47
|
-
|
48
|
-
def draw
|
49
|
-
super
|
50
|
-
@fps_counter.draw
|
45
|
+
@ruby = MovingRuby.new(:x => width/2, :y => height/2)
|
51
46
|
end
|
52
47
|
|
53
48
|
end
|
@@ -25,7 +25,8 @@ class Example < Lotu::Window
|
|
25
25
|
|
26
26
|
@ruby = WarpingRuby.new
|
27
27
|
@cursor1 = Lotu::Cursor.new(:image => 'crosshair.png',
|
28
|
-
:keys => {MsLeft => [:click, false]}
|
28
|
+
:keys => {MsLeft => [:click, false]},
|
29
|
+
:color => 0xff0099ff)
|
29
30
|
@cursor2 = Lotu::Cursor.new(:image => 'crosshair.png',
|
30
31
|
:use_mouse => false,
|
31
32
|
:keys => {
|
@@ -33,8 +34,8 @@ class Example < Lotu::Window
|
|
33
34
|
KbUp => :up,
|
34
35
|
KbDown => :down,
|
35
36
|
KbLeft => :left,
|
36
|
-
KbRight => :right
|
37
|
-
|
37
|
+
KbRight => :right},
|
38
|
+
:color => 0xff99ff00)
|
38
39
|
|
39
40
|
@cursor1.on(:click) do |x,y|
|
40
41
|
@ruby.warp(x,y)
|
@@ -42,14 +43,18 @@ class Example < Lotu::Window
|
|
42
43
|
@cursor2.on(:click) do |x,y|
|
43
44
|
@ruby.warp(x,y)
|
44
45
|
end
|
45
|
-
end
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
@
|
51
|
-
@
|
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)
|
52
56
|
end
|
57
|
+
|
53
58
|
end
|
54
59
|
|
55
60
|
Example.new.show
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
LIB_PATH = File.join(File.dirname(__FILE__), '..', '..', 'lib', 'lotu.rb')
|
3
|
+
require File.expand_path(LIB_PATH)
|
4
|
+
include Gosu::Button
|
5
|
+
|
6
|
+
class SteeringRuby < Lotu::Actor
|
7
|
+
def initialize(opts={})
|
8
|
+
super
|
9
|
+
set_image 'CptnRuby Gem.png'
|
10
|
+
activate_system(Lotu::Steering, opts)
|
11
|
+
end
|
12
|
+
|
13
|
+
def warp(x, y)
|
14
|
+
@pos.x, @pos.y = x, y
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Example < Lotu::Window
|
19
|
+
def initialize
|
20
|
+
super
|
21
|
+
set_keys(KbEscape => :close,
|
22
|
+
MsRight => :reset_ruby)
|
23
|
+
|
24
|
+
with_path __FILE__ do
|
25
|
+
load_images '../media'
|
26
|
+
end
|
27
|
+
|
28
|
+
@ruby = SteeringRuby.new(:mass => 0.3, :max_speed => 100, :max_turn_rate => 140)
|
29
|
+
@ruby.warp(width/2, height/2)
|
30
|
+
@ruby.activate(:evade)
|
31
|
+
|
32
|
+
@ruby2 = SteeringRuby.new
|
33
|
+
@ruby2.activate(:pursuit)
|
34
|
+
|
35
|
+
@cursor = Lotu::Cursor.new(:image => 'crosshair.png',
|
36
|
+
:keys => {MsLeft => [:click, false]})
|
37
|
+
@cursor.on(:click) do |x,y|
|
38
|
+
@ruby.pursuer = @ruby2#Lotu::Vector2d.new(x, y)
|
39
|
+
@ruby2.evader = @ruby
|
40
|
+
end
|
41
|
+
|
42
|
+
@window_info = Lotu::TextBox.new
|
43
|
+
@window_info.watch(@fps_counter)
|
44
|
+
@window_info.watch(@cursor, :color => 0xffff0000)
|
45
|
+
|
46
|
+
@ruby_info = Lotu::TextBox.new(:attach_to => @ruby, :font_size => 16)
|
47
|
+
@ruby_info.watch(@ruby)
|
48
|
+
end
|
49
|
+
|
50
|
+
def reset_ruby
|
51
|
+
@ruby.pos.x = width/2
|
52
|
+
@ruby.pos.y = height/2
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
Example.new.show
|
data/lib/lotu/actor.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Lotu
|
2
2
|
class Actor
|
3
|
-
attr_accessor :parent, :x, :y
|
3
|
+
attr_accessor :parent, :x, :y, :systems
|
4
4
|
|
5
5
|
def initialize(opts={})
|
6
6
|
default_opts = {
|
@@ -20,6 +20,8 @@ module Lotu
|
|
20
20
|
self.extend Controllable
|
21
21
|
self.extend Eventful
|
22
22
|
self.extend Collidable
|
23
|
+
|
24
|
+
@systems = {}
|
23
25
|
end
|
24
26
|
|
25
27
|
# Easy access to delta-time
|
@@ -32,6 +34,17 @@ module Lotu
|
|
32
34
|
@parent.update_queue.delete(self)
|
33
35
|
end
|
34
36
|
|
35
|
-
def
|
37
|
+
def activate_system(klass, opts={})
|
38
|
+
@systems[klass] = klass.new(self, opts)
|
39
|
+
end
|
40
|
+
|
41
|
+
def update
|
42
|
+
@systems.each_pair do |klass, system|
|
43
|
+
system.update
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def draw;end
|
48
|
+
|
36
49
|
end
|
37
50
|
end
|
@@ -6,25 +6,36 @@ module Lotu
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def init_behavior
|
9
|
+
class << self
|
10
|
+
attr_accessor :angle
|
11
|
+
end
|
12
|
+
|
9
13
|
@image = nil
|
10
|
-
@color = 0xffffffff
|
11
14
|
@z = 0
|
12
15
|
@angle = 0.0
|
13
16
|
@center_x = 0.5
|
14
17
|
@center_y = 0.5
|
15
18
|
@factor_x = 1.0
|
16
19
|
@factor_y = 1.0
|
17
|
-
@color = 0xffffffff
|
18
20
|
@mode = :default
|
19
21
|
end
|
20
22
|
|
23
|
+
def draw_me
|
24
|
+
@parent.draw_queue << self unless @parent.draw_queue.include?(self)
|
25
|
+
end
|
26
|
+
|
27
|
+
def image
|
28
|
+
@image
|
29
|
+
end
|
30
|
+
|
21
31
|
def set_image(image)
|
22
32
|
@image = @parent.image(image)
|
23
|
-
|
33
|
+
draw_me
|
24
34
|
end
|
25
35
|
|
26
36
|
def draw
|
27
|
-
|
37
|
+
super
|
38
|
+
@image.draw_rot(@x, @y, @z, @angle, @center_x, @center_y, @factor_x, @factor_y, @color, @mode) unless @image.nil?
|
28
39
|
end
|
29
40
|
|
30
41
|
def die
|
@@ -46,9 +46,7 @@ module Lotu
|
|
46
46
|
puts "Loading from: #{path}"
|
47
47
|
|
48
48
|
count = 0
|
49
|
-
Dir.entries(path).
|
50
|
-
entry =~ regexp
|
51
|
-
end.each do |entry|
|
49
|
+
Dir.entries(path).grep(regexp).each do |entry|
|
52
50
|
begin
|
53
51
|
container[entry] = klass.new($window, File.join(path, entry))
|
54
52
|
count += 1
|
data/lib/lotu/cursor.rb
CHANGED
@@ -22,11 +22,7 @@ class FpsCounter
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
def draw
|
26
|
-
$window.font.draw("FPS: #{to_s}", 10, 10, 0, 1.0, 1.0, 0xffffff00)
|
27
|
-
end
|
28
|
-
|
29
25
|
def to_s
|
30
|
-
"
|
26
|
+
"@samples(#{@samples}) @fps(#{format("%.2f",@fps)}) @objs(#{@objs}) @actors(#{@actors}) @inputs(#{@inputs})"
|
31
27
|
end
|
32
28
|
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module Lotu
|
2
|
+
class Vector2d
|
3
|
+
attr_reader :x, :y
|
4
|
+
|
5
|
+
def self.up
|
6
|
+
new(0, -1)
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(x=0, y=0)
|
10
|
+
clear_cache
|
11
|
+
@x = Float(x)
|
12
|
+
@y = Float(y)
|
13
|
+
end
|
14
|
+
|
15
|
+
def clear_cache
|
16
|
+
@length = nil
|
17
|
+
@length_sq = nil
|
18
|
+
@normalized = false
|
19
|
+
end
|
20
|
+
|
21
|
+
def zero!
|
22
|
+
self.x = 0
|
23
|
+
self.y = 0
|
24
|
+
end
|
25
|
+
|
26
|
+
def zero?
|
27
|
+
@x == 0 && @y == 0
|
28
|
+
end
|
29
|
+
|
30
|
+
def length
|
31
|
+
@length ||= Math.sqrt(length_sq)
|
32
|
+
end
|
33
|
+
|
34
|
+
def length_sq
|
35
|
+
@length_sq ||= @x*@x + @y*@y
|
36
|
+
end
|
37
|
+
|
38
|
+
def x=(x)
|
39
|
+
clear_cache
|
40
|
+
@x = Float(x)
|
41
|
+
end
|
42
|
+
|
43
|
+
def y=(y)
|
44
|
+
clear_cache
|
45
|
+
@y = Float(y)
|
46
|
+
end
|
47
|
+
|
48
|
+
def normalize
|
49
|
+
if zero?
|
50
|
+
Vector2d.new(0,0)
|
51
|
+
else
|
52
|
+
Vector2d.new(@x/length, @y/length)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def normalize!
|
57
|
+
if zero?
|
58
|
+
@length = 0
|
59
|
+
@length_sq = 0
|
60
|
+
@normalized = true
|
61
|
+
end
|
62
|
+
|
63
|
+
return self if @normalized
|
64
|
+
@x /= length
|
65
|
+
@y /= length
|
66
|
+
clear_cache
|
67
|
+
@normalized = true
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def +(v)
|
72
|
+
Vector2d.new(@x+v.x, @y+v.y)
|
73
|
+
end
|
74
|
+
|
75
|
+
def -(v)
|
76
|
+
Vector2d.new(@x-v.x, @y-v.y)
|
77
|
+
end
|
78
|
+
|
79
|
+
def /(n)
|
80
|
+
Vector2d.new(@x/n, @y/n)
|
81
|
+
end
|
82
|
+
|
83
|
+
def *(n)
|
84
|
+
Vector2d.new(@x*n, @y*n)
|
85
|
+
end
|
86
|
+
|
87
|
+
def truncate!(max_l)
|
88
|
+
return self if length < max_l
|
89
|
+
normalize!
|
90
|
+
self.x *= max_l
|
91
|
+
self.y *= max_l
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
def dot(v)
|
96
|
+
@x*v.x + @y*v.y
|
97
|
+
end
|
98
|
+
|
99
|
+
def perp
|
100
|
+
Vector2d.new(@y, -@x)
|
101
|
+
end
|
102
|
+
|
103
|
+
def angle
|
104
|
+
Gosu.angle(0, 0, @x, @y)
|
105
|
+
end
|
106
|
+
|
107
|
+
def angle_to(v)
|
108
|
+
Gosu.angle_diff(angle, v.angle)
|
109
|
+
end
|
110
|
+
|
111
|
+
def sign_to(v)
|
112
|
+
if @y * v.x > @x * v.y
|
113
|
+
return -1
|
114
|
+
else
|
115
|
+
return 1
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def to_s
|
120
|
+
format('%.2f, %.2f', @x, @y)
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module Lotu
|
3
|
+
class Steering
|
4
|
+
attr_reader :force
|
5
|
+
|
6
|
+
def initialize(actor, opts={})
|
7
|
+
# Add new functionality to Actor
|
8
|
+
actor.extend ActorMethods
|
9
|
+
|
10
|
+
# Initialize attributes
|
11
|
+
default_opts = {
|
12
|
+
:mass => 1,
|
13
|
+
:max_speed => 350,
|
14
|
+
:max_turn_rate => 180,
|
15
|
+
:max_force => 300,
|
16
|
+
:wander_radius => 120,
|
17
|
+
:wander_distance => 240.0
|
18
|
+
}
|
19
|
+
opts = default_opts.merge!(opts)
|
20
|
+
|
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]
|
27
|
+
|
28
|
+
# More attributes
|
29
|
+
@actor = actor
|
30
|
+
@behaviors = {}
|
31
|
+
@force = Vector2d.new
|
32
|
+
@zero = Vector2d.new
|
33
|
+
end
|
34
|
+
|
35
|
+
def update
|
36
|
+
@force.zero!
|
37
|
+
@behaviors.each_pair do |behavior, active|
|
38
|
+
@force += send(behavior) if active
|
39
|
+
end
|
40
|
+
|
41
|
+
@actor.accel = @force / @actor.mass
|
42
|
+
@actor.accel.truncate!(@actor.max_force)
|
43
|
+
|
44
|
+
max_angle = @actor.max_turn_rate * @actor.dt
|
45
|
+
new_velocity = @actor.vel + @actor.accel * @actor.dt
|
46
|
+
angle_to_new_velocity = @actor.heading.angle_to(new_velocity)
|
47
|
+
|
48
|
+
if angle_to_new_velocity.abs > max_angle
|
49
|
+
sign = @actor.heading.sign_to(new_velocity)
|
50
|
+
corrected_angle = @actor.heading.angle + max_angle * sign
|
51
|
+
@actor.vel.x = Gosu.offset_x(corrected_angle, new_velocity.length)
|
52
|
+
@actor.vel.y = Gosu.offset_y(corrected_angle, new_velocity.length)
|
53
|
+
else
|
54
|
+
@actor.vel = new_velocity
|
55
|
+
end
|
56
|
+
|
57
|
+
@actor.vel.truncate!(@actor.max_speed)
|
58
|
+
@actor.pos += @actor.vel * @actor.dt
|
59
|
+
|
60
|
+
if @actor.vel.length > 0.0001
|
61
|
+
@actor.heading = @actor.vel.normalize
|
62
|
+
end
|
63
|
+
|
64
|
+
@actor.x = @actor.pos.x
|
65
|
+
@actor.y = @actor.pos.y
|
66
|
+
@actor.angle = @actor.heading.angle
|
67
|
+
end
|
68
|
+
|
69
|
+
def activate(behavior)
|
70
|
+
@behaviors[behavior] = true
|
71
|
+
end
|
72
|
+
|
73
|
+
def deactivate(behavior)
|
74
|
+
@behaviors[behavior] = false
|
75
|
+
end
|
76
|
+
|
77
|
+
# The steering behaviors themselves
|
78
|
+
def seek
|
79
|
+
return @zero if @actor.target.nil?
|
80
|
+
desired_velocity = (@actor.target - @actor.pos).normalize * @actor.max_speed
|
81
|
+
return desired_velocity - @actor.vel
|
82
|
+
end
|
83
|
+
|
84
|
+
def flee
|
85
|
+
return @zero if @actor.target.nil?
|
86
|
+
desired_velocity = (@actor.pos - @actor.target).normalize * @actor.max_speed
|
87
|
+
return desired_velocity - @actor.vel
|
88
|
+
end
|
89
|
+
|
90
|
+
def arrive(deceleration = :normal)
|
91
|
+
return @zero if @actor.target.nil?
|
92
|
+
deceleration_values = {
|
93
|
+
:fast => 0.5,
|
94
|
+
:normal => 1,
|
95
|
+
:slow => 2
|
96
|
+
}
|
97
|
+
deceleration_tweaker = 1.0
|
98
|
+
to_target = @actor.target - @actor.pos
|
99
|
+
distance_to_target = to_target.length
|
100
|
+
|
101
|
+
if distance_to_target > 10
|
102
|
+
speed = distance_to_target / (deceleration_tweaker * deceleration_values[deceleration])
|
103
|
+
speed = [speed, @actor.max_speed].min
|
104
|
+
desired_velocity = to_target * speed / distance_to_target
|
105
|
+
return desired_velocity - @actor.vel
|
106
|
+
else
|
107
|
+
@actor.vel /= 1.15
|
108
|
+
@actor.accel /= 1.15
|
109
|
+
end
|
110
|
+
return @zero
|
111
|
+
end
|
112
|
+
|
113
|
+
def pursuit
|
114
|
+
return @zero if @actor.evader.nil?
|
115
|
+
to_evader = @actor.evader.pos - @actor.pos
|
116
|
+
relative_heading = @actor.heading.dot(@actor.evader.heading)
|
117
|
+
if to_evader.dot(@actor.heading) > 0 && relative_heading < -0.95
|
118
|
+
@actor.target = @actor.evader.pos
|
119
|
+
return seek
|
120
|
+
end
|
121
|
+
|
122
|
+
look_ahead_time = to_evader.length / (@actor.max_speed + @actor.evader.vel.length)
|
123
|
+
predicted_position = @actor.evader.pos + @actor.evader.vel * look_ahead_time
|
124
|
+
@actor.target = predicted_position
|
125
|
+
return seek
|
126
|
+
end
|
127
|
+
|
128
|
+
def evade
|
129
|
+
return @zero if @actor.pursuer.nil?
|
130
|
+
to_pursuer = @actor.pursuer.pos - @actor.pos
|
131
|
+
look_ahead_time = to_pursuer.length / (@actor.max_speed + @actor.pursuer.vel.length)
|
132
|
+
predicted_position = @actor.pursuer.pos + @actor.pursuer.vel * look_ahead_time
|
133
|
+
@actor.target = @actor.pursuer.pos
|
134
|
+
return flee
|
135
|
+
end
|
136
|
+
|
137
|
+
# TODO: Fix wander
|
138
|
+
def wander
|
139
|
+
wander_jitter = 10
|
140
|
+
|
141
|
+
@actor.wander_target += Vector2d.new(Gosu.random(-1,1), Gosu.random(-1,1))
|
142
|
+
@actor.wander_target.normalize!
|
143
|
+
@actor.wander_target *= @actor.wander_radius
|
144
|
+
target_local = @actor.wander_target + Vector2d.new(0, @actor.wander_distance)
|
145
|
+
target_world = local_to_world(target_local, @actor.heading, @actor.heading.perp, @actor.pos)
|
146
|
+
return target_world - @actor.pos
|
147
|
+
end
|
148
|
+
|
149
|
+
def local_to_world(local_target, heading, side, pos)
|
150
|
+
local_angle = heading.angle_to(local_target)
|
151
|
+
x = Gosu.offset_x(local_angle, local_target.length)
|
152
|
+
y = Gosu.offset_y(local_angle, local_target.length)
|
153
|
+
world_point = Vector2d.new(x, y) + pos
|
154
|
+
end
|
155
|
+
|
156
|
+
module ActorMethods
|
157
|
+
|
158
|
+
def self.extended(instance)
|
159
|
+
instance.steering_setup
|
160
|
+
end
|
161
|
+
|
162
|
+
def steering_setup
|
163
|
+
# Create accessors for the actor
|
164
|
+
class << self
|
165
|
+
attr_accessor :mass, :pos, :heading, :vel, :accel,
|
166
|
+
:max_speed, :max_turn_rate, :max_force,
|
167
|
+
:wander_radius, :wander_distance, :wander_target,
|
168
|
+
:target, :evader, :pursuer
|
169
|
+
end
|
170
|
+
|
171
|
+
# Some defaults
|
172
|
+
@pos = Vector2d.new(@x, @y)
|
173
|
+
offset_x = Gosu.offset_x(@angle, 1)
|
174
|
+
offset_y = Gosu.offset_y(@angle, 1)
|
175
|
+
@heading = Vector2d.new(offset_x, offset_y)
|
176
|
+
@vel = Vector2d.new
|
177
|
+
@accel = Vector2d.new
|
178
|
+
@wander_target = Vector2d.new
|
179
|
+
end
|
180
|
+
|
181
|
+
def activate(behavior)
|
182
|
+
@systems[Steering].activate(behavior)
|
183
|
+
end
|
184
|
+
|
185
|
+
# to_s utility methods
|
186
|
+
def to_s
|
187
|
+
["@angle(#{format('%.2f', @angle)}°)",
|
188
|
+
"@pos(#{@pos})",
|
189
|
+
"@heading(#{@heading})",
|
190
|
+
"@vel |#{format('%.2f', @vel.length)}| (#{@vel})",
|
191
|
+
"@accel |#{format('%.2f', @accel.length)}| (#{@accel})",
|
192
|
+
"@seek_target(#{@seek_target})"]
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
198
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Lotu
|
2
|
+
class TextBox < Actor
|
3
|
+
|
4
|
+
def initialize(opts={})
|
5
|
+
default_opts = {
|
6
|
+
:font_size => 20
|
7
|
+
}
|
8
|
+
opts = default_opts.merge!(opts)
|
9
|
+
super(opts)
|
10
|
+
@watch_list = []
|
11
|
+
@subject_opts = {}
|
12
|
+
@font_size = opts[:font_size]
|
13
|
+
@attached_to = opts[:attach_to]
|
14
|
+
# Since we aren't setting an image for this, we need to specify
|
15
|
+
# this actor needs to be drawed
|
16
|
+
draw_me
|
17
|
+
end
|
18
|
+
|
19
|
+
def watch(subject, opts={})
|
20
|
+
@watch_list << subject
|
21
|
+
@subject_opts[subject] = opts
|
22
|
+
end
|
23
|
+
|
24
|
+
def attach_to(actor)
|
25
|
+
@attached_to = actor
|
26
|
+
end
|
27
|
+
|
28
|
+
def update
|
29
|
+
unless @attached_to.nil?
|
30
|
+
@x = @attached_to.x + @attached_to.image.width / 2
|
31
|
+
@y = @attached_to.y - @attached_to.image.height / 2
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def draw
|
36
|
+
pos_y = 0
|
37
|
+
@watch_list.each do |watched|
|
38
|
+
my_font_size = @subject_opts[watched][:font_size] || @font_size
|
39
|
+
my_color = @subject_opts[watched][:color] || @color
|
40
|
+
my_text = watched.to_s
|
41
|
+
if my_text.is_a?(String)
|
42
|
+
$window.fonts[my_font_size].draw(my_text, @x, @y + pos_y, @z, @factor_x, @factor_y, my_color)
|
43
|
+
pos_y += my_font_size
|
44
|
+
else
|
45
|
+
my_text.each do |line|
|
46
|
+
$window.fonts[my_font_size].draw(line, @x, @y + pos_y, @z, @factor_x, @factor_y, my_color)
|
47
|
+
pos_y += my_font_size
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
data/lib/lotu/window.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
module Lotu
|
2
2
|
class Window < Gosu::Window
|
3
3
|
# delta time
|
4
|
-
attr_reader :dt, :systems
|
4
|
+
attr_reader :dt, :systems, :fonts
|
5
5
|
attr_accessor :update_queue, :draw_queue, :input_listeners, :font
|
6
6
|
|
7
7
|
def initialize(params={})
|
8
|
-
super(
|
8
|
+
super(800, 600, false)
|
9
9
|
|
10
10
|
# Handy global window variable
|
11
11
|
$window = self
|
@@ -18,7 +18,7 @@ module Lotu
|
|
18
18
|
|
19
19
|
@fps_counter = FpsCounter.new
|
20
20
|
@last_time = Gosu::milliseconds
|
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
|
data/lib/lotu.rb
CHANGED
@@ -2,6 +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}"}
|
5
6
|
%w{collidable controllable resourceful drawable controllable/input_controller eventful}.each{|file| require "behaviors/#{file}"}
|
6
|
-
%w{collision}.each{|file| require "systems/#{file}"}
|
7
|
-
%w{
|
7
|
+
%w{collision steering}.each{|file| require "systems/#{file}"}
|
8
|
+
%w{window actor cursor text_box}.each{|file| require file}
|
data/lotu.gemspec
CHANGED
@@ -5,16 +5,17 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{lotu}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.4"
|
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-
|
12
|
+
s.date = %q{2010-03-21}
|
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 = [
|
16
16
|
"LICENSE",
|
17
|
-
"README.rdoc"
|
17
|
+
"README.rdoc",
|
18
|
+
"TODO"
|
18
19
|
]
|
19
20
|
s.files = [
|
20
21
|
".document",
|
@@ -43,6 +44,7 @@ Gem::Specification.new do |s|
|
|
43
44
|
"examples/media/lobo_tuerto.png",
|
44
45
|
"examples/media/title.png",
|
45
46
|
"examples/mouse_pointer/mouse_pointer.rb",
|
47
|
+
"examples/steering_behaviors/steering.rb",
|
46
48
|
"lib/lotu.rb",
|
47
49
|
"lib/lotu/actor.rb",
|
48
50
|
"lib/lotu/behaviors/collidable.rb",
|
@@ -52,8 +54,11 @@ Gem::Specification.new do |s|
|
|
52
54
|
"lib/lotu/behaviors/eventful.rb",
|
53
55
|
"lib/lotu/behaviors/resourceful.rb",
|
54
56
|
"lib/lotu/cursor.rb",
|
55
|
-
"lib/lotu/fps.rb",
|
57
|
+
"lib/lotu/misc/fps.rb",
|
58
|
+
"lib/lotu/misc/vector2d.rb",
|
56
59
|
"lib/lotu/systems/collision.rb",
|
60
|
+
"lib/lotu/systems/steering.rb",
|
61
|
+
"lib/lotu/text_box.rb",
|
57
62
|
"lib/lotu/window.rb",
|
58
63
|
"lotu.gemspec"
|
59
64
|
]
|
@@ -63,7 +68,8 @@ Gem::Specification.new do |s|
|
|
63
68
|
s.rubygems_version = %q{1.3.6}
|
64
69
|
s.summary = %q{A simple, agile Ruby game development framework.}
|
65
70
|
s.test_files = [
|
66
|
-
"examples/
|
71
|
+
"examples/steering_behaviors/steering.rb",
|
72
|
+
"examples/hello_world/hello_world.rb",
|
67
73
|
"examples/mouse_pointer/mouse_pointer.rb"
|
68
74
|
]
|
69
75
|
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 4
|
9
|
+
version: 0.1.4
|
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-
|
17
|
+
date: 2010-03-21 00:00:00 -06:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -40,6 +40,7 @@ extensions: []
|
|
40
40
|
extra_rdoc_files:
|
41
41
|
- LICENSE
|
42
42
|
- README.rdoc
|
43
|
+
- TODO
|
43
44
|
files:
|
44
45
|
- .document
|
45
46
|
- .gitignore
|
@@ -67,6 +68,7 @@ files:
|
|
67
68
|
- examples/media/lobo_tuerto.png
|
68
69
|
- examples/media/title.png
|
69
70
|
- examples/mouse_pointer/mouse_pointer.rb
|
71
|
+
- examples/steering_behaviors/steering.rb
|
70
72
|
- lib/lotu.rb
|
71
73
|
- lib/lotu/actor.rb
|
72
74
|
- lib/lotu/behaviors/collidable.rb
|
@@ -76,10 +78,14 @@ files:
|
|
76
78
|
- lib/lotu/behaviors/eventful.rb
|
77
79
|
- lib/lotu/behaviors/resourceful.rb
|
78
80
|
- lib/lotu/cursor.rb
|
79
|
-
- lib/lotu/fps.rb
|
81
|
+
- lib/lotu/misc/fps.rb
|
82
|
+
- lib/lotu/misc/vector2d.rb
|
80
83
|
- lib/lotu/systems/collision.rb
|
84
|
+
- lib/lotu/systems/steering.rb
|
85
|
+
- lib/lotu/text_box.rb
|
81
86
|
- lib/lotu/window.rb
|
82
87
|
- lotu.gemspec
|
88
|
+
- TODO
|
83
89
|
has_rdoc: true
|
84
90
|
homepage: http://github.com/lobo-tuerto/lotu
|
85
91
|
licenses: []
|
@@ -111,5 +117,6 @@ signing_key:
|
|
111
117
|
specification_version: 3
|
112
118
|
summary: A simple, agile Ruby game development framework.
|
113
119
|
test_files:
|
120
|
+
- examples/steering_behaviors/steering.rb
|
114
121
|
- examples/hello_world/hello_world.rb
|
115
122
|
- examples/mouse_pointer/mouse_pointer.rb
|