protected static bool BlockedFrom(GameActor actor, Vector3 toward, float closest) { Vector3 from = actor.WorldCollisionCenter; Vector3 ray = toward - from; float len = ray.Length(); if (len > closest) { ray /= len; ray *= closest; } toward = from + ray; SimWorld.Terra.Terrain.HitBlock hitBlock = new Boku.SimWorld.Terra.Terrain.HitBlock(); Vector2 minMaxZ = new Vector2(0.0f, Single.MaxValue); float height = actor.Movement.Altitude - SimWorld.Terra.Terrain.GetTerrainAndPathHeight(actor.Movement.Position); Vector4 maxStep = new Vector4( height + actor.Chassis.WaistOffset, // max single step up Single.MinValue, // max step down -1.0f, // water depth at which transition land to water occurs (-1 to ignore) -1.0f); // water depth at which transition water to land occurs (-1 to ignore) switch (actor.Domain) { case GameActor.MovementDomain.Air: minMaxZ.X = -1.0f; break; case GameActor.MovementDomain.Land: maxStep.Z = 1.0f; break; case GameActor.MovementDomain.Water: maxStep.X = Single.MaxValue; maxStep.W = 1.0f; break; } if (SimWorld.Terra.Terrain.Blocked(from, toward, minMaxZ, maxStep, ref hitBlock, actor.Movement.Altitude)) { if (Vector3.DistanceSquared(from, hitBlock.Position) < closest * closest) { actor.AddLOSLine(from, hitBlock.Position, new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); return(true); } actor.AddLOSLine(from, hitBlock.Position, new Vector4(1.0f, 1.0f, 0.0f, 1.0f)); } else { actor.AddLOSLine(from, toward, new Vector4(0.0f, 1.0f, 0.0f, 1.0f)); } return(false); } // end of BlockedFrom()
public override ActionSet ComposeActionSet(Reflex reflex, GameActor gameActor) { ClearActionSet(actionSet); UpdateCanBlend(reflex); if (!reflex.targetSet.AnyAction) { return(actionSet); } // Don't wander if we are immobilized. The effect is the bot randomly turning in place, which looks broken. ConstraintModifier constraintMod = reflex.GetModifierByType(typeof(ConstraintModifier)) as ConstraintModifier; if (constraintMod != null && constraintMod.ConstraintType == ConstraintModifier.Constraints.Immobile) { // We still need to add an attractor to the movement set so that the reflex will be considered acted on. // TODO (****) is thre a better way to indicate this? actionSet.AddActionTarget(Action.AllocTargetLocationAction(reflex, gameActor.Movement.Position, autoTurn: true)); return(actionSet); } // Check if this target has timed out. if (Time.GameTimeTotalSeconds > this.wanderTimeout) { this.pickNewTarget = true; } // Calculate a vector toward target in 2d. Vector2 wanderTarget2d = new Vector2(this.wanderTarget.X, this.wanderTarget.Y); Vector2 value2d = wanderTarget2d - new Vector2(gameActor.Movement.Position.X, gameActor.Movement.Position.Y); float distance = Vector3.Dot(wanderDir, gameActor.Movement.Position) - wanderDist + gameActor.BoundingSphere.Radius; if (distance > 0) { this.pickNewTarget = true; } if (this.pickNewTarget) { this.pickNewTarget = false; // we need to pick a random wander target this.wanderTarget = RandomTargetLocation(gameActor); this.wanderTimeout = Time.GameTimeTotalSeconds + maxTimeOnTarget; } actionSet.AddActionTarget(Action.AllocTargetLocationAction(reflex, wanderTarget, autoTurn: true)); /// For debugging, uncomment this line and enable Debug: Display Line of Sight /// on the character in question. gameActor.AddLOSLine(gameActor.Movement.Position, new Vector3(wanderTarget.X, wanderTarget.Y, gameActor.Movement.Position.Z), new Vector4(0.0f, 1.0f, 0.0f, 1.0f)); return(actionSet); }
/// <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); }