Mercurial > projects > openmelee
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; |