public override ActionSet ComposeActionSet(Reflex reflex, GameActor gameActor) { ClearActionSet(actionSet); UpdateCanBlend(reflex); if (!reflex.targetSet.AnyAction) { return(actionSet); } SensorTarget target = reflex.targetSet.Nearest; if (target != null) { // Calculate a vector toward target. Vector3 value = target.Direction; bool apply = reflex.ModifyHeading(gameActor, Modifier.ReferenceFrames.World, ref value); if (apply) { actionSet.AddAction(Action.AllocTargetLocationAction(reflex, target.Position, autoTurn: true)); } } return(actionSet); }
public override ActionSet ComposeActionSet(Reflex reflex, GameActor gameActor) { ClearActionSet(actionSet); UpdateCanBlend(reflex); if (!reflex.targetSet.AnyAction) { return(actionSet); } if (reflex.targetSet.Count > 0) { if (reflex.targetSet.CullToFurthest(gameActor, BlockedFrom)) { // the targetSet is in order by distance SensorTarget target = reflex.targetSet.Furthest; // calculate a vector toward target Vector3 value = target.Direction; bool apply = reflex.ModifyHeading(gameActor, Modifier.ReferenceFrames.World, ref value); if (apply) { // radius should be from object actionSet.AddAttractor(AllocAttractor(target.Range, value, target.GameThing, reflex), 0.4f); } } } return(actionSet); }
public override ActionSet ComposeActionSet(Reflex reflex, GameActor gameActor) { ClearActionSet(actionSet); UpdateCanBlend(reflex); if (!reflex.targetSet.AnyAction) { return(actionSet); } if (reflex.targetSet.Count > 0) { // falloff == Avoid, !falloff == Away if (falloff) { // Avoid. // We want to avoid all actors so we need to loop over the full set. foreach (SensorTarget target in reflex.targetSet) { GameActor targetActor = target.GameThing as GameActor; if (targetActor != null) { actionSet.AddAction(Action.AllocAvoidAction(reflex, targetActor)); } } } else { // Away. Vector3 actorPos = gameActor.Movement.Position; Vector3 dir = Vector3.Zero; // The direction we want to flee. // We want to move away from all actors so we need to loop over the full set. foreach (SensorTarget target in reflex.targetSet) { GameActor targetActor = target.GameThing as GameActor; if (targetActor != null) { Vector3 fromTarget = actorPos - targetActor.Movement.Position; float dist = fromTarget.Length(); // Normalize fromTarget then divide again by dist. This gives us a linear // falloff of strength based on distance so that a target that is twice as // close will have twice the weight on the flee direction. dir += fromTarget / dist / dist; } } bool apply = reflex.ModifyHeading(gameActor, Modifier.ReferenceFrames.World, ref dir); if (apply) { dir.Normalize(); actionSet.AddAction(Action.AllocVelocityAction(reflex, dir, autoTurn: true)); } } // end else if falloff } // end if targetSet not empty. return(actionSet); }
public override ActionSet ComposeActionSet(Reflex reflex, GameActor gameActor) { ClearActionSet(movementSet); UpdateCanBlend(reflex); if (!reflex.targetSet.Action) { return(movementSet); } // calculate a vector relative to the camera and input stick // see "Game Programming Gems 2", "Classic Super Mario 64 Third-Person Control and Animation" // Vector2 valueStick = Vector2.Zero; if (reflex.targetSet.Param != null && reflex.targetSet.Param is Vector2) { valueStick = (Vector2)reflex.targetSet.Param; } Vector3 cameraDir = InGame.inGame.Camera.ViewDir; cameraDir.Z = 0.0f; if (cameraDir != Vector3.Zero) { cameraDir.Normalize(); } Vector3 cameraRight = new Vector3(cameraDir.Y, -cameraDir.X, 0.0f); Vector3 value = new Vector3((valueStick.X * cameraRight.X) + (valueStick.Y * cameraDir.X), (valueStick.X * cameraRight.Y) + (valueStick.Y * cameraDir.Y), 0.0f); value *= strength; bool apply = reflex.ModifyHeading(gameActor, Modifier.ReferenceFrames.World, ref value); if (apply) { if (reflex.Actuator != null && (reflex.Actuator.category & Actuator.Category.Action) == Actuator.Category.Action) { // support it as action even it it supports direction also movementSet.AddActionTarget(AllocEffector(0.0f, value, null, reflex), 0.0f); } else { // radius should be from object movementSet.AddAttractor(AllocEffector(value.Length() * 3.0f, value, null, reflex), 0.0f); } } return(movementSet); }
public override ActionSet ComposeActionSet(Reflex reflex, GameActor gameActor) { ClearActionSet(actionSet); UpdateCanBlend(reflex); if (!reflex.targetSet.AnyAction) { return(actionSet); } float angle = (float)Math.Acos(gameActor.Movement.Facing.X); if (gameActor.Movement.Facing.Y < 0.0f) { angle = MathHelper.TwoPi - angle; } angle += MathHelper.PiOver4; // rotate 45 angle = (angle + (float)Math.PI * 2.0f) % ((float)Math.PI * 2.0f); // calculate a vector toward target Vector3 value = new Vector3(); value.X = (float)Math.Cos(angle); value.Y = (float)Math.Sin(angle); value.Z = 0.0f; value *= 0.001f; // remove any forward velocity bool apply = reflex.ModifyHeading(gameActor, Modifier.ReferenceFrames.World, ref value); if (apply) { // radius should be from object actionSet.AddAttractor(Action.AllocAttractor(0.0f, value, null, reflex), 0.0f); } // unused // this.used = false; // will get reset if truely used return(actionSet); }
public override ActionSet ComposeActionSet(Reflex reflex, GameActor gameActor) { ClearActionSet(actionSet); UpdateCanBlend(reflex); if (!reflex.targetSet.AnyAction) { return(actionSet); } Vector2 valueStick = defaultStickVector; if (reflex.targetSet.Param != null && reflex.targetSet.Param is Vector2) { // Read the gamepad input valueStick = (Vector2)reflex.targetSet.Param; } Vector3 actorDir = gameActor.Movement.Heading; actorDir.Z = 0; // The direction we want to be moving. Vector3 targetDirection = Vector3.Zero; bool isForwardSel = this.Categories.Get((int)BrainCategories.ForwardSelector); Vector3 cameraRight = new Vector3(actorDir.Y, -actorDir.X, 0.0f); targetDirection = new Vector3((valueStick.X * cameraRight.X) + (valueStick.Y * actorDir.X), (valueStick.X * cameraRight.Y) + (valueStick.Y * actorDir.Y), 0.0f); // We used to normalize here but that makes movement binary instead of analog, ie // it acts as if the stick is fully over all the time instead of allowing for // finer control. /* * value.Normalize(); * // If value was 0,0,0 we'll get NaNs when we normalize so check for this * // and restore valid values for value. (sorry, couldn't resist) * if (float.IsNaN(value.X)) * { * value = Vector3.Zero; * } */ targetDirection *= strength; bool absoluteDirection = false; // See if the modifiers want to give us an absolute direction. Vector3 modifiedDirection = Vector3.Zero; bool apply = reflex.ModifyHeading(gameActor, Modifier.ReferenceFrames.World, ref modifiedDirection); if (apply && modifiedDirection != Vector3.Zero) { // Yes, we have an absolute direction. We still need to apply the non-frame modifiers (speed, color, etc) float stickPower = targetDirection.Length(); targetDirection = modifiedDirection; if (stickPower != 0) { targetDirection *= stickPower; } apply = reflex.ModifyHeading(gameActor, Modifier.ReferenceFrames.Local, ref targetDirection); absoluteDirection = true; } else { // No absolute direction given, we'll use the input given to us by the brain. apply = reflex.ModifyHeading(gameActor, Modifier.ReferenceFrames.World, ref targetDirection); } if (apply) { // TODO (****) Move all the Rover specific stuff to the Rover class. None of this should be here. // SGI_MOD also apply the speed modifier for hills for the Rover Boku.SimWorld.Chassis.RoverChassis RovChassis = gameActor.Chassis as Boku.SimWorld.Chassis.RoverChassis; if (RovChassis != null) { RovChassis.ModifyHeading(ref targetDirection); } if (!reflex.IsUserControlled) { if (absoluteDirection) { actionSet.AddAction(Action.AllocVelocityAction(reflex, targetDirection, autoTurn: true)); } else { // Move Forward // TODO (****) Is this controllable by stick? If so, 1.0 should be replaced by stick value. actionSet.AddAction(Action.AllocSpeedAction(reflex, 1.0f)); } } else { // If this actor is user controlled, we need to rotate the heading to make it camera relative. // Unless the direction given is an absolute one, like "North". // Note, we need to test for Zero here otherwise we add an action even if the user isn't giving // any inputs. if (targetDirection != Vector3.Zero) { if (!absoluteDirection && !isForwardSel && reflex.Task.IsUserControlled) { Matrix mat = Matrix.CreateRotationZ(InGame.inGame.Camera.Rotation - gameActor.Movement.RotationZ); targetDirection = Vector3.TransformNormal(targetDirection, mat); } actionSet.AddAction(Action.AllocVelocityAction(reflex, targetDirection, autoTurn: true)); } } } return(actionSet); }
public override ActionSet ComposeActionSet(Reflex reflex, GameActor gameActor) { ClearActionSet(actionSet); //UpdateCanBlend(reflex); if (!reflex.targetSet.AnyAction) { return(actionSet); } TurnModifier turnMod = reflex.GetModifierByType(typeof(TurnModifier)) as TurnModifier; GamePadStickFilter stickFilt = reflex.Data.GetFilterByType(typeof(GamePadStickFilter)) as GamePadStickFilter; GamePadTriggerFilter triggerFilt = reflex.Data.GetFilterByType(typeof(GamePadTriggerFilter)) as GamePadTriggerFilter; MouseFilter mouseFilt = reflex.Data.GetFilterByType(typeof(MouseFilter)) as MouseFilter; TouchGestureFilter touchFilt = reflex.Data.GetFilterByType(typeof(TouchGestureFilter)) as TouchGestureFilter; KeyBoardKeyFilter keyBoardKeyFilt = reflex.Data.GetFilterByType(typeof(KeyBoardKeyFilter)) as KeyBoardKeyFilter; // Will be modified depending on stick or trigger position. float speedModifier = 1.0f; // // Start by looking at the input filters to attenuate speed. // if (stickFilt != null) { // Note that we take the sign into account further below. // For now, just get the magnitude. speedModifier *= (float)Math.Abs(stickFilt.stickPosition.X); } else if (triggerFilt != null) { speedModifier *= triggerFilt.triggerValue; } else if (mouseFilt != null) { // Note that we take the sign into account further below. // For now, just get the magnitude. speedModifier *= Math.Abs(mouseFilt.ScreenPosition.X); } else if (touchFilt != null) { if (touchFilt.type == TouchGestureFilterType.Rotate) { reflex.Task.GameActor.Chassis.WasTouchRotated = true; } } // Decide if we're turning toward a particular direction or // setting a turn rate. Note, this will miss some cases // that we clean up further down. The failure is rooted // in TurnModifier if you want to go fix it. :-) Vector3 desiredHeading = Vector3.Zero; float targetHeading = 0; // See if the modifiers want to give us an absolute direction. reflex.ModifyHeading(gameActor, Modifier.ReferenceFrames.World, ref desiredHeading); targetHeading = MyMath.ZRotationFromDirection(desiredHeading); // If we have a desiredHeading, use that, else assume we're just turning. if (desiredHeading != Vector3.Zero) { // We were given an absolute direction, use it. actionSet.AddActionTarget(Action.AllocHeadingAction(reflex, targetHeading, speedModifier)); } else { // Determine which relative direction we should use. TurnModifier.TurnDirections direction = TurnModifier.TurnDirections.None; if (turnMod != null) { // If a turn modifier was specified, use its direction. direction = turnMod.direction; } else if (stickFilt != null) { // No turn modifier but we have a gamepad stick, use it to determine turn direction. direction = stickFilt.stickPosition.X > 0 ? TurnModifier.TurnDirections.Right : TurnModifier.TurnDirections.Left; } else if (triggerFilt != null) { // No turn modifier but we have a gamepad trigger, use it to determine turn direction. // Basically this automatically maps left trigger to left turn and right trigger to right turn. direction = triggerFilt.trigger == GamePadTriggerFilter.GamePadTrigger.LeftTrigger ? TurnModifier.TurnDirections.Left : TurnModifier.TurnDirections.Right; } else if (mouseFilt != null) { // No turn modifier but we do have mouse input. direction = mouseFilt.ScreenPosition.X > 0 ? TurnModifier.TurnDirections.Right : TurnModifier.TurnDirections.Left; // This scaling by 2 has no real justification except // that it matches the magnitude of the original code // so this is more compatible with older games. speedModifier *= 2.0f; } else if (touchFilt != null) { // No turn modifier but we do have touch input. if (touchFilt.type == TouchGestureFilterType.Rotate) { if (touchFilt.DeltaRotation < 0) { direction = TurnModifier.TurnDirections.Left; } else if (touchFilt.DeltaRotation > 0) { direction = TurnModifier.TurnDirections.Right; } } else if (touchFilt.type == TouchGestureFilterType.Tap) { direction = TurnModifier.TurnDirections.Toward; } } else if (keyBoardKeyFilt != null) { if (reflex.targetSet != null) { Vector2 dir = (Vector2)(reflex.targetSet.Param); if (dir.X == 1) { direction = TurnModifier.TurnDirections.Right; } else if (dir.X == -1) { direction = TurnModifier.TurnDirections.Left; } } } else { // Nothing exists that would help us decide which way we should turn, choose left. // This could be the user programming WHEN DO Turn direction = TurnModifier.TurnDirections.Left; } // Calculate the new direction we want to face. switch (direction) { // Turn to our left. case TurnModifier.TurnDirections.Left: { float angle = 0.0f; if (touchFilt != null && touchFilt.type == TouchGestureFilterType.Rotate) { angle = gameActor.Movement.RotationZ + Math.Abs(touchFilt.DeltaRotation); } else { angle = gameActor.Movement.RotationZ + MathHelper.PiOver2; } desiredHeading = new Vector3((float)Math.Cos(angle), (float)Math.Sin(angle), 0); actionSet.AddActionTarget(Action.AllocTurnSpeedAction(reflex, speedModifier), 0); } break; // Turn to our right. case TurnModifier.TurnDirections.Right: { float angle = 0.0f; if (touchFilt != null && touchFilt.type == TouchGestureFilterType.Rotate) { angle = gameActor.Movement.RotationZ - Math.Abs(touchFilt.DeltaRotation); } else { angle = gameActor.Movement.RotationZ - MathHelper.PiOver2; } desiredHeading = new Vector3((float)Math.Cos(angle), (float)Math.Sin(angle), 0); actionSet.AddActionTarget(Action.AllocTurnSpeedAction(reflex, -speedModifier), 0); } break; // Turn to face the direction we are moving. case TurnModifier.TurnDirections.Forward: { float angle = MyMath.ZRotationFromDirection(gameActor.Movement.Velocity); desiredHeading = new Vector3((float)Math.Cos(angle), (float)Math.Sin(angle), 0); actionSet.AddActionTarget(Action.AllocHeadingAction(reflex, targetHeading, speedModifier)); } break; // Turn to face the sensed object. case TurnModifier.TurnDirections.Toward: // We're probably here because of WHEN Mouse Left DO Turn Toward. // This is another case where we want a Heading Action. if (reflex.targetSet.Nearest != null) { speedModifier = 1; // If we're doing mouse-look (WHEN Mouse Over DO Turn Toward) try // and create a bit of a dead-zone in the middle. bool mouseOver = false; foreach (Filter f in reflex.Filters) { MouseFilter mf = f as MouseFilter; if (mf != null && mf.type == MouseFilterType.Hover) { mouseOver = true; break; } } desiredHeading = reflex.targetSet.Nearest.Direction; targetHeading = MyMath.ZRotationFromDirection(desiredHeading); // If mouseOver, calc damping to slow down turning when already looking // in the direction we want. This helps make it less twitchy. float dampingFactor = 1.0f; if (mouseOver) { float dot = Vector3.Dot(desiredHeading, gameActor.Movement.Heading); float deadzoneAngle = 0.96f; float dampzoneAngle = 0.9f; // If very close to center, just treat as zero. if (dot > deadzoneAngle) { dampingFactor = 0; } else if (dot > dampzoneAngle) { // Not in center, but close so damp turning. // The closer we are to already looking in the correct direction, increase damping. dampingFactor = MyMath.RemapRange(dot, deadzoneAngle, dampzoneAngle, 0, 1); // Smooth the result to flatten out the center. dampingFactor = MyMath.SmoothStep(0, 1, dampingFactor); } } actionSet.AddActionTarget(Action.AllocHeadingAction(reflex, targetHeading, dampingFactor * speedModifier)); } break; } } return(actionSet); } // end of ComposeActionSet()
public override ActionSet ComposeActionSet(Reflex reflex, GameActor gameActor) { ClearActionSet(actionSet); UpdateCanBlend(reflex); if (!reflex.targetSet.AnyAction) { return(actionSet); } Vector3 direction = Vector3.Zero; // This is either the direction to the nearest target or, if no target, the direction we want (forward, etc). GameThing gameThing = null; float range = 0.0f; // TODO (****) This was causing a null ref in the "Green Ghost V Looper" level // when the user pressed the mouse button to launch a wisp. In that case the // targetSet has 1 element in it but it's not "valid" so it never goes into // the nearestTargets list, hence targetSet.Nearest is null. // Note I also pulled the Finalize call out of the if statement since Finialize(sic) // is what creates the nearestTargets list. // Still need to investigate the underlying cause. // Turns out to be an issue with Ghost. Ghosted objects are "ignored" so not valid // for the Nearest list. Should probably rethink how invisible and ghosted work. if (reflex.targetSet.Nearest != null && !(reflex.targetSet.Nearest.GameThing is NullActor)) { // We have a "real" target. // The targetSet is in order by distance. // Pick the nearest member of the target set. SensorTarget target = reflex.targetSet.Nearest; direction = target.Direction; gameThing = target.GameThing; range = target.Range; } else { // No explicit target. // This handles the case of WHEN GamePad AButton DO Shoot // where instead of having a target we just have a direction. if (reflex.targetSet.Param != null && reflex.targetSet.Param is Vector2) { direction = new Vector3((Vector2)reflex.targetSet.Param, 0.0f); } else { direction = Vector3.Zero; } gameThing = null; range = 1.0f; } bool apply = reflex.ModifyHeading(gameActor, Modifier.ReferenceFrames.World, ref direction); if (apply) { // TODO (****) Probably need a TargetObject Action. // radius should be from object actionSet.AddActionTarget(Action.AllocAttractor(range, direction, gameThing, reflex), 0.4f); } return(actionSet); }
public override ActionSet ComposeActionSet(Reflex reflex, GameActor gameActor) { ClearActionSet(actionSet); UpdateCanBlend(reflex); if (!reflex.targetSet.AnyAction) { return(actionSet); } Vector2 valueStick = Vector2.Zero; if (reflex.targetSet.Param != null && reflex.targetSet.Param is Vector2) { // Read the gamepad input valueStick = (Vector2)reflex.targetSet.Param; // Flip gamepad axes for buttons and triggers, since they report in the y-axis. foreach (Filter filter in reflex.Filters) { if (filter is GamePadButtonFilter || filter is GamePadTriggerFilter) { valueStick = new Vector2(valueStick.Y, -valueStick.X); } } valueStick.Y = 0; } else { valueStick = new Vector2(0.75f, 0); } // Pass stick value to modifiers (quickly, slowly, left, right, etc) Vector3 stick3d = new Vector3(valueStick.X, 0, 0); bool apply = reflex.ModifyHeading(gameActor, Modifier.ReferenceFrames.Local, ref stick3d); if (apply) { float strength = stick3d.Length(); Vector3 velocity = new Vector3(gameActor.Movement.Velocity.X, gameActor.Movement.Velocity.Y, 0); float angle = 0; float maxAngle = MathHelper.Pi / 2.01f; // Special handling of the "toward" turn modifier, since it doesn't have enough information to know // which way "toward" actually is for an actor. if (reflex.targetSet.Nearest != null && reflex.HasModifier("modifier.turntoward")) { SensorTarget target = reflex.targetSet.Nearest; Vector3 toTarget = target.Direction; toTarget.Z = 0; toTarget.Normalize(); angle = MyMath.ZRotationFromDirection(toTarget); Vector3 unit = new Vector3((float)Math.Cos(angle), (float)Math.Sin(angle), 0); // radius should be from object actionSet.AddAttractor(Action.AllocAttractor(target.Range, unit * 0.001f, target.GameThing, reflex, specialInstruction: BaseAction.SpecialInstruction.MatchVectorScale | BaseAction.SpecialInstruction.EnforceMinScale), 0.4f); } else { // Dampen sensitivity for small stick movements. strength *= strength; if (strength > 0.001f) { // Scale the strength so that the speed modifiers have a visible effect. strength *= 0.3f; // See if the modifiers want to give us an absolute direction in which to turn. Vector3 modifiedDirection = Vector3.Zero; reflex.ModifyHeading(gameActor, Modifier.ReferenceFrames.World, ref modifiedDirection); if (modifiedDirection != Vector3.Zero) { // Yes, we have an absolute turn direction. angle = MyMath.ZRotationFromDirection(modifiedDirection); // Check to see whether we're already facing this direction. if (Math.Abs(angle - gameActor.Movement.RotationZ) < 0.001f) { apply = false; } // Put the angle into the local reference frame so that the code below can act on it. // The actor's rotation will be added back in later. angle -= gameActor.Movement.RotationZ; } else { // For relative turning angle, start with the max angle possible in the desired direction. It will be // reduced according to stick input strength in the code below. angle = maxAngle * -Math.Sign(stick3d.X); } if (apply) { // If the actor is moving very slow or standing still, then build a turn vector who's angle // is derived from the strength of the stick input. angle = angle * strength; // Put the angle into the world reference frame. angle += gameActor.Movement.RotationZ; // cap it within 0-pi*2; angle = (angle + MathHelper.TwoPi) % (MathHelper.TwoPi); Vector3 unit = new Vector3((float)Math.Cos(angle), (float)Math.Sin(angle), 0); // radius should be from object; none in this case actionSet.AddAttractor(Action.AllocAttractor(1.0f, unit * 0.001f, null, reflex, specialInstruction: BaseAction.SpecialInstruction.MatchVectorScale | BaseAction.SpecialInstruction.EnforceMinScale), 0.0f); } } } } // apply return(actionSet); }
public override ActionSet ComposeActionSet(Reflex reflex, GameActor gameActor) { ClearActionSet(actionSet); UpdateCanBlend(reflex); if (!reflex.targetSet.AnyAction) { return(actionSet); } // The targetSet is in order by distance use the closest one. // Should be null if 0 count in target set. SensorTarget target = reflex.targetSet.Nearest; if (target != null && !(target.GameThing is NullActor)) { // calculate a vector toward target Vector3 toTarget = target.Position - gameActor.Movement.Position; float targetZ = target.GameThing.WorldCollisionCenter.Z; float deltaZ = targetZ - gameActor.WorldCollisionCenter.Z; toTarget.Z = 0; float dist = toTarget.Length(); float targetRadius = 0; if (target.GameThing != null && !(target.GameThing is NullActor)) { targetRadius = target.GameThing.BoundingSphere.Radius; } float radius = targetRadius + gameActor.BoundingSphere.Radius; radius = Math.Max(radius, 1.0f); radius *= 8.0f * radiusScale; radius *= (1.0f + escapeRadius); Vector3 toTargetUnit = Vector3.Normalize(toTarget); // Calculate the desired angle relative to the vector toward target. float localTheta; if (dist > radius) { localTheta = (float)Math.Cos(radius / dist); } else { localTheta = -(float)Math.Cos(dist / radius * Math.PI * 0.5); } // Get the vector from the local desired angle. Vector3 localDir = new Vector3( (float)Math.Cos(localTheta), (float)Math.Sin(localTheta), 0); // Scale the local vector by the strength setting from CardSpace. localDir *= strength; // Modify the local vector (left, right, quickly, slowly, etc). bool apply = reflex.ModifyHeading(gameActor, Modifier.ReferenceFrames.Local, ref localDir); if (apply) { // Because CCW is considered "left". if (CCW) { localDir.X *= -1; } // Get the modified desired angle. localTheta = MyMath.ZRotationFromDirection(localDir); // Combine with the rotation of vector toward target relative to the actor. float thetaToTarget = MyMath.ZRotationFromDirection(toTargetUnit); float finalTheta = thetaToTarget + localTheta - MathHelper.PiOver2; // cap it within 0-pi*2; finalTheta = (finalTheta + MathHelper.TwoPi) % (MathHelper.TwoPi); if (controlZ && (gameActor.Chassis is FloatInAirChassis || gameActor.Chassis is SwimChassis || gameActor.Chassis is SaucerChassis)) { gameActor.Chassis.TargetAltitude = targetZ; } else { deltaZ = 0; } // Build a heading vector in world space. Vector3 finalDir = new Vector3( (float)Math.Cos(finalTheta), (float)Math.Sin(finalTheta), deltaZ); finalDir.Normalize(); CheckEscape(reflex, gameActor, target.Position, target.GameThing, finalDir); finalDir *= localDir.Length(); actionSet.AddAction(Action.AllocVelocityAction(reflex, finalDir, autoTurn: true)); } } return(actionSet); }