Class: Lotu::Steering

Inherits:
Object
  • Object
show all
Defined in:
lib/lotu/systems/steering.rb

Defined Under Namespace

Modules: ActorMethods

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(actor, opts = {}) ⇒ Steering

Returns a new instance of Steering.



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/lotu/systems/steering.rb', line 6

def initialize(actor, opts={})
  # Add new functionality to Actor
  actor.extend ActorMethods

  # Initialize attributes
  default_opts = {
    :mass => 1,
    :max_speed => 350,
    :max_turn_rate => 180,
    :max_force => 300,
    :wander_radius => 120,
    :wander_distance => 240.0
  }
  opts = default_opts.merge!(opts)

  actor.mass = opts[:mass]
  actor.max_speed = opts[:max_speed]
  actor.max_turn_rate = opts[:max_turn_rate]
  actor.max_force = opts[:max_force]
  actor.wander_radius = opts[:wander_radius]
  actor.wander_distance = opts[:wander_distance]

  # More attributes
  @actor = actor
  @behaviors = {}
  @force = Vector2d.new
  @zero = Vector2d.new
  actor.pos.x = actor.x
  actor.pos.y = actor.y
end

Instance Attribute Details

#forceObject (readonly)

Returns the value of attribute force.



4
5
6
# File 'lib/lotu/systems/steering.rb', line 4

def force
  @force
end

Instance Method Details

#activate(behavior) ⇒ Object



71
72
73
# File 'lib/lotu/systems/steering.rb', line 71

def activate(behavior)
  @behaviors[behavior] = true
end

#arrive(deceleration = :normal) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/lotu/systems/steering.rb', line 92

def arrive(deceleration = :normal)
  return @zero if @actor.target.nil?
  deceleration_values = {
    :fast => 0.5,
    :normal => 1,
    :slow => 2
  }
  deceleration_tweaker = 1.0
  to_target = @actor.target - @actor.pos
  distance_to_target = to_target.length

  if distance_to_target > 10
    speed = distance_to_target / (deceleration_tweaker * deceleration_values[deceleration])
    speed = [speed, @actor.max_speed].min
    desired_velocity = to_target * speed / distance_to_target
    return desired_velocity - @actor.vel
  else
    @actor.vel /= 1.15
    @actor.accel /= 1.15
  end
  return @zero
end

#deactivate(behavior) ⇒ Object



75
76
77
# File 'lib/lotu/systems/steering.rb', line 75

def deactivate(behavior)
  @behaviors[behavior] = false
end

#evadeObject



130
131
132
133
134
135
136
137
# File 'lib/lotu/systems/steering.rb', line 130

def evade
  return @zero if @actor.pursuer.nil?
  to_pursuer = @actor.pursuer.pos - @actor.pos
  look_ahead_time = to_pursuer.length / (@actor.max_speed + @actor.pursuer.vel.length)
  predicted_position = @actor.pursuer.pos + @actor.pursuer.vel * look_ahead_time
  @actor.target = @actor.pursuer.pos
  return flee
end

#fleeObject



86
87
88
89
90
# File 'lib/lotu/systems/steering.rb', line 86

def flee
  return @zero if @actor.target.nil?
  desired_velocity = (@actor.pos - @actor.target).normalize * @actor.max_speed
  return desired_velocity - @actor.vel
end

#local_to_world(local_target, heading, side, pos) ⇒ Object



151
152
153
154
155
156
# File 'lib/lotu/systems/steering.rb', line 151

def local_to_world(local_target, heading, side, pos)
  local_angle = heading.angle_to(local_target)
  x = Gosu.offset_x(local_angle, local_target.length)
  y = Gosu.offset_y(local_angle, local_target.length)
  world_point = Vector2d.new(x, y) + pos
end

#pursuitObject



115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/lotu/systems/steering.rb', line 115

def pursuit
  return @zero if @actor.evader.nil?
  to_evader = @actor.evader.pos - @actor.pos
  relative_heading = @actor.heading.dot(@actor.evader.heading)
  if to_evader.dot(@actor.heading) > 0 && relative_heading < -0.95
    @actor.target = @actor.evader.pos
    return seek
  end

  look_ahead_time = to_evader.length / (@actor.max_speed + @actor.evader.vel.length)
  predicted_position = @actor.evader.pos + @actor.evader.vel * look_ahead_time
  @actor.target = predicted_position
  return seek
end

#seekObject

The steering behaviors themselves



80
81
82
83
84
# File 'lib/lotu/systems/steering.rb', line 80

def seek
  return @zero if @actor.target.nil?
  desired_velocity = (@actor.target - @actor.pos).normalize * @actor.max_speed
  return desired_velocity - @actor.vel
end

#updateObject



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/lotu/systems/steering.rb', line 37

def update
  @force.zero!
  @behaviors.each_pair do |behavior, active|
    @force += send(behavior) if active
  end

  @actor.accel = @force / @actor.mass
  @actor.accel.truncate!(@actor.max_force)

  max_angle = @actor.max_turn_rate * @actor.dt
  new_velocity = @actor.vel + @actor.accel * @actor.dt
  angle_to_new_velocity = @actor.heading.angle_to(new_velocity)

  if angle_to_new_velocity.abs > max_angle
    sign = @actor.heading.sign_to(new_velocity)
    corrected_angle = @actor.heading.angle + max_angle * sign
    @actor.vel.x = Gosu.offset_x(corrected_angle, new_velocity.length)
    @actor.vel.y = Gosu.offset_y(corrected_angle, new_velocity.length)
  else
    @actor.vel = new_velocity
  end

  @actor.vel.truncate!(@actor.max_speed)
  @actor.pos += @actor.vel * @actor.dt

  if @actor.vel.length > 0.0001
    @actor.heading = @actor.vel.normalize
  end

  @actor.x = @actor.pos.x
  @actor.y = @actor.pos.y
  @actor.angle = @actor.heading.angle
end

#wanderObject

TODO: Fix wander



140
141
142
143
144
145
146
147
148
149
# File 'lib/lotu/systems/steering.rb', line 140

def wander
  wander_jitter = 10

  @actor.wander_target += Vector2d.new(Gosu.random(-1,1), Gosu.random(-1,1))
  @actor.wander_target.normalize!
  @actor.wander_target *= @actor.wander_radius
  target_local = @actor.wander_target + Vector2d.new(0, @actor.wander_distance)
  target_world = local_to_world(target_local, @actor.heading, @actor.heading.perp, @actor.pos)
  return target_world - @actor.pos
end