steering_behaviors 1.0.0

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.
@@ -0,0 +1,42 @@
1
+ ##
2
+ # Copyright 2013, Prylis Incorporated.
3
+ #
4
+ # This file is part of The Ruby Steering Behaviors Library.
5
+ # http://github.com/cpowell/steering-behaviors
6
+ # You can redistribute and/or modify this software only in accordance with
7
+ # the terms found in the "LICENSE" file included with the framework.
8
+
9
+ class SteeringBehaviors::Evade
10
+ extend SteeringBehaviors::Common
11
+
12
+ # Evade a moving target by anticipating its future position. Calculates where it thinks
13
+ # the target will be, and leverages 'flee' to calculate how to best escape.
14
+ # See http://www.red3d.com/cwr/steer/
15
+ #
16
+ # * *Args* :
17
+ # - +evading_kinematic+ -> evading kinematic
18
+ # - +enemy_kinematic+ -> kinematic of the thing to evade
19
+ # * *Returns* :
20
+ # - a steering force
21
+ #
22
+ def self.steer(evading_kinematic, enemy_kinematic)
23
+ offset = enemy_kinematic.position_vec - evading_kinematic.position_vec
24
+ direct_distance = offset.length
25
+ unit_offset = offset / direct_distance
26
+
27
+ parallelness = evading_kinematic.heading_vec.dot(enemy_kinematic.heading_vec)
28
+ forwardness = unit_offset.dot(evading_kinematic.heading_vec)
29
+
30
+ gen, tf = compute_time_factor(forwardness, parallelness)
31
+
32
+ direct_travel_time = direct_distance / evading_kinematic.speed
33
+ direct_travel_time_2 = direct_distance / (evading_kinematic.speed + enemy_kinematic.speed)
34
+ estimated_time_enroute = direct_travel_time_2 * tf
35
+
36
+ # printf "#{ipos.entity}'s target #{qpos.entity} is '#{gen}'. fness: %0.3f pness: %0.3f f: %0.3f p: %0.3f tf: %0.3f\n", forwardness, parallelness, f, p, tf
37
+ predicted_pos_vec = enemy_kinematic.position_vec + (enemy_kinematic.velocity_vec * estimated_time_enroute)
38
+
39
+ return [predicted_pos_vec, SteeringBehaviors::Flee.steer(evading_kinematic, predicted_pos_vec)]
40
+ end
41
+
42
+ end
@@ -0,0 +1,24 @@
1
+ ##
2
+ # Copyright 2013, Prylis Incorporated.
3
+ #
4
+ # This file is part of The Ruby Steering Behaviors Library.
5
+ # http://github.com/cpowell/steering-behaviors
6
+ # You can redistribute and/or modify this software only in accordance with
7
+ # the terms found in the "LICENSE" file included with the framework.
8
+
9
+ class SteeringBehaviors::Flee
10
+
11
+ # Flee a specific position via the best possible route.
12
+ # See http://www.red3d.com/cwr/steer/
13
+ #
14
+ # * *Args* :
15
+ # - +kinematic+ -> the thing that is fleeing
16
+ # - +flee_position+ -> the position-vector that we want to flee from
17
+ # * *Returns* :
18
+ # - the calculated steering force
19
+ #
20
+ def self.steer(kinematic, flee_position)
21
+ best_velocity_to_target = (kinematic.position_vec - flee_position).normalize * kinematic.max_speed
22
+ best_velocity_to_target - kinematic.velocity_vec
23
+ end
24
+ end
@@ -0,0 +1,28 @@
1
+ ##
2
+ # Copyright 2013, Prylis Incorporated.
3
+ #
4
+ # This file is part of The Ruby Steering Behaviors Library.
5
+ # http://github.com/cpowell/steering-behaviors
6
+ # You can redistribute and/or modify this software only in accordance with
7
+ # the terms found in the "LICENSE" file included with the framework.
8
+
9
+ class SteeringBehaviors::Match
10
+
11
+ # Matches the target's course and speed.
12
+ #
13
+ # * *Args* :
14
+ # - +hunter_kinematic+ -> my moving thing
15
+ # - +quarry_kinematic+ -> kinematic of the target
16
+ # * *Returns* :
17
+ # - the steering force
18
+ #
19
+ def self.steer(hunter_kinematic, quarry_kinematic)
20
+ course_diff = ( ( quarry_kinematic.heading_vec.radians - hunter_kinematic.heading_vec.radians + 3*Math::PI ) % (2*Math::PI) ) - Math::PI
21
+
22
+ speed_diff = quarry_kinematic.speed - hunter_kinematic.speed
23
+
24
+ target_local = SteeringBehaviors::Vector.new(course_diff * quarry_kinematic.speed, speed_diff)
25
+ target_local.rotate!(hunter_kinematic.heading_vec.radians)
26
+ end
27
+
28
+ end
@@ -0,0 +1,31 @@
1
+ ##
2
+ # Copyright 2013, Prylis Incorporated.
3
+ #
4
+ # This file is part of The Ruby Steering Behaviors Library.
5
+ # http://github.com/cpowell/steering-behaviors
6
+ # You can redistribute and/or modify this software only in accordance with
7
+ # the terms found in the "LICENSE" file included with the framework.
8
+
9
+ class SteeringBehaviors::Orthogonal
10
+
11
+ # Moves at a 90-degree angle to the target's course.
12
+ #
13
+ # * *Args* :
14
+ # - +hunter_kinematic+ -> my moving thing
15
+ # - +quarry_kinematic+ -> kinematic of the target
16
+ # * *Returns* :
17
+ # -
18
+ #
19
+ def self.steer(hunter_kinematic, quarry_kinematic)
20
+ option_a = SteeringBehaviors::Vector.new(quarry_kinematic.heading_vec.y, -quarry_kinematic.heading_vec.x)
21
+ option_b = SteeringBehaviors::Vector.new(-quarry_kinematic.heading_vec.y, quarry_kinematic.heading_vec.x)
22
+
23
+ da = option_a.delta(hunter_kinematic.heading_vec)
24
+ db = option_b.delta(hunter_kinematic.heading_vec)
25
+
26
+ best_hdg_vec = (da < db ? option_a : option_b)
27
+
28
+ desired_velocity = best_hdg_vec * hunter_kinematic.velocity_vec.length
29
+ end
30
+
31
+ end
@@ -0,0 +1,45 @@
1
+ ##
2
+ # Copyright 2013, Prylis Incorporated.
3
+ #
4
+ # This file is part of The Ruby Steering Behaviors Library.
5
+ # http://github.com/cpowell/steering-behaviors
6
+ # You can redistribute and/or modify this software only in accordance with
7
+ # the terms found in the "LICENSE" file included with the framework.
8
+
9
+ class SteeringBehaviors::Pursue
10
+ extend SteeringBehaviors::Common
11
+
12
+ # Pursue a moving target by anticipating its future position. Calculates where it thinks
13
+ # the target will be, and leverages 'seek' to calculate how to get there.
14
+ # See http://www.red3d.com/cwr/steer/
15
+ #
16
+ # * *Args* :
17
+ # - +hunter_kinematic+ -> pursuing kinematic
18
+ # - +quarry_kinematic+ -> kinematic of the target
19
+ # * *Returns* :
20
+ # -
21
+ # * *Raises* :
22
+ # - ++ ->
23
+ #
24
+ def self.steer(hunter_kinematic, quarry_kinematic)
25
+ offset = quarry_kinematic.position_vec - hunter_kinematic.position_vec
26
+ direct_distance = offset.length
27
+ unit_offset = offset / direct_distance
28
+
29
+ parallelness = hunter_kinematic.heading_vec.dot(quarry_kinematic.heading_vec)
30
+ forwardness = unit_offset.dot(hunter_kinematic.heading_vec)
31
+
32
+ gen, tf = compute_time_factor(forwardness, parallelness)
33
+
34
+ direct_travel_time = direct_distance / hunter_kinematic.speed
35
+ direct_travel_time_2 = direct_distance / (hunter_kinematic.speed + quarry_kinematic.speed)
36
+ estimated_time_enroute = direct_travel_time_2 * tf
37
+
38
+ # printf "#{ipos.entity}'s target #{qpos.entity} is '#{gen}'. fness: %0.3f pness: %0.3f f: %0.3f p: %0.3f tf: %0.3f\n", forwardness, parallelness, f, p, tf
39
+ predicted_pos_vec = quarry_kinematic.position_vec + (quarry_kinematic.velocity_vec * estimated_time_enroute)
40
+
41
+ return [predicted_pos_vec, SteeringBehaviors::Seek.steer(hunter_kinematic, predicted_pos_vec)]
42
+ end
43
+
44
+
45
+ end
@@ -0,0 +1,24 @@
1
+ ##
2
+ # Copyright 2013, Prylis Incorporated.
3
+ #
4
+ # This file is part of The Ruby Steering Behaviors Library.
5
+ # http://github.com/cpowell/steering-behaviors
6
+ # You can redistribute and/or modify this software only in accordance with
7
+ # the terms found in the "LICENSE" file included with the framework.
8
+
9
+ class SteeringBehaviors::Seek
10
+
11
+ # Seek a specific position; unlike 'arrive', will not slow down to stop at that pos.
12
+ # See http://www.red3d.com/cwr/steer/
13
+ #
14
+ # * *Args* :
15
+ # - +kinematic+ -> my seeking thing
16
+ # - +goal_position+ -> the position-vector where we want to go
17
+ # * *Returns* :
18
+ # - the calculated steering force
19
+ #
20
+ def self.steer(kinematic, goal_position)
21
+ desired_velocity = (goal_position - kinematic.position_vec).normalize * kinematic.max_speed
22
+ desired_velocity - kinematic.velocity_vec
23
+ end
24
+ end
@@ -0,0 +1,56 @@
1
+ ##
2
+ # Copyright 2013, Prylis Incorporated.
3
+ #
4
+ # This file is part of The Ruby Steering Behaviors Library.
5
+ # http://github.com/cpowell/steering-behaviors
6
+ # You can redistribute and/or modify this software only in accordance with
7
+ # the terms found in the "LICENSE" file included with the framework.
8
+
9
+ class SteeringBehaviors::Steering
10
+
11
+ # Given a steering force vector, alter course and velocity accordingly.
12
+ # Takes turn rate limitations, mass, and other limits into account, and
13
+ # directly alters the provided Mobile component.
14
+ #
15
+ # * *Args* :
16
+ # - +kinematic+ -> the kinematic thing
17
+ # - +steering_force+ -> force vector supplied by a steering behavior
18
+ # - +delta+ -> time delta (in seconds) used for scaling the result
19
+ # - +accelerative+ -> whether you want the steering to change the thing's velocity (default true)
20
+ #
21
+ def self.feel_the_force(kinematic, steering_force, delta, accelerative=true) #, mobile, position)
22
+ acceleration = steering_force / kinematic.mass
23
+
24
+ # Compute the new, proposed velocity vector.
25
+ desired_velocity = kinematic.velocity_vec + (acceleration * delta)
26
+
27
+ desired_velocity.truncate!(kinematic.speed) if !accelerative
28
+ desired_velocity.truncate!(kinematic.max_speed)
29
+
30
+ # If this timeslice's proposed velocity-vector exceeds the turn rate,
31
+ # come up with a revised velociy-vec that doesn't exceed the rate -- and use that.
32
+ angle = Math.acos kinematic.heading_vec.dot(desired_velocity.normalize)
33
+ max_course_change = kinematic.max_turn * delta
34
+
35
+ if angle.abs > max_course_change
36
+ direction = SteeringBehaviors::Vector.sign(kinematic.velocity_vec, desired_velocity) # -1==CCW, 1==CW
37
+ limited_crse = kinematic.heading_vec.radians - max_course_change * direction
38
+
39
+ # printf "Current %0.4f. Angle %0.4f %s exceeds max change %0.4f. Desired course [%0.4f], limited course [%0.4f]\n",
40
+ # kinematic.heading_vec.radians,
41
+ # angle,
42
+ # (direction==1 ? 'CW' : 'CCW'),
43
+ # max_course_change,
44
+ # desired_velocity.radians,
45
+ # limited_crse
46
+
47
+ kinematic.velocity_vec = SteeringBehaviors::Vector.new(
48
+ Math.sin(limited_crse) * kinematic.speed,
49
+ Math.cos(limited_crse) * kinematic.speed
50
+ )
51
+ else
52
+ kinematic.velocity_vec = desired_velocity
53
+ end
54
+ end
55
+
56
+ end
@@ -0,0 +1,185 @@
1
+ ##
2
+ # Copyright 2013, Prylis Incorporated.
3
+ #
4
+ # This file is part of The Ruby Steering Behaviors Library.
5
+ # http://github.com/cpowell/steering-behaviors
6
+ # You can redistribute and/or modify this software only in accordance with
7
+ # the terms found in the "LICENSE" file included with the framework.
8
+
9
+ class SteeringBehaviors::Vector
10
+ attr_reader :x, :y
11
+
12
+ def initialize(x=0,y=0)
13
+ @x = x.to_f
14
+ @y = y.to_f
15
+ end
16
+
17
+ def ==(other)
18
+ self.class == other.class && @x==other.x && @y==other.y
19
+ end
20
+ alias_method :eql?, :==
21
+
22
+ def x=(x)
23
+ @x = x.to_f
24
+ end
25
+
26
+ def y=(y)
27
+ @y = y.to_f
28
+ end
29
+
30
+ def length
31
+ Math.sqrt(@x**2 + @y**2)
32
+ end
33
+
34
+ def +(v)
35
+ SteeringBehaviors::Vector.new(@x + v.x, @y + v.y)
36
+ end
37
+
38
+ def /(n)
39
+ SteeringBehaviors::Vector.new(@x/n, @y/n)
40
+ end
41
+
42
+ def *(n)
43
+ SteeringBehaviors::Vector.new(@x*n, @y*n)
44
+ end
45
+
46
+ def -(v)
47
+ SteeringBehaviors::Vector.new(@x - v.x, @y - v.y)
48
+ end
49
+
50
+ def delta(other)
51
+ (( ( other.radians - self.radians + Math::PI + 2*Math::PI ) % (2*Math::PI) ) - Math::PI).abs
52
+ end
53
+
54
+ def normalize!
55
+ orig_length = length
56
+ return self if orig_length == 1.0 || orig_length == 0
57
+
58
+ @x /= orig_length
59
+ @y /= orig_length
60
+
61
+ self
62
+ end
63
+
64
+ def normalize
65
+ orig_length = length
66
+ return self if orig_length == 1.0 || orig_length == 0
67
+
68
+ SteeringBehaviors::Vector.new(@x/orig_length, @y/orig_length)
69
+ end
70
+
71
+ def truncate!(max)
72
+ return self if length < max
73
+
74
+ self.normalize!
75
+ self.x *= max
76
+ self.y *= max
77
+
78
+ self
79
+ end
80
+
81
+ # A · B = A.x * B.x + A.y * B.y
82
+ def dot(b)
83
+ val = @x*b.x + @y*b.y
84
+ val = 1.0 if val > 1.0
85
+ val = -1.0 if val < -1.0
86
+
87
+ val
88
+ end
89
+
90
+ def perpendicular
91
+ SteeringBehaviors::Vector.new(@y, -@x)
92
+ end
93
+
94
+ def compass_bearing(y_down_more_positive=false)
95
+ if y_down_more_positive
96
+ up = SteeringBehaviors::Vector.new(0, -1)
97
+ else
98
+ up = SteeringBehaviors::Vector.new(0, 1)
99
+ end
100
+
101
+ theta = Math.acos(self.normalize.dot(up))
102
+
103
+ theta *= -1 if @x < 0
104
+ degs = SteeringBehaviors::Vector.rad2deg(theta)
105
+ degs += 360 if degs < 0
106
+
107
+ degs
108
+ end
109
+
110
+ def sign(other)
111
+ SteeringBehaviors::Vector.sign(self, other)
112
+ end
113
+
114
+ def radians
115
+ theta = Math.acos(@y/length)
116
+ if @x < 0
117
+ theta *= -1
118
+ end
119
+
120
+ theta % (2 * Math::PI)
121
+ end
122
+
123
+ def from_compass_bearing!(brg)
124
+ rad = SteeringBehaviors::Vector.deg2rad(brg)
125
+ self.x = Math.sin(rad)
126
+ self.y = Math.cos(rad)
127
+ end
128
+
129
+ def rotate!(radians)
130
+ circle_cos = Math.cos(-radians)
131
+ circle_sin = Math.sin(-radians)
132
+
133
+ x_rot = circle_cos * x - circle_sin * y
134
+ y_rot = circle_sin * x + circle_cos * y
135
+
136
+ self.x, self.y = x_rot, y_rot
137
+ self
138
+ end
139
+
140
+ def rotate(radians)
141
+ circle_cos = Math.cos(-radians)
142
+ circle_sin = Math.sin(-radians)
143
+
144
+ x_rot = circle_cos * x - circle_sin * y
145
+ y_rot = circle_sin * x + circle_cos * y
146
+
147
+ SteeringBehaviors::Vector.new(x_rot, y_rot)
148
+ end
149
+
150
+ def self.sign(v1, v2)
151
+ if v1.y * v2.x > v1.x*v2.y
152
+ return -1 # clockwise
153
+ else
154
+ return 1 # anti-clockwise
155
+ end
156
+ end
157
+
158
+ # def self.position_to_world_coords(point, heading_vec, pos)
159
+ # local_angle = heading_vec.radians + point.radians
160
+
161
+ # x = Math.sin(local_angle) * point.length
162
+ # y = Math.cos(local_angle) * point.length
163
+
164
+ # world_point = SteeringBehaviors::Vector.new(x,y) + pos
165
+ # world_point
166
+ # end
167
+
168
+ def self.deg2rad(d)
169
+ d * 0.017453292519943295 # Math::PI / 180.0
170
+ end
171
+
172
+ def self.rad2deg(r)
173
+ r * 57.29577951308232 # 180.0 / Math::PI
174
+ end
175
+
176
+ def self.from_compass_bearing(brg)
177
+ rad = SteeringBehaviors::Vector.deg2rad(brg)
178
+ SteeringBehaviors::Vector.new(Math.sin(rad), Math.cos(rad))
179
+ end
180
+
181
+ def to_s
182
+ format("Vector {[%.7f, %.7f] len %0.7f}", @x, @y, length)
183
+ end
184
+
185
+ end