Mercurial > projects > openmelee
annotate ai/steer.d @ 21:cad384ad349e
avoid
author | zzzzrrr <mason.green@gmail.com> |
---|---|
date | Thu, 26 Mar 2009 07:02:56 -0400 |
parents | 6efd0830715b |
children | 4fce5596d1f6 |
rev | line source |
---|---|
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
1 /* |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
2 * Copyright (c) 2009, Mason Green (zzzzrrr) |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
3 * http://www.dsource.org/projects/openmelee |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
4 * Based on OpenSteer, Copyright (c) 2002-2003, Sony Computer Entertainment America |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
5 * Original author: Craig Reynolds |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
6 * |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
7 * All rights reserved. |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
8 * |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
9 * Redistribution and use in source and binary forms, with or without modification, |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
10 * are permitted provided that the following conditions are met: |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
11 * |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
12 * * Redistributions of source code must retain the above copyright notice, |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
13 * this list of conditions and the following disclaimer. |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
14 * * Redistributions in binary form must reproduce the above copyright notice, |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
15 * this list of conditions and the following disclaimer in the documentation |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
16 * and/or other materials provided with the distribution. |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
17 * * Neither the name of the polygonal nor the names of its contributors may be |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
18 * used to endorse or promote products derived from this software without specific |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
19 * prior written permission. |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
20 * |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
32 */ |
18 | 33 module openmelee.ai.steer; |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
34 |
19 | 35 import tango.io.Stdout : Stdout; |
36 | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
37 import blaze.common.bzMath: bzDot, bzClamp, bzVec2; |
19 | 38 import blaze.collision.shapes.bzShape : bzShape; |
39 import tango.math.Math : sqrt; | |
15 | 40 import blaze.dynamics.bzBody: bzBody; |
41 | |
18 | 42 import openmelee.ships.ship : Ship, State; |
43 import openmelee.ai.utilities; | |
0 | 44 |
9 | 45 class Steer |
46 { | |
47 // Constructor: initializes state | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
48 this (Ship ship) |
0 | 49 { |
19 | 50 m_ship = ship; |
51 m_body = ship.rBody; | |
52 m_radius = 0.0f; | |
53 | |
54 // Find radius of body | |
55 for (bzShape s = m_body.shapeList; s; s = s.next) { | |
56 if(s.sweepRadius > m_radius) { | |
57 m_radius = s.sweepRadius; | |
58 } | |
59 } | |
15 | 60 } |
61 | |
62 struct PathIntersection | |
63 { | |
64 bool intersect; | |
65 float distance; | |
66 bzVec2 surfacePoint; | |
67 bzVec2 surfaceNormal; | |
68 bzBody obstacle; | |
9 | 69 } |
0 | 70 |
9 | 71 // reset state |
72 void reset () { | |
73 // initial state of wander behavior | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
74 m_wanderSide = 0; |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
75 m_wanderUp = 0; |
9 | 76 } |
77 | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
78 void update() { |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
79 m_position = m_ship.state.position; |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
80 m_velocity = m_ship.state.velocity; |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
81 m_speed = m_ship.state.speed; |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
82 m_maxForce = m_ship.state.maxForce; |
12 | 83 m_forward = m_ship.state.forward; |
9 | 84 } |
0 | 85 |
9 | 86 // -------------------------------------------------- steering behaviors |
0 | 87 |
9 | 88 bzVec2 steerForWander (float dt) { |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
89 // random walk m_wanderSide and m_wanderUp between -1 and +1 |
9 | 90 float speed = 12 * dt; // maybe this (12) should be an argument? |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
91 m_wanderSide = scalarRandomWalk (m_wanderSide, speed, -1, +1); |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
92 m_wanderUp = scalarRandomWalk (m_wanderUp, speed, -1, +1); |
0 | 93 |
9 | 94 // return a pure lateral steering vector: (+/-Side) + (+/-Up) |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
95 return (m_side * m_wanderSide) + (m_up * m_wanderUp); |
9 | 96 } |
0 | 97 |
9 | 98 // Seek behavior |
99 bzVec2 steerForSeek (bzVec2 target) { | |
100 bzVec2 desiredVelocity = target - m_position; | |
101 return desiredVelocity - m_velocity; | |
102 } | |
0 | 103 |
9 | 104 // Flee behavior |
105 bzVec2 steerForFlee (bzVec2 target) { | |
106 bzVec2 desiredVelocity = m_position - target; | |
107 return desiredVelocity - m_velocity; | |
108 } | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
109 |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
110 /* |
9 | 111 // xxx proposed, experimental new seek/flee [cwr 9-16-02] |
112 bzVec2 xxxsteerForFlee (bzVec2 target) { | |
113 bzVec2 offset = m_position - target; | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
114 bzVec2 desiredVelocity = bzClamp(offset.truncateLength (maxSpeed ()); |
9 | 115 return desiredVelocity - m_velocity; |
116 } | |
0 | 117 |
9 | 118 bzVec2 xxxsteerForSeek (bzVec2 target) { |
119 // bzVec2 offset = target - position; | |
120 bzVec2 offset = target - m_position; | |
121 bzVec2 desiredVelocity = offset.truncateLength (maxSpeed ()); //xxxnew | |
122 return desiredVelocity - m_velocity; | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
123 } |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
124 */ |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
125 |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
126 /* |
9 | 127 // ------------------------------------------------------------------------ |
128 // Obstacle Avoidance behavior | |
129 // | |
130 // Returns a steering force to avoid a given obstacle. The purely | |
131 // lateral steering force will turn our vehicle towards a silhouette edge | |
132 // of the obstacle. Avoidance is required when (1) the obstacle | |
133 // intersects the vehicle's current path, (2) it is in front of the | |
134 // vehicle, and (3) is within minTimeToCollision seconds of travel at the | |
135 // vehicle's current velocity. Returns a zero vector value (bzVec2::zero) | |
136 // when no avoidance is required. | |
137 bzVec2 steerToAvoidObstacle (float minTimeToCollision, Obstacle obstacle) { | |
0 | 138 |
9 | 139 bzVec2 avoidance = obstacle.steerToAvoid (this, minTimeToCollision); |
140 return avoidance; | |
141 } | |
142 | |
15 | 143 // avoids all obstacles in an ObstacleGroup |
144 */ | |
145 | |
146 bzVec2 steerToAvoidObstacles (float minTimeToCollision, bzBody obstacles) { | |
0 | 147 |
9 | 148 bzVec2 avoidance; |
149 PathIntersection nearest, next; | |
15 | 150 float minDistanceToCollision = minTimeToCollision * m_speed; |
19 | 151 |
9 | 152 next.intersect = false; |
153 nearest.intersect = false; | |
0 | 154 |
9 | 155 // test all obstacles for intersection with my forward axis, |
156 // select the one whose point of intersection is nearest | |
19 | 157 for (bzBody o = obstacles; o; o = o.next) { |
158 | |
159 if(o is m_body) continue; | |
160 | |
15 | 161 // This code which presumes the obstacle is spherical |
20 | 162 findNextIntersectionWithSphere(o, next, minDistanceToCollision); |
0 | 163 |
15 | 164 if (!nearest.intersect || (next.intersect && next.distance < nearest.distance)) { |
165 nearest = next; | |
166 } | |
9 | 167 } |
19 | 168 |
9 | 169 // when a nearest intersection was found |
15 | 170 if (nearest.intersect && (nearest.distance < minDistanceToCollision)) { |
19 | 171 Stdout("Distance: ")(nearest.distance).newline; |
9 | 172 // compute avoidance steering force: take offset from obstacle to me, |
173 // take the component of that which is lateral (perpendicular to my | |
174 // forward direction), set length to maxForce, add a bit of forward | |
175 // component (in capture the flag, we never want to slow down) | |
15 | 176 bzVec2 offset = m_position - nearest.obstacle.position; |
19 | 177 avoidance = perpendicularComponent(offset, m_forward); |
15 | 178 avoidance.normalize; |
19 | 179 //avoidance *= m_maxForce; |
180 //avoidance += m_forward * m_maxForce * 0.75f; | |
0 | 181 } |
182 | |
9 | 183 return avoidance; |
184 } | |
21 | 185 |
186 bzVec2 avoid(float minTimeToCollision, bzBody obstacles) { | |
187 | |
188 float avoidMargin = 1.0f; | |
189 float maxLookahead = minTimeToCollision * m_speed; | |
190 | |
191 // Make sure we're moving | |
192 if (m_velocity.length > 0) | |
193 { | |
194 for (bzBody o = obstacles; o; o = o.next) { | |
195 | |
196 if(o is m_body) continue; | |
197 | |
198 // Find the distance from the line we're moving along to the obstacle. | |
199 bzVec2 movementNormal = m_velocity; | |
200 movementNormal.normalize(); | |
201 bzVec2 characterToObstacle = o.position - m_position; | |
202 | |
203 real distanceSquared = bzDot(characterToObstacle, movementNormal); | |
204 distanceSquared = characterToObstacle.length - | |
205 distanceSquared*distanceSquared; | |
206 | |
207 // Check for collision | |
208 // Find radius of obstacle | |
209 float oRad = 0; | |
210 for (bzShape shape = o.shapeList; shape; shape = shape.next) { | |
211 if(shape.sweepRadius > oRad) { | |
212 oRad = shape.sweepRadius; | |
213 } | |
214 } | |
215 | |
216 real radius = oRad + avoidMargin; | |
217 if (distanceSquared < radius*radius) | |
218 { | |
219 // Find how far along our movement vector the closest pass is | |
220 real distanceToClosest = bzDot(characterToObstacle, movementNormal); | |
221 | |
222 // Make sure this isn't behind us and is closer than our lookahead. | |
223 if (distanceToClosest > 0 && distanceToClosest < maxLookahead) | |
224 { | |
225 // Find the closest point | |
226 bzVec2 closestPoint = | |
227 o.position + movementNormal*distanceToClosest; | |
228 | |
229 // Find the point of avoidance | |
230 bzVec2 target = | |
231 o.position + | |
232 (closestPoint - o.position).length * | |
233 (oRad + avoidMargin); | |
234 | |
235 return target; | |
236 } | |
237 } | |
238 } | |
239 } | |
240 return bzVec2.zeroVect; | |
241 | |
242 } | |
15 | 243 |
19 | 244 void findNextIntersectionWithSphere(bzBody obs, |
20 | 245 inout PathIntersection intersection, float mdc) { |
19 | 246 |
247 // This routine is based on the Paul Bourke's derivation in: | |
248 // Intersection of a Line and a Sphere (or circle) | |
249 // http://www.swin.edu.au/astronomy/pbourke/geometry/sphereline/ | |
250 | |
251 float b, c, d, p, q, s; | |
252 bzVec2 lc; | |
253 | |
254 // initialize pathIntersection object | |
255 intersection.intersect = false; | |
256 intersection.obstacle = obs; | |
257 | |
258 // find "local center" (lc) of sphere in boid's coordinate space | |
259 lc = m_body.localPoint(obs.position); | |
260 | |
261 // computer line-sphere intersection parameters | |
262 b = 0; | |
263 | |
264 // Find radius of obstacle | |
265 float obsRadius = 0; | |
266 for (bzShape shape = obs.shapeList; shape; shape = shape.next) { | |
267 if(shape.sweepRadius > obsRadius) { | |
268 obsRadius = shape.sweepRadius; | |
269 } | |
270 } | |
271 | |
20 | 272 c = square(lc.x) + square(lc.y) - square(obsRadius + m_radius+mdc); |
19 | 273 d = (b * b) - (4 * c); |
274 | |
275 // when the path does not intersect the sphere | |
276 if (d < 0) return; | |
277 | |
278 // otherwise, the path intersects the sphere in two points with | |
279 // parametric coordinates of "p" and "q". | |
280 // (If "d" is zero the two points are coincident, the path is tangent) | |
281 s = sqrt(d); | |
282 p = (-b + s) * 0.5f; | |
283 q = (-b - s) * 0.5f; | |
284 | |
285 // both intersections are behind us, so no potential collisions | |
286 if ((p < 0) && (q < 0)) return; | |
287 | |
288 // at least one intersection is in front of us | |
289 intersection.intersect = true; | |
290 intersection.distance = | |
291 ((p > 0) && (q > 0)) ? | |
292 // both intersections are in front of us, find nearest one | |
293 ((p < q) ? p : q) : | |
294 // otherwise only one intersections is in front, select it | |
295 ((p > 0) ? p : q); | |
296 } | |
297 | |
15 | 298 /* |
9 | 299 // ------------------------------------------------------------------------ |
300 // Unaligned collision avoidance behavior: avoid colliding with other | |
301 // nearby vehicles moving in unconstrained directions. Determine which | |
302 // (if any) other other vehicle we would collide with first, then steers | |
303 // to avoid the site of that potential collision. Returns a steering | |
304 // force vector, which is zero length if there is no impending collision. | |
305 | |
306 bzVec2 steerToAvoidNeighbors (float minTimeToCollision, AVGroup others) { | |
0 | 307 |
9 | 308 // first priority is to prevent immediate interpenetration |
309 bzVec2 separation = steerToAvoidCloseNeighbors (0, others); | |
310 if (separation != bzVec2::zero) return separation; | |
0 | 311 |
9 | 312 // otherwise, go on to consider potential future collisions |
313 float steer = 0; | |
15 | 314 Ship threat; |
9 | 315 |
316 // Time (in seconds) until the most immediate collision threat found | |
317 // so far. Initial value is a threshold: don't look more than this | |
318 // many frames into the future. | |
319 float minTime = minTimeToCollision; | |
0 | 320 |
9 | 321 // xxx solely for annotation |
322 bzVec2 xxxThreatPositionAtNearestApproach; | |
323 bzVec2 xxxOurPositionAtNearestApproach; | |
0 | 324 |
9 | 325 // for each of the other vehicles, determine which (if any) |
326 // pose the most immediate threat of collision. | |
327 for (AVIterator i = others.begin(); i != others.end(); i++) | |
328 { | |
15 | 329 Ship other = i; |
330 if (other !is this) | |
0 | 331 { |
9 | 332 // avoid when future positions are this close (or less) |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
333 float collisionDangerThreshold = radius * 2; |
9 | 334 |
335 // predicted time until nearest approach of "this" and "other" | |
336 float time = predictNearestApproachTime (other); | |
0 | 337 |
9 | 338 // If the time is in the future, sooner than any other |
339 // threatened collision... | |
340 if ((time >= 0) (time < minTime)) | |
341 { | |
342 // if the two will be close enough to collide, | |
343 // make a note of it | |
344 if (computeNearestApproachPositions (other, time) | |
345 < collisionDangerThreshold) | |
0 | 346 { |
9 | 347 minTime = time; |
348 threat = other; | |
349 xxxThreatPositionAtNearestApproach | |
350 = hisPositionAtNearestApproach; | |
351 xxxOurPositionAtNearestApproach | |
352 = ourPositionAtNearestApproach; | |
0 | 353 } |
354 } | |
355 } | |
9 | 356 } |
0 | 357 |
9 | 358 // if a potential collision was found, compute steering to avoid |
359 if (threat) | |
360 { | |
361 // parallel: +1, perpendicular: 0, anti-parallel: -1 | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
362 float parallelness = m_forward.dot(threat.forward); |
9 | 363 float angle = 0.707f; |
364 | |
365 if (parallelness < -angle) | |
0 | 366 { |
9 | 367 // anti-parallel "head on" paths: |
368 // steer away from future threat position | |
369 bzVec2 offset = xxxThreatPositionAtNearestApproach - m_position; | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
370 float sideDot = offset.dot(m_side()); |
9 | 371 steer = (sideDot > 0) ? -1.0f : 1.0f; |
372 } | |
373 else | |
374 { | |
375 if (parallelness > angle) | |
0 | 376 { |
9 | 377 // parallel paths: steer away from threat |
378 bzVec2 offset = threat.position - m_position; | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
379 float sideDot = bzDot(offset, m_side); |
0 | 380 steer = (sideDot > 0) ? -1.0f : 1.0f; |
381 } | |
382 else | |
383 { | |
9 | 384 // perpendicular paths: steer behind threat |
385 // (only the slower of the two does this) | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
386 if (threat.speed() <= speed) |
0 | 387 { |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
388 float sideDot = bzDot(m_side, threat.velocity); |
0 | 389 steer = (sideDot > 0) ? -1.0f : 1.0f; |
390 } | |
391 } | |
392 } | |
393 } | |
394 | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
395 return m_side() * steer; |
9 | 396 } |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
397 */ |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
398 |
9 | 399 // Given two vehicles, based on their current positions and velocities, |
400 // determine the time until nearest approach | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
401 float predictNearestApproachTime (State other) { |
9 | 402 |
403 // imagine we are at the origin with no velocity, | |
404 // compute the relative velocity of the other vehicle | |
405 bzVec2 myVelocity = m_velocity; | |
406 bzVec2 otherVelocity = other.velocity; | |
407 bzVec2 relVelocity = otherVelocity - myVelocity; | |
408 float relSpeed = relVelocity.length; | |
409 | |
410 // for parallel paths, the vehicles will always be at the same distance, | |
411 // so return 0 (aka "now") since "there is no time like the present" | |
412 if (relSpeed == 0) return 0; | |
413 | |
414 // Now consider the path of the other vehicle in this relative | |
415 // space, a line defined by the relative position and velocity. | |
416 // The distance from the origin (our vehicle) to that line is | |
417 // the nearest approach. | |
418 | |
419 // Take the unit tangent along the other vehicle's path | |
420 bzVec2 relTangent = relVelocity / relSpeed; | |
421 | |
422 // find distance from its path to origin (compute offset from | |
423 // other to us, find length of projection onto path) | |
424 bzVec2 relPosition = m_position - other.position; | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
425 float projection = bzDot(relTangent, relPosition); |
9 | 426 |
427 return projection / relSpeed; | |
428 } | |
0 | 429 |
9 | 430 // Given the time until nearest approach (predictNearestApproachTime) |
431 // determine position of each vehicle at that time, and the distance | |
432 // between them | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
433 float computeNearestApproachPositions (State other, float time) { |
0 | 434 |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
435 bzVec2 myTravel = m_forward * m_speed * time; |
9 | 436 bzVec2 otherTravel = other.forward * other.speed * time; |
437 | |
438 bzVec2 myFinal = m_position + myTravel; | |
439 bzVec2 otherFinal = other.position + otherTravel; | |
0 | 440 |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
441 return (myFinal - otherFinal).length; |
9 | 442 } |
443 | |
444 // ------------------------------------------------------------------------ | |
445 // pursuit of another vehicle ( version with ceiling on prediction time) | |
446 | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
447 bzVec2 steerForPursuit (State quarry) { |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
448 return steerForPursuit (quarry, float.max); |
9 | 449 } |
450 | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
451 bzVec2 steerForPursuit (State quarry, float maxPredictionTime) { |
0 | 452 |
9 | 453 // offset from this to quarry, that distance, unit vector toward quarry |
454 bzVec2 offset = quarry.position - m_position; | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
455 float distance = offset.length; |
9 | 456 bzVec2 unitOffset = offset / distance; |
457 | |
458 // how parallel are the paths of "this" and the quarry | |
459 // (1 means parallel, 0 is pependicular, -1 is anti-parallel) | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
460 float parallelness = bzDot(m_forward , quarry.forward); |
9 | 461 |
462 // how "forward" is the direction to the quarry | |
463 // (1 means dead ahead, 0 is directly to the side, -1 is straight back) | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
464 float forwardness = bzDot(m_forward , unitOffset); |
0 | 465 |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
466 float directTravelTime = distance / m_speed; |
9 | 467 int f = intervalComparison (forwardness, -0.707f, 0.707f); |
468 int p = intervalComparison (parallelness, -0.707f, 0.707f); | |
469 | |
470 float timeFactor = 0; // to be filled in below | |
0 | 471 |
9 | 472 // Break the pursuit into nine cases, the cross product of the |
473 // quarry being [ahead, aside, or behind] us and heading | |
474 // [parallel, perpendicular, or anti-parallel] to us. | |
475 switch (f) | |
476 { | |
477 case +1: | |
478 switch (p) | |
0 | 479 { |
9 | 480 case +1: // ahead, parallel |
481 timeFactor = 4; | |
0 | 482 break; |
9 | 483 case 0: // ahead, perpendicular |
484 timeFactor = 1.8f; | |
0 | 485 break; |
9 | 486 case -1: // ahead, anti-parallel |
487 timeFactor = 0.85f; | |
0 | 488 break; |
489 } | |
9 | 490 break; |
491 case 0: | |
492 switch (p) | |
493 { | |
494 case +1: // aside, parallel | |
495 timeFactor = 1; | |
496 break; | |
497 case 0: // aside, perpendicular | |
498 timeFactor = 0.8f; | |
499 break; | |
500 case -1: // aside, anti-parallel | |
501 timeFactor = 4; | |
502 break; | |
503 } | |
504 break; | |
505 case -1: | |
506 switch (p) | |
507 { | |
508 case +1: // behind, parallel | |
509 timeFactor = 0.5f; | |
510 break; | |
511 case 0: // behind, perpendicular | |
512 timeFactor = 2; | |
513 break; | |
514 case -1: // behind, anti-parallel | |
515 timeFactor = 2; | |
516 break; | |
517 } | |
518 break; | |
519 } | |
0 | 520 |
9 | 521 // estimated time until intercept of quarry |
522 float et = directTravelTime * timeFactor; | |
523 | |
524 // xxx experiment, if kept, this limit should be an argument | |
525 float etl = (et > maxPredictionTime) ? maxPredictionTime : et; | |
526 | |
527 // estimated position of quarry at intercept | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
528 bzVec2 target = quarry.predictFuturePosition(etl); |
0 | 529 |
12 | 530 return target; //steerForSeek (target); |
9 | 531 } |
532 | |
533 // ------------------------------------------------------------------------ | |
534 // evasion of another vehicle | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
535 bzVec2 steerForEvasion (State menace, float maxPredictionTime) { |
0 | 536 |
9 | 537 // offset from this to menace, that distance, unit vector toward menace |
538 bzVec2 offset = menace.position - m_position; | |
539 float distance = offset.length; | |
0 | 540 |
9 | 541 float roughTime = distance / menace.speed; |
542 float predictionTime = ((roughTime > maxPredictionTime) ? maxPredictionTime : roughTime); | |
543 bzVec2 target = menace.predictFuturePosition (predictionTime); | |
544 | |
545 return steerForFlee (target); | |
546 } | |
0 | 547 |
548 | |
9 | 549 // ------------------------------------------------------------------------ |
550 // tries to maintain a given speed, returns a maxForce-clipped steering | |
551 // force along the forward/backward axis | |
552 bzVec2 steerForTargetSpeed (float targetSpeed) { | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
553 float mf = m_maxForce; |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
554 float speedError = targetSpeed - m_speed; |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
555 return m_forward * bzClamp(speedError, -mf, +mf); |
9 | 556 } |
0 | 557 |
558 | |
9 | 559 // ----------------------------------------------------------- utilities |
560 bool isAhead (bzVec2 target) {return isAhead (target, 0.707f);}; | |
561 bool isAside (bzVec2 target) {return isAside (target, 0.707f);}; | |
562 bool isBehind (bzVec2 target) {return isBehind (target, -0.707f);}; | |
0 | 563 |
9 | 564 bool isAhead (bzVec2 target, float cosThreshold) |
565 { | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
566 bzVec2 targetDirection = target - m_position; |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
567 targetDirection.normalize(); |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
568 return bzDot(m_forward, targetDirection) > cosThreshold; |
9 | 569 } |
0 | 570 |
9 | 571 bool isAside (bzVec2 target, float cosThreshold) |
572 { | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
573 bzVec2 targetDirection = target - m_position; |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
574 targetDirection.normalize(); |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
575 float dp = bzDot(m_forward, targetDirection); |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
576 return (dp < cosThreshold) && (dp > -cosThreshold); |
9 | 577 } |
0 | 578 |
9 | 579 bool isBehind (bzVec2 target, float cosThreshold) |
580 { | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
581 bzVec2 targetDirection = target - m_position; |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
582 targetDirection.normalize(); |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
583 return bzDot(m_forward, targetDirection) < cosThreshold; |
9 | 584 } |
585 | |
586 private: | |
587 | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
588 Ship m_ship; |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
589 |
9 | 590 bzVec2 m_position; |
591 bzVec2 m_velocity; | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
592 bzVec2 m_up; |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
593 bzVec2 m_side; |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
594 bzVec2 m_forward; |
19 | 595 float m_radius; |
596 bzBody m_body; | |
11
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
597 |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
598 float m_speed = 0; |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
599 float m_maxForce = 0; |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
600 |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
601 // Wander behavior |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
602 float m_wanderSide; |
d998bf1b0654
Added utilities and AI; fixed steer
Mason Green <mason.green@gmail.com>
parents:
9
diff
changeset
|
603 float m_wanderUp; |
9 | 604 } |