comparison ai/steer.d @ 24:441eb7672404

impleneted steer to avoid
author zzzzrrr <mason.green@gmail.com>
date Fri, 27 Mar 2009 16:05:24 -0400
parents e79347dd38a3
children 88cca12cc8b9
comparison
equal deleted inserted replaced
23:e79347dd38a3 24:441eb7672404
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */ 32 */
33 module openmelee.ai.steer; 33 module openmelee.ai.steer;
34 34
35 import tango.io.Stdout : Stdout; 35 import tango.io.Stdout : Stdout;
36 import tango.util.container.LinkedList : LinkedList;
36 37
37 import blaze.common.bzMath: bzDot, bzClamp, bzVec2; 38 import blaze.common.bzMath: bzDot, bzClamp, bzVec2;
38 import blaze.collision.shapes.bzShape : bzShape; 39 import blaze.collision.shapes.bzShape : bzShape;
39 import tango.math.Math : sqrt; 40 import tango.math.Math : sqrt;
40 import blaze.dynamics.bzBody: bzBody; 41 import blaze.dynamics.bzBody: bzBody;
41 42
42 import openmelee.ships.ship : Ship, State; 43 import openmelee.ships.ship : Ship, State;
43 import openmelee.ai.utilities; 44 import openmelee.ai.utilities;
45 import openmelee.ai.ai : Threat;
46
47 alias LinkedList!(Ship) ObjectList;
44 48
45 class Steer 49 class Steer
46 { 50 {
51
52 ObjectList objectList;
53
47 // Constructor: initializes state 54 // Constructor: initializes state
48 this (Ship ship) 55 this (Ship ship, ObjectList objectList)
49 { 56 {
57 this.objectList = objectList;
50 m_ship = ship; 58 m_ship = ship;
51 m_body = ship.rBody; 59 m_body = ship.rBody;
52 } 60 }
53 61
54 struct PathIntersection 62 struct PathIntersection
130 bzVec2 steerToAvoidObstacle (float minTimeToCollision, Obstacle obstacle) { 138 bzVec2 steerToAvoidObstacle (float minTimeToCollision, Obstacle obstacle) {
131 139
132 bzVec2 avoidance = obstacle.steerToAvoid (this, minTimeToCollision); 140 bzVec2 avoidance = obstacle.steerToAvoid (this, minTimeToCollision);
133 return avoidance; 141 return avoidance;
134 } 142 }
135
136 // avoids all obstacles in an ObstacleGroup
137 */ 143 */
138 144
139 bzVec2 steerToAvoidObstacles (float minTimeToCollision, bzBody obstacles) { 145 // Steer to avoid
140 146 void collisionThreat(inout Threat threat, float maxLookAhead = 10.0f) {
141 bzVec2 avoidance; 147
142 PathIntersection nearest, next; 148 // 1. Find the target that’s closest to collision
143 float minDistanceToCollision = 10; //minTimeToCollision * m_speed;
144 149
145 next.intersect = false; 150 float radius = m_radius;
146 nearest.intersect = false; 151 float rad = 0.0f;
147 152 float shortestTime = float.max;
148 // test all obstacles for intersection with my forward axis, 153
149 // select the one whose point of intersection is nearest 154 // Loop through each target
150 for (bzBody o = obstacles; o; o = o.next) { 155 foreach(obstacle; objectList) {
156
157 bzBody target = obstacle.rBody;
151 158
152 if(o is m_body) continue;
153
154 // This code which presumes the obstacle is spherical
155 findNextIntersectionWithSphere(o, next);
156
157 if (!nearest.intersect || (next.intersect && next.distance < nearest.distance)) {
158 nearest = next;
159 }
160 }
161
162 // when a nearest intersection was found
163 if (nearest.intersect && (nearest.distance < minDistanceToCollision)) {
164 // compute avoidance steering force: take offset from obstacle to me,
165 // take the component of that which is lateral (perpendicular to my
166 // forward direction), set length to maxForce, add a bit of forward
167 // component (in capture the flag, we never want to slow down)
168 bzVec2 offset = m_position - nearest.obstacle.position;
169 avoidance = perpendicularComponent(offset, m_forward);
170 avoidance.normalize;
171 avoidance = m_body.localPoint(avoidance);
172 ///avoidance *= m_maxForce;
173 //avoidance += m_forward * m_maxForce * 0.75f;
174 }
175
176 auto state = cast(State) m_body.userData;
177 state.avoid = avoidance;
178 return avoidance;
179 }
180
181 bzVec2 getSteering(bzBody obstacles) {
182
183 // 1. Find the target that’s closest to collision
184
185 float radius = m_radius;
186 float rad;
187 // Store the first collision time
188 float shortestTime = 0.5;
189
190 // Store the target that collides then, and other data
191 // that we will need and can avoid recalculating
192 bzBody firstTarget = null;
193 float firstMinSeparation = 0;
194 float firstDistance = 0;
195 bzVec2 firstRelativePos;
196 bzVec2 firstRelativeVel;
197 bzVec2 relativePos;
198
199 // Loop through each target
200 for (bzBody target = obstacles; target; target = target.next) {
201
202 if(target is m_body) continue; 159 if(target is m_body) continue;
203 160
204 // Calculate the time to collision 161 // Calculate the time to collision
205 relativePos = m_body.localPoint(target.position); // - m_position; 162 bzVec2 relativePos = target.position - m_position;
206 bzVec2 relativeVel = target.linearVelocity - m_velocity; 163 bzVec2 relativeVel = m_velocity - target.linearVelocity;
207 float relativeSpeed = relativeVel.length; 164 float relativeSpeed = relativeVel.length;
208 float timeToCollision = bzDot(relativePos, relativeVel) / 165 // Time to closest point of approach
166 float timeToCPA = bzDot(relativePos, relativeVel) /
209 (relativeSpeed * relativeSpeed); 167 (relativeSpeed * relativeSpeed);
210 168
211 // Check if it is going to be a collision at all 169 // Threat is separating
170 if(timeToCPA < 0) {
171 continue;
172 }
173
212 float distance = relativePos.length; 174 float distance = relativePos.length;
213 float minSeparation = distance - relativeSpeed * shortestTime;
214 175
215 float eRad; 176 // Clamp look ahead time
216 for (bzShape s = target.shapeList; s; s = s.next) { 177 timeToCPA = bzClamp(timeToCPA, 0, maxLookAhead);
217 if(s.sweepRadius > eRad) { 178
218 eRad = s.sweepRadius; 179 // Calculate closest point of approach
219 } 180 bzVec2 cpa = m_position + m_velocity * timeToCPA;
220 } 181 bzVec2 eCpa = target.position + target.linearVelocity * timeToCPA;
182 relativePos = (eCpa - cpa);
183 float dCPA = relativePos.length;
221 184
222 if (minSeparation > radius + eRad) continue; 185 // No collision
223 186 if (dCPA > radius + obstacle.state.radius) continue;
224 // Check if it is the shortest 187
225 if (timeToCollision > 0 && timeToCollision < shortestTime) { 188 // Check if it's the closest collision threat
226 189 if (timeToCPA < shortestTime && dCPA < threat.minSeparation) {
227 // Store the time, target and other data 190 shortestTime = timeToCPA;
228 shortestTime = timeToCollision; 191 threat.target = obstacle;
229 firstTarget = target; 192 threat.distance = distance;
230 firstMinSeparation = minSeparation; 193 threat.relativePos = relativePos;
231 firstDistance = distance; 194 threat.relativeVel = relativeVel;
232 firstRelativePos = relativePos; 195 threat.minSeparation = dCPA;
233 firstRelativeVel = relativeVel; 196 rad = obstacle.state.radius;
234 rad = eRad;
235 } 197 }
236 } 198 }
237 199
238 // 2. Calculate the steering 200 // 2. Calculate the steering
239 201
240 // If we have no target, then exit 202 // If we have no target, then exit
241 if(!firstTarget) return bzVec2.zeroVect; 203 if(!threat.target) return;
242
243 Stdout(shortestTime).newline;
244 204
245 // If we’re going to hit exactly, or if we’re already 205 // If we’re going to hit exactly, or if we’re already
246 // colliding, then do the steering based on current 206 // colliding, then do the steering based on current
247 // position. 207 // position.
248 if(firstMinSeparation <= 0 || firstDistance < radius + rad) { 208 //if(threat.minSeparation < m_radius || threat.distance < radius + rad) {
249 relativePos = firstTarget.position - m_position; 209 //threat.steering = m_position - threat.target.state.position;
250 } else { 210 //} else {
251 // Otherwise calculate the future relative position: 211 // Otherwise calculate the future relative position:
252 relativePos = firstRelativePos + 212 threat.steering = threat.relativePos;
253 firstRelativeVel * shortestTime; 213 //}
254 } 214 }
255
256 // Avoid the target
257 bzVec2 steering = relativePos;
258 steering.normalize();
259 //steering.linear = relativePos * maxAcceleration
260
261 return steering;
262 }
263
264 bzVec2 avoid(float maxLookAhead, bzBody obstacles) {
265
266 float avoidMargin = 1.0f;
267 float maxLookahead = maxLookAhead * m_speed;
268
269 // Make sure we're moving
270 if (m_velocity.length > 0)
271 {
272 for (bzBody o = obstacles; o; o = o.next) {
273
274 if(o is m_body) continue;
275
276 // Find the distance from the line we're moving along to the obstacle.
277 bzVec2 movementNormal = m_velocity;
278 movementNormal.normalize();
279 bzVec2 characterToObstacle = o.position - m_position;
280
281 real distanceSquared = bzDot(characterToObstacle, movementNormal);
282 distanceSquared = characterToObstacle.lengthSquared - distanceSquared*distanceSquared;
283
284 // Check for collision
285 float oRad = 0;
286 for (bzShape s = o.shapeList; s; s = s.next) {
287 if(s.sweepRadius > oRad) {
288 oRad = s.sweepRadius;
289 }
290 }
291
292 real radius = oRad + avoidMargin;
293 if (distanceSquared < radius*radius)
294 {
295 // Find how far along our movement vector the closest pass is
296 real distanceToClosest = bzDot(characterToObstacle, movementNormal);
297
298 // Make sure this isn't behind us and is closer than our lookahead.
299 if (distanceToClosest > 0 && distanceToClosest < maxLookahead)
300 {
301 // Find the closest point
302 bzVec2 closestPoint = o.position + movementNormal * distanceToClosest;
303
304 // Find the point of avoidance
305 bzVec2 target = o.position + (closestPoint - o.position).length * radius;
306
307 target -= m_position;
308
309 auto state = cast(State) m_body.userData;
310 state.avoid = m_body.localPoint(target);
311
312 return target;
313 }
314 }
315 }
316 }
317
318 auto state = cast(State) m_body.userData;
319 state.avoid = bzVec2.zeroVect;
320
321 return bzVec2.zeroVect;
322
323 }
324
325 void findNextIntersectionWithSphere(bzBody obs,
326 inout PathIntersection intersection) {
327
328 // This routine is based on the Paul Bourke's derivation in:
329 // Intersection of a Line and a Sphere (or circle)
330 // http://local.wasp.uwa.edu.au/~pbourke/geometry/sphereline/raysphere.c
331
332 float b, c, d, p, q, s;
333 bzVec2 lc;
334
335 // initialize pathIntersection object
336 intersection.intersect = false;
337 intersection.obstacle = obs;
338
339 // find "local center" (lc) of sphere in coordinate space
340 lc = m_body.localPoint(obs.position);
341
342 // computer line-sphere intersection parameters
343 b = 0;
344
345 // Find radius of obstacle
346 float obsRadius = 0;
347 for (bzShape shape = obs.shapeList; shape; shape = shape.next) {
348 if(shape.sweepRadius > obsRadius) {
349 obsRadius = shape.sweepRadius;
350 }
351 }
352
353 c = square(lc.x) + square(lc.y) - square(obsRadius + m_radius);
354 d = (b * b) - (4 * c);
355
356 // when the path does not intersect the sphere
357 if (d < 0) return;
358
359 // otherwise, the path intersects the sphere in two points with
360 // parametric coordinates of "p" and "q".
361 // (If "d" is zero the two points are coincident, the path is tangent)
362 s = sqrt(d);
363 p = (-b + s) * 0.5f;
364 q = (-b - s) * 0.5f;
365
366 // both intersections are behind us, so no potential collisions
367 if ((p < 0) && (q < 0)) return;
368
369 // at least one intersection is in front of us
370 intersection.intersect = true;
371 intersection.distance =
372 ((p > 0) && (q > 0)) ?
373 // both intersections are in front of us, find nearest one
374 ((p < q) ? p : q) :
375 // otherwise only one intersections is in front, select it
376 ((p > 0) ? p : q);
377 }
378 215
379 /* 216 /*
380 // ------------------------------------------------------------------------ 217 // ------------------------------------------------------------------------
381 // Unaligned collision avoidance behavior: avoid colliding with other 218 // Unaligned collision avoidance behavior: avoid colliding with other
382 // nearby vehicles moving in unconstrained directions. Determine which 219 // nearby vehicles moving in unconstrained directions. Determine which
520 bzVec2 otherFinal = other.position + otherTravel; 357 bzVec2 otherFinal = other.position + otherTravel;
521 358
522 return (myFinal - otherFinal).length; 359 return (myFinal - otherFinal).length;
523 } 360 }
524 361
525 // ------------------------------------------------------------------------ 362 bzVec2 targetEnemy (State quarry, float maxPredictionTime) {
526 // pursuit of another vehicle ( version with ceiling on prediction time)
527
528 bzVec2 steerForPursuit (State quarry) {
529 return steerForPursuit (quarry, float.max);
530 }
531
532 bzVec2 steerForPursuit (State quarry, float maxPredictionTime) {
533 363
534 // offset from this to quarry, that distance, unit vector toward quarry 364 // offset from this to quarry, that distance, unit vector toward quarry
535 bzVec2 offset = quarry.position - m_position; 365 bzVec2 offset = quarry.position - m_position;
536 float distance = offset.length; 366 float distance = offset.length;
537 bzVec2 unitOffset = offset / distance; 367 bzVec2 unitOffset = offset / distance;