/// <summary> /// Look to see if we are blocked from where we are trying to go. If /// we are, we'll expand out our circle radius until we are no longer /// blocked, and then slowly shrink it back again. /// </summary> /// <param name="gameActor"></param> private void CheckEscape(Reflex reflex, GameActor gameActor, Vector3 targetPos, GameThing target, Vector3 finalDir) { Vector3 from = gameActor.WorldCollisionCenter; bool escaping = false; float escapeSpeed = 10.0f; GameActor.BlockedInfo blockInfo = new GameActor.BlockedInfo(); if (gameActor.Blocked( from + finalDir * gameActor.Chassis.SafeStoppingDistance(), ref blockInfo)) { gameActor.AddLOSLine(from, blockInfo.Contact, new Vector4(1.0f, 0, 0, 1.0f)); if (!reflex.ModifierParams.HasTurn) { CCW = !CCW; } else { float kMinEscape = -0.25f; targetPos.Z = from.Z; if ((escapeRadius > kMinEscape) && !gameActor.Blocked(target, targetPos, ref blockInfo)) { escapeRadius -= escapeSpeed * Time.GameTimeFrameSeconds; escaping = true; } else { escapeRadius += escapeSpeed * Time.GameTimeFrameSeconds; escaping = true; } } } if (!escaping) { if (escapeRadius > 0) { escapeRadius = Math.Max(0.0f, escapeRadius - escapeSpeed * 0.5f * Time.GameTimeFrameSeconds); } else { escapeRadius = Math.Min(0.0f, escapeRadius + escapeSpeed * 0.5f * Time.GameTimeFrameSeconds); } } float kMaxEscape = 25.0f; if (escapeRadius > kMaxEscape) { escapeRadius = kMaxEscape; } }
private Vector3 RandomTargetLocation(GameActor gameActor) { Debug.Assert(gameActor != null); Vector3 location3d; // Find a random spot for the wander target. Take into account whether or not we've // stalled and the movement domain of this actor. Vector3 collisionNormal = Vector3.Zero; int count = 13; // Prevent this from looping forever. On the off chance that we get n in a row // bad locations we shouldn't worry since the timeout will prevent the actor // from getting stuck. bool validLocation = false; do { float newRotation; // If we timed out or if we're taking too many attempts to get a valid target, assume we're bouncing // against a barrier and use a wider angle to pick the next target location. float angleWidthModifier = 1.0f; if (count < 8 || Time.GameTimeTotalSeconds > this.wanderTimeout) { angleWidthModifier = 2.0f; } newRotation = angleWidthModifier * (float)BokuGame.bokuGame.rnd.NextDouble() * wanderArcRange + wanderArcMin; float rangeMin = wanderRangeMin; float rangeDelta = wanderRangeRange; float wanderDistance = (float)BokuGame.bokuGame.rnd.NextDouble() * rangeDelta + rangeMin; if (gameActor.Chassis.HasFacingDirection) { newRotation += gameActor.Movement.RotationZ; } else { Vector3 dir = gameActor.Movement.Velocity; dir.Z = 0.0f; dir.Normalize(); if (!float.IsNaN(dir.X)) { float rotation = (float)Math.Acos(dir.X); if (dir.Y < 0.0f) { rotation = MathHelper.TwoPi - rotation; } newRotation += rotation; } } // cap it within 0-pi*2; newRotation = (newRotation + MathHelper.TwoPi) % (MathHelper.TwoPi); Vector3 newHeading = new Vector3((float)Math.Cos(newRotation), (float)Math.Sin(newRotation), 0.0f); location3d = newHeading * wanderDistance + gameActor.Movement.Position; GameActor.BlockedInfo blockInfo = new GameActor.BlockedInfo(); bool blocked = gameActor.Blocked(location3d, ref blockInfo); if (blocked) { gameActor.AddLOSLine(gameActor.Movement.Position, blockInfo.Contact, new Vector4(1.0f, 0, 0, 1.0f)); float avert = gameActor.Chassis.SafeAversionDistance(); float closest = gameActor.BoundingSphere.Radius + avert; if (Vector3.DistanceSquared(gameActor.WorldCollisionCenter, blockInfo.Contact) > closest * closest) { // We have room to move in that direction Vector3 dir = blockInfo.Contact - gameActor.WorldCollisionCenter; float length = dir.Length(); Debug.Assert(length > 0); dir /= length; length -= closest; Debug.Assert(length > 0); dir *= length; location3d = gameActor.Movement.Position + dir; blocked = false; } } if (!blocked) { // Actors can have different domains within which they move. Try and ensure that // the point picked for wandering is valid for this actor's domain. switch (gameActor.Domain) { case GameActor.MovementDomain.Air: validLocation = CheckAirBounds(location3d); break; case GameActor.MovementDomain.Land: { // Test for ground with no water. float terrainAltitude = Terrain.GetTerrainAndPathHeight(location3d); if (terrainAltitude > 0.0f && Terrain.GetWaterBase(location3d) == 0.0f) { validLocation = true; location3d.Z = terrainAltitude; } } break; case GameActor.MovementDomain.Water: { // Test for water and set location somewhere between surface and ground. float waterAltitude = Terrain.GetWaterBase(location3d); if (Terrain.GetWaterBase(location3d) > 0.0f) { validLocation = true; float terrainAltitude = Terrain.GetTerrainAndPathHeight(location3d); location3d.Z = terrainAltitude + (waterAltitude - terrainAltitude) * (float)BokuGame.bokuGame.rnd.NextDouble(); } } break; } } --count; } while (count > 0 && !validLocation); if (!validLocation) { location3d.Z = Terrain.GetTerrainAndPathHeight(location3d); } wanderDir = location3d - gameActor.Movement.Position; wanderDir.Z = 0; if (wanderDir.LengthSquared() > 0) { wanderDir.Normalize(); } wanderDist = Vector3.Dot(location3d, wanderDir); return(location3d); }