public override void PreCollisionTestUpdate(GameThing thing) { Movement movement = thing.Movement; DesiredMovement desiredMovement = thing.DesiredMovement; GameActor.State state = thing.CurrentState; float secs = Time.GameTimeFrameSeconds; if (state == GameActor.State.Active) { // Apply DesiredMovement values to current movement. ApplyDesiredMovement(movement, desiredMovement); float height = 0; bool bounce = CollideWithGround(movement, ref height); // Apply external force. movement.Velocity += desiredMovement.ExternalForce.GetValueOrDefault() / thing.Mass * secs; // Apply drag to velocity. ApplyFriction(movement, desiredMovement, applyVertical: true); // Apply velocity to position. movement.Position += movement.Velocity * secs; } }
} // end of SetLoopedAnimationWeights() #endregion #region Internal /// <summary> /// Applies the values set by the brain in DesiredMovement to this chassis. /// </summary> /// <param name="movement"></param> /// <param name="desiredMovement"></param> public override void ApplyDesiredMovement(Movement movement, DesiredMovement desiredMovement) { // Apply velocity changes. ApplyDesiredVelocityForHover(movement, desiredMovement); // Apply rotation changes. ApplyDesiredRotation(movement, desiredMovement); } // end of ApplyDesiredMovement()
/// <summary> /// Applies the values set by the brain in DesiredMovement to this chassis. /// </summary> /// <param name="movement"></param> /// <param name="desiredMovement"></param> public override void ApplyDesiredMovement(Movement movement, DesiredMovement desiredMovement) { // Apply velocity changes. ApplyDesiredVelocityForHover(movement, desiredMovement); // Apply vertical movement. ApplyDesiredVerticalMovement(movement, desiredMovement); } // end of ApplyDesiredMovement()
public override void PreCollisionTestUpdate(GameThing thing) { Movement movement = thing.Movement; DesiredMovement desiredMovement = thing.DesiredMovement; GameActor.State state = thing.CurrentState; float secs = Time.GameTimeFrameSeconds; float waterAltitude = Terrain.GetWaterBase(movement.Position); // Only move if active. if (state == GameActor.State.Active) { // Apply DesiredMovement values to current movement. ApplyDesiredMovement(movement, desiredMovement); float height = 0; bool bounce = CollideWithGround(movement, ref height); // Apply external force. movement.Velocity += desiredMovement.ExternalForce.GetValueOrDefault() / thing.Mass * secs; // Apply drag to velocity. ApplyFriction(movement, desiredMovement, applyVertical: true); // Apply velocity to position. movement.Position += movement.Velocity * secs; // Prevent going up steep slopes and have bots slide down slopes. if (waterAltitude > 0.0f || !thing.StayAboveWater) { Vector3 normal = Terrain.GetNormal(movement.Position); if (normal.Z < slopeThreshold) { Vector3 velocity = movement.Velocity; velocity.X += normal.X * 10.0f * secs; velocity.Y += normal.Y * 10.0f * secs; movement.Velocity = velocity; } } if (waterAltitude > 0.0f) { Vector3 collCenter = thing.WorldCollisionCenter; float collRad = thing.WorldCollisionRadius; if ((waterAltitude - Terrain.WaveHeight < collCenter.Z + collRad) && (waterAltitude > collCenter.Z - collRad)) { CheckRipples(thing, collRad); } } } // end of if state is active. } // end of PreCollisionTestUpdate()
} // end of ApplyDesiredMovement() /// <summary> /// Applies the values set by the brain in DesiredMovement to this chassis. /// Assume the bot is falling so only rotation changes apply. /// </summary> /// <param name="movement"></param> /// <param name="desiredMovement"></param> public void ApplyDesiredMovementWhenFalling(Movement movement, DesiredMovement desiredMovement) { // Apply velocity changes. Sort of. We need this to affect the // rotation of the bot but, since we're falling, the velocity shouldn't change. Vector3 velocity = movement.Velocity; ApplyDesiredVelocityForHover(movement, desiredMovement); movement.Velocity = velocity; // Restore saved value. // Apply rotation changes. ApplyDesiredRotation(movement, desiredMovement); // Note that we don't clamp velocity to facing direction here. // This means that we can spin in the air without changing our velocity. } // end of ApplyDesiredMovementWhenFalling()
/// <summary> /// Applies the values set by the brain in DesiredMovement to this chassis. /// </summary> /// <param name="movement"></param> /// <param name="desiredMovement"></param> public override void ApplyDesiredMovement(Movement movement, DesiredMovement desiredMovement) { // Apply velocity changes. ApplyDesiredVelocityForHover(movement, desiredMovement); // Apply rotation changes. ApplyDesiredRotation(movement, desiredMovement); // Clamp velocity to forward/backward. Don't mess with vertical movement. // Calc facing dir without any Z component. Vector3 forward = movement.Facing * new Vector3(1, 1, 0); forward.Normalize(); float dot = Vector3.Dot(forward, movement.Velocity); Vector3 vel = dot * forward; movement.Velocity = new Vector3(vel.X, vel.Y, movement.Velocity.Z); } // end of ApplyDesiredMovement()
} // end of SetLoopedAnimationWeights() /// <summary> /// Applies the values set by the brain in DesiredMovement to this chassis. /// </summary> /// <param name="movement"></param> /// <param name="desiredMovement"></param> public override void ApplyDesiredMovement(Movement movement, DesiredMovement desiredMovement) { // For boats we want them to be able to turn BUT they shouldn't be able // to move sideways. So, we need to calc the rotation as usual but // the velocity should only be applied based on the direction the bot // is facing. // Apply velocity changes. ApplyDesiredVelocityForHover(movement, desiredMovement); // Apply rotation changes. ApplyDesiredRotation(movement, desiredMovement); // Clamp velocity to forward/backward. Don't mess with vertical movement. // Calc facing dir without any Z component. Vector3 forward = movement.Facing * new Vector3(1, 1, 0); forward.Normalize(); float dot = Vector3.Dot(forward, movement.Velocity); Vector3 vel = dot * forward; movement.Velocity = new Vector3(vel.X, vel.Y, movement.Velocity.Z); } // end of ApplyDesiredMovement()
public override MovementOutput GetMovement() { DesiredOutput = DesiredMovement.GetMovement(); DesiredVelocity = this.Character.velocity + DesiredOutput.linear; if (DesiredVelocity.magnitude > MaxSpeed) { DesiredVelocity.Normalize(); DesiredVelocity *= MaxSpeed; } samples.Add(DesiredVelocity); for (int i = 0; i < numSamples; i++) { angle = Random.Range(0, MathConstants.MATH_2PI); magnitude = Random.Range(0, MaxSpeed); velocitySample = MathHelper.ConvertOrientationToVector(angle) * magnitude; samples.Add(velocitySample); } base.Target.velocity = GetBestSample(samples, DesiredVelocity); return(base.GetMovement()); }
/// <summary> /// Applies the values set by the brain in DesiredMovement to this chassis. /// </summary> /// <param name="movement"></param> /// <param name="desiredMovement"></param> public override void ApplyDesiredMovement(Movement movement, DesiredMovement desiredMovement) { // Apply velocity changes. ApplyDesiredVelocityForHover(movement, desiredMovement); // Apply rotation changes. ApplyDesiredRotation(movement, desiredMovement); // Apply vertical movement. ApplyDesiredVerticalMovement(movement, desiredMovement); /* * // Clamp velocity to prevent side to side motion. * if (movement.Velocity.LengthSquared() > 5.0f) * { * Vector3 right = Vector3.Cross(movement.Facing, Vector3.UnitZ); * right.Normalize(); * float dot = Vector3.Dot(right, movement.Velocity); * // The 0.8 factor just prevents this from clamping down absolutely. * // It give the motion a bit more fluid feel. * movement.Velocity -= 0.8f * dot * right; * } */ // Clamp velocity to forward/backward. Don't mess with vertical movement. // Calc facing dir without any Z component. /* * if (movement.Velocity.LengthSquared() > 5.0f) * { * Vector3 forward = movement.Facing * new Vector3(1, 1, 0); * forward.Normalize(); * float dot = Vector3.Dot(forward, movement.Velocity); * Vector3 vel = dot * forward; * movement.Velocity = new Vector3(vel.X, vel.Y, movement.Velocity.Z); * } */ } // end of ApplyDesiredMovement()
} // end of PreCollisionTestUpdate() public override void SetLoopedAnimationWeights(AnimationSet anims, Movement movement, DesiredMovement desiredMovement) { anims.IdleWeight = anims.IsOpen ? 1.0f : 0.0f; anims.ForwardWeight = 0.0f; anims.BackwardsWeight = 0.0f; anims.RightWeight = 0.0f; anims.LeftWeight = 0.0f; } // end of SetLoopedAnimationWeights()
} // end of DynamicChassis CollisionResponse() /// <summary> /// Applies the values set by the brain in DesiredMovement to this chassis. /// </summary> /// <param name="movement"></param> /// <param name="desiredMovement"></param> public override void ApplyDesiredMovement(Movement movement, DesiredMovement desiredMovement) { GameActor actor = Parent as GameActor; ApplyDesiredRotation(movement, desiredMovement); } // end of ApplyDesiredMovement()
public override void PreCollisionTestUpdate(GameThing thing) { Movement movement = thing.Movement; DesiredMovement desiredMovement = thing.DesiredMovement; GameActor.State state = thing.CurrentState; UpdateDustEmitter(state); // Only do the update if this thing is active and moving. if (((state == GameActor.State.Active) || (state == GameThing.State.Dead || state == GameThing.State.Squashed)) && Moving) { float dt = (float)Time.GameTimeFrameSeconds; floating = false; inWater = false; // Start with existing values. Vector3 position = movement.Position; Vector3 prevPosition = position; Vector3 velocity = movement.Velocity; // Are we in water? if (Terrain.GetWaterBase(position) > 0.0f) { Vector3 surfaceNormal = Vector3.UnitZ; float waterAlt = Terrain.GetWaterHeightAndNormal(position, ref surfaceNormal); if (waterAlt > 0) { inWater = true; CheckRipples(thing, velocity, waterAlt); } // Are we in the water? if (position.Z < waterAlt) { // TODO (****) This could be made better by also having // the density control how deep an object floats. // Should we sink or float? if (Density > 1.0f) { // Sink. // Add attenuated gravity. float attenuation = 1.0f - 1.0f / Density; velocity.Z += attenuation * Gravity * dt; } else { // Float. // Add attenuated, inverted gravity. float attenuation = 1.0f - Density; velocity.Z -= attenuation * Gravity * dt; // If we're floating near the surface, have the waves move us a bit. float depth = waterAlt - position.Z; if (depth < 0.5f) { float amp = (0.5f - depth) * 20.0f; velocity.X += surfaceNormal.X * dt * amp; velocity.Y += surfaceNormal.Y * dt * amp; floating = true; } } // Now attenuate velocity to simulate drag from water. velocity = MyMath.Lerp(velocity, Vector3.Zero, 2.0f * dt); } else { // Not in water. // Add in effect of full gravity. velocity.Z += Gravity * dt; } } else { // No water. // Add in effect of full gravity. velocity.Z += Gravity * dt; } // Apply external force. velocity += desiredMovement.ExternalForce.GetValueOrDefault() / thing.Mass * dt; // Update location. position.X += velocity.X * dt; position.Y += velocity.Y * dt; // Push changes back to movement. movement.Velocity = velocity; movement.Position = position; // If we're version 1 or later also do rotation based on brain. if (Parent.Version >= 1) { ApplyDesiredMovement(movement, desiredMovement); } } // end of if moving else if (state != GameActor.State.Active && thing.ActorHoldingThis == null && state != GameThing.State.Dead && state != GameThing.State.Squashed && !Moving) { // We're not active but we still want to be able to have our height adjusted. movement.Altitude = Terrain.GetTerrainAndPathHeight(Top(movement.Position)) + EditHeight; } } // end of DynamicPropChassis PreCollisionTestUpdate()
} // end of PostCollisionTestUpdate() /// <summary> /// Based on the chassis' internal values, sets the blend values for the /// four standard looping animations. /// </summary> /// <param name="anims"></param> public override void SetLoopedAnimationWeights(AnimationSet anims, Movement movement, DesiredMovement desiredMovement) { StandardSetLoopedAnimationWeights(anims, movement, desiredMovement); } // end of SetLoopedAnimationWeights()
public override void PreCollisionTestUpdate(GameThing thing) { Movement movement = thing.Movement; DesiredMovement desiredMovement = thing.DesiredMovement; GameActor.State state = thing.CurrentState; // Are we being held? if (thing.ActorHoldingThis != null) { grounded = false; return; } float secs = Time.GameTimeFrameSeconds; Vector3 position = movement.Position; Vector3 bow = position + 0.9f * movement.Facing; // Test for grounding. { float waterAltitude = Terrain.GetWaterHeight(position); float terrainAltitude = Terrain.GetTerrainAndPathHeight(Top(position)); grounded = (terrainAltitude > 0) && (terrainAltitude > waterAltitude - HullDraft); } if (state == GameActor.State.Active) { if (!grounded) { // Apply DesiredMovement values to current movement. ApplyDesiredMovement(movement, desiredMovement); if (Jump && CanJump() && !jumping && !landing) { startJumpAnimation = true; // Tell animation to start. jumping = true; jumpStartTime = Time.GameTimeTotalSeconds; } Jump = false; if (jumping) { // If the pre delay time has passed, do the jump. if (Time.GameTimeTotalSeconds > jumpStartTime + preJumpDelay) { movement.Velocity += new Vector3(0, 0, effectiveJumpStrength); jumping = false; landing = true; lastJumpTime = Time.GameTimeTotalSeconds; } } // Apply external force. movement.Velocity += desiredMovement.ExternalForce.GetValueOrDefault() / thing.Mass * secs; // Apply drag to velocity. ApplyFriction(movement, desiredMovement, applyVertical: true); // Apply velocity to position. movement.Position += movement.Velocity * secs; } // end if not grounded. else { // We're grounded but we may be high enough to stil be falling. // Apply velocity to position. movement.Position += movement.Velocity * secs; } } // End of if active. else { // Not active but we still want to be able to move vertically // if the water level is changing in the editor. //movement.Altitude += movement.Velocity.Z * Time.WallClockFrameSeconds; //grounded = false; } // Check for hitting the ground. Only do this if we're active and not fully grounded. if (!grounded && (state != GameActor.State.Dead || state != GameActor.State.Squashed)) { bow = position + 0.9f * movement.Facing; float waterBase = Terrain.GetWaterBase(bow); float terrainAltitude = Terrain.GetHeight(bow); if (terrainAltitude < waterBase) { // Only slide if active. if (state == GameActor.State.Active) { float waterAltitude = Terrain.GetWaterHeight(bow); // Are we hitting ground? If so, reduce speed and slide back a bit. if (position.Z - hullDraft < terrainAltitude) { // Stuck. speed *= 1.0f - secs; speed = 0.0f; // Slide down hill a bit. Vector3 terrainNormal = Terrain.GetNormal(movement.Position); terrainNormal.Z = 0.0f; movement.Position += terrainNormal * 2.0f * secs; movement.Velocity = terrainNormal; } } } } // End of if not grounded or dead. velocityZ = movement.Velocity.Z; } // end of PreCollisionTestUpdate()
//basic movement logic from hover chassis, added velocity using logic from swim chassis for when under water public override void PreCollisionTestUpdate(GameThing thing) { Movement movement = thing.Movement; DesiredMovement desiredMovement = thing.DesiredMovement; GameActor.State state = thing.CurrentState; // Are we being held? if (thing.ActorHoldingThis != null) { return; } float secs = Time.GameTimeFrameSeconds; // Calc the height we want to be at. float waterAltitude = Terrain.GetWaterBase(movement.PrevPosition); float terrainAltitude = 0.0f; Vector3 terrainNormal = Vector3.Zero; GetTerrainAltitudeAndNormalFromFeelers(movement, ref terrainAltitude, ref terrainNormal); // Heights are distance above ground, not absolute values. float heightGoal = EditHeight; float floor = terrainAltitude; Vector3 collCenter = thing.WorldCollisionCenter; bool underWater = false; // Only move if active. if (state == GameActor.State.Active) { // Bounce off ground? bool bounce = false; // Under water? if (waterAltitude > 0 && (waterAltitude - Terrain.WaveHeight) > (collCenter.Z + thing.WorldCollisionRadius)) { underWater = true; } float height = movement.Position.Z - Parent.CollisionCenter.Z - Parent.CollisionRadius - floor; if (allowBrainMovement) { // Apply DesiredMovement values to current movement. ApplyDesiredMovement(movement, desiredMovement); // If not underwater we should ignore any velocity changes. We still // need to process them so that we get turning correct. if (!underWater) { // Undo velocity changes. movement.Velocity = movement.PrevVelocity; } bounce = CollideWithGround(movement, ref height); // Create a dust puff when bouncing off dry ground. // TODO (****) This doesn't seem to actually do anything. As far as I can // tell the CreateDustPuff() call is never called by anything else so it may // not work at all. May be worth looking into if you're bored some time. /* * if (bounce && waterAltitude == 0) * { * // Must have bounced on dry ground. Give a puff * // of dust if moving fast enough. * if (Math.Abs(movement.Velocity.Z) > 1.0f) * { * ExplosionManager.CreateDustPuff(movement.Position, Parent.CollisionRadius, 1.0f); * } * } */ } // end if BrainMovement // Apply external force. movement.Velocity += desiredMovement.ExternalForce.GetValueOrDefault() / thing.Mass * secs; // Apply drag to velocity. if (underWater) { ApplyFriction(movement, desiredMovement, applyVertical: true); } // Lerp pitch based on vertical rate. /* * if (desiredMovement.Coasting) * { * pitch = MyMath.Lerp(pitch, 0, secs); * } * else * { * float deltaZ = movement.Velocity.Z; * pitch = MyMath.Lerp(pitch, deltaZ, secs); * } */ // If not underwater then we should fall to the ground. If we just hit the ground, skip // adding gravity since that will force us too deep into the ground. if (!underWater && !bounce) { float s0 = (-movement.Velocity.Z + (float)Math.Sqrt(movement.Velocity.Z * movement.Velocity.Z - 4 * Gravity * height)) / (2.0f * Gravity); float s1 = (-movement.Velocity.Z - (float)Math.Sqrt(movement.Velocity.Z * movement.Velocity.Z - 4 * Gravity * height)) / (2.0f * Gravity); float s = MathHelper.Max(s0, s1); // s will be negative when falling off the edge of the world. if (float.IsNaN(s) || s < 0) { s = secs; } else { s = MathHelper.Min(s, secs); } // Calc affect of gravity using minimum of frame secs or time until we hit the ground. // By using the shorter of the two we help prevent things from getting driven into the ground. Vector3 velocity = movement.Velocity; velocity.Z += Gravity * s; movement.Velocity = velocity; } // Apply velocity to position. We don't do this on the frame where a bounce occurs. By // skipping this frame we help stabilize the behavior on the ground. The actor sits without bouncing. if (!bounce) { movement.Position += movement.Velocity * secs; } // If body is intersecting with water surface, add some splashes/ripples. if (waterAltitude != 0) { // Test if the collision sphere is breaking the surface of the water. If we falling in, create a splash. // If we're just cruising along, create ripples. Note that the extra 1.5f scaling is to make the ripples // appear even if we just get close to the surface. if (movement.Position.Z - Parent.CollisionRadius < waterAltitude && movement.Position.Z + Parent.CollisionRadius * 1.5f > waterAltitude) { CheckSplash(Parent, movement.Position, movement.Velocity); CheckRipples(Parent, Parent.CollisionRadius); } } } // end of if state is active. // If paused we still want to apply vertical motion // so that in edit mode we maintain the correct height. if (state == GameThing.State.Paused) { Vector3 position = movement.Position; position.Z = floor + EditHeight; movement.Position = position; } } // end of PreCollisionTestUpdate()
public override void PreCollisionTestUpdate(GameThing thing) { Movement movement = thing.Movement; DesiredMovement desiredMovement = thing.DesiredMovement; GameActor.State state = thing.CurrentState; float secs = Time.GameTimeFrameSeconds; if (state != GameActor.State.Dead || state != GameThing.State.Squashed) { if (state == GameActor.State.Active) { // Only do movement if we are not being held. if (thing.ActorHoldingThis == null) { // Check if we're up in the air. If so, then we don't kick up // any dust. We can still turn while in the air but it doesn't // affect the direction we're moving until we hit the ground. float terrainHeight = Terrain.GetTerrainAndPathHeight(Top(movement.Position)); bool falling = terrainHeight == 0.0f; float heightAboveGround = movement.Altitude - terrainHeight - MinHeight; if (landing) { // This version doesn't clamp movement to only work forward and backward. ApplyDesiredMovementWhenFalling(movement, desiredMovement); } else { ApplyDesiredMovement(movement, desiredMovement); } // Are we waiting to land? if (landing && movement.Velocity.Z < 0.0f) { // Are we close enough? float predictedHeight = heightAboveGround + movement.Velocity.Z * preLandDelay; if (predictedHeight < CloseToGround) { startLandAnimation = true; landing = false; if (TerrainDataValid && Feelers != null) { Foley.PlayCollision(thing, Feelers[0].TerrainMaterialInfo.TerrainType); } else { Foley.PlayCollision(thing, 0); } } } onGround = heightAboveGround < CloseToGround; onGround = onGround && !falling; if (onGround) { // The bot is on the ground. // Only jump if on the ground and not already jumping. if (Jump && !jumping && !landing) { startJumpAnimation = true; // Tell animation to start. jumping = true; jumpStartTime = Time.GameTimeTotalSeconds; jumpVelocity = new Vector2(movement.Velocity.X, movement.Velocity.Y); float len = jumpVelocity.Length(); if (len > 0.1f) { jumpVelocity /= len; } else { jumpVelocity = new Vector2(movement.Facing.X, movement.Facing.Y); jumpVelocity.Normalize(); } } } // Always clear jump flag. Jump = false; if (jumping && !landing) { // If the pre delay time has passed, do the jump. if (Time.GameTimeTotalSeconds > jumpStartTime + preJumpDelay) { movement.Velocity += new Vector3(0, 0, effectiveJumpStrength); jumping = false; landing = true; } } HandleMovement(movement); // Apply drag to velocity. ApplyFriction(movement, desiredMovement, applyVertical: false); // Apply external force. movement.Velocity += desiredMovement.ExternalForce.GetValueOrDefault() / thing.Mass * secs; // Move due to velocity. movement.Position += movement.Velocity * secs; } // end of if not held. } // end of if active. } // end of if not dead } // end of PreCollisionTestUpdate()
} // end of PreCollisionTestUpdate() public override void PostCollisionTestUpdate(GameThing thing) { Movement movement = thing.Movement; float secs = Time.GameTimeFrameSeconds; // Init the target Altitude. This should only happen the very first time // after a reset. We want to use the bot's initial height as the original // target height but at the time the c'tor is called we don't yet know // where the bot will be positioned. if (startingAltitude == DefaultStartingAltitude) { startingAltitude = Terrain.GetTerrainAndPathHeight(Top(movement.Position)) + EditHeight; if (Parent.StayAboveWater) { startingAltitude = Math.Max(startingAltitude, Terrain.GetWaterHeight(movement.Position) + EditHeight); } } // // Now handle "automatic" vertical movement. We want the charactors to try and stay at their // startingAltitude. // // Look at desiredMovement to see if user is actively changing altitude. If // so then adjust startingAltitude to match. DesiredMovement desiredMovement = Parent.DesiredMovement; bool coastingVertically = desiredMovement.CoastingVertically; if (!coastingVertically) { if (desiredMovement.DesiredAltitude.HasValue) { startingAltitude = desiredMovement.DesiredAltitude.Value; } if (desiredMovement.DesiredVerticalSpeed.HasValue) { // If we're moving up/down, assume current altitude is the new target. startingAltitude = movement.Altitude; } } // Adjust height for terrain. float waterAltitude = Terrain.GetWaterBase(movement.Position); float terrainAltitude = MaxFeelerAltitude(movement, movement.Position, thing.ReScale); // altitudeBase is the ground/water we're flying over and basing our height on. float altitudeBase = thing.StayAboveWater ? MathHelper.Max(terrainAltitude, waterAltitude) : terrainAltitude; float altitudeGoal = altitudeBase + EditHeight; altitudeGoal = Math.Max(altitudeGoal, startingAltitude); targetAltitude = 0; // Bounce off ground? if (movement.Altitude < terrainAltitude) { BounceOffGround(thing, terrainAltitude, Vector3.UnitZ); } movement.Altitude = MyMath.Lerp(movement.Altitude, altitudeGoal, 0.1f * 30.0f * secs); // Apply this rotation to the actor. This rotation just makes the character // spin. It has no effect on the heading or facing direction. float angle = movement.RotationZ + (float)Time.GameTimeTotalSeconds * RotationRate; Matrix mat = Matrix.CreateRotationZ(angle); mat.Translation = movement.Position; // Note that we're just setting the rotation angle back to the // original value. The extra rotation only goes into LocalMatrix. movement.SetLocalMatrixAndRotation(mat, movement.RotationZ); } // end of PostCollisionTestUpdate()
public override void PreCollisionTestUpdate(GameThing thing) { Movement movement = thing.Movement; DesiredMovement desiredMovement = thing.DesiredMovement; GameActor.State state = thing.CurrentState; if (thing.ActorHoldingThis != null) { return; } float secs = Time.GameTimeFrameSeconds; // Only move if active. if (state == GameActor.State.Active) { if (AllowBrainMovement) { // Apply DesiredMovement values to current movement. ApplyDesiredMovement(movement, desiredMovement); } // Apply external force. movement.Velocity += desiredMovement.ExternalForce.GetValueOrDefault() / thing.Mass * secs; // Apply drag to velocity. ApplyFriction(movement, desiredMovement, applyVertical: false); // Create local copies to work on. Will be copied back to movement below. Vector3 velocity = movement.Velocity; Vector3 position = movement.Position; // Apply velocity to position.movement. position += velocity * secs; if (Jump && CanJump()) { // If we've already jumped but not yet landed or double-jumped, // see if we're allowed to double jump. if (landing && !doubleJumping) { // Look for the vertical velocity to be slow as an // indicator of the top of the arc. if (Math.Abs(velocity.Z) < 0.15f) { doubleJumping = true; velocity.Z += 1.5f * effectiveJumpStrength; } } else if (!landing && !jumping) { startJumpAnimation = true; // Tell animation to start. jumping = true; jumpStartTime = Time.GameTimeTotalSeconds; } } Jump = false; if (jumping) { // If the pre delay time has passed, do the jump. if (Time.GameTimeTotalSeconds > jumpStartTime + preJumpDelay) { velocity.Z += effectiveJumpStrength; jumping = false; landing = true; lastJumpTime = Time.GameTimeTotalSeconds; } } // Copy changes back to movement. movement.Position = position; movement.Velocity = velocity; } // end of if state is active. // // Balance hover force with gravity so that chassis stays at the right height. // Note that we use the previous position for this since we "know" it's valid. // { // Create local copies to work on. Will be copied back to movement below. Vector3 velocity = movement.Velocity; Vector3 position = movement.Position; Vector3 prevPosition = movement.PrevPosition; // Calc the height we want to be at. float waterAltitude = Terrain.GetWaterBase(prevPosition); float terrainAltitude = 0.0f; Vector3 terrainNormal = Vector3.Zero; GetTerrainAltitudeAndNormalFromFeelers(movement, ref terrainAltitude, ref terrainNormal); // Heights are distance above ground, not absolute values. float heightGoal = EditHeight; float floor = 0.0f; // Level we're hovering over. bool overWater = false; if (thing.StayAboveWater) { if (waterAltitude > terrainAltitude) { floor = waterAltitude; overWater = true; if (!landing) { /// Only apply ripples if we are _not_ in the middle /// of a jump. CheckRipples(thing, thing.CollisionRadius); } } else { floor = terrainAltitude; } } else { floor = terrainAltitude; /// Look to see if we are intersecting the water surface, /// and if so kick off some ripples. Vector3 collCenter = thing.WorldCollisionCenter; float collRad = thing.WorldCollisionRadius; if ((waterAltitude - Terrain.WaveHeight < collCenter.Z + collRad) && (waterAltitude > collCenter.Z - collRad)) { CheckRipples(thing, collRad * 1.5f); } } // If there's no terrain or we're already falling, just let the bot fall. // Compare against -1 to ensure that we can't get pushed through the ground. if (floor == 0.0f || position.Z - MinHeight < -1.0f) { floor = float.MinValue; } float curHeight = position.Z - floor; // Are we waiting to land? if (landing) { // Are we close enough? float predictedHeight = curHeight + velocity.Z * preLandDelay; if (predictedHeight < heightGoal) { startLandAnimation = true; landing = false; doubleJumping = false; } } /* * // Did we bounce into the ground? * if (curHeight < MinHeight) * { * position.Z = floor + MinHeight; // Move back to above ground. * velocity.Z = Math.Max(velocity.Z, 0.1f); * * // TODO (****) Bump sound and dust cloud? * } * else */ { float ratio = heightGoal / curHeight; if (float.IsNaN(ratio)) { ratio = 0.0f; } float hoverEffect = ratio * -Gravity; float lift = hoverEffect + Gravity; // Clamp lift to limits. if (lift > 0.0f) { // Clamp to maximum acceleration up. Allow vertical acceleration to be 10x the // normal linear acceleration. This will help keep bots out of the ground. lift = 10.0f * MathHelper.Clamp(lift, 0, MaxLinearAcceleration * LinearAccelerationModifier); } else { // Clamp to gravity down. lift = MathHelper.Clamp(lift, Gravity, 0); } float deltaZ = lift * secs; velocity.Z *= (1.0f - secs); // Damp vertical velocity. velocity.Z += deltaZ; // If lift > 0 clamp the upward velocity to the max allowed without overshoot. if (lift > 0) { float d = heightGoal - curHeight; float t = (float)Math.Sqrt(Math.Abs(2.0f * d / Gravity)); float speed = -t * Gravity; velocity.Z = Math.Min(velocity.Z, speed); } // Did we bounce off the ground? if (curHeight < 0.0f) { // Move position back above ground. position.Z -= curHeight; // Make sure we're moving up, not down. Also apply some agressive damping. // This prevents hover chassis bots from getting too bouncy if they are // too close to the ground. velocity.Z = (float)Math.Abs(velocity.Z) * CoefficientOfRestitution * 0.2f; } } // Add in effect of slope. if (!overWater && Moving) { // Attenuate SlopeAttenuation based on how high we are. Basically, if // we're way up in the air, ignore slopes. float slopeFactor = SlopeAttenuation; if (curHeight > EditHeight) { if (curHeight > EditHeight * 2.0f) { // Too high, ignore slope. slopeFactor = 0.0f; } else { slopeFactor *= 1.0f - (curHeight - EditHeight) / EditHeight; } } if (slopeFactor > 0) { // Translate any change in height to a change in velocity. float deltaZ = prevPosition.Z - position.Z; float deltaV = (float)Math.Sqrt(Math.Abs(2.0f * Gravity * deltaZ)); // Always have a minimal deltaV. This causes things to start // sliding downhill if not moving. deltaV = Math.Max(deltaV, 0.01f); // Get direction apply new velocity. terrainNormal.Z = 0.0f; velocity += slopeFactor * deltaV * terrainNormal; } } // Apply vertical velocity to position. position.Z += velocity.Z * secs; // Copy changes back to movement. movement.Velocity = velocity; movement.Position = position; } } // end of PreCollisionTestUpdate()
} // end of PreCollisionTestUpdate() public override void SetLoopedAnimationWeights(AnimationSet anims, Movement movement, DesiredMovement desiredMovement) { float idleWeight = 0.0f; float forwardWeight = 0.0f; float backwardsWeight = 0.0f; float rightWeight = 0.0f; float leftWeight = 0.0f; // For the sub and fish the forward and backward animations // are used for dive and surface so look at the pitch value // and assign weights from that. if (pitch > 0) { // Surfacing backwardsWeight = Math.Min(pitch / maxPitch, 1.0f);; } else { forwardWeight = Math.Max(-pitch / maxPitch, -1.0f); } // Blend left/right animations based only soley on current rotation rate. { // Both the current rotation and the desired rotation are // in the range 0..2pi so shift into -pi..pi range to make // the comparison easier. float delta = movement.RotationZRate; if (delta > 0.0f) { rightWeight = Math.Min(delta / MathHelper.PiOver2, 1.0f); } else { leftWeight = Math.Min(-delta / MathHelper.PiOver2, 1.0f); } } // Bias weights toward either full or off with a 10% flat area. forwardWeight = MyMath.SmoothStep(0.1f, 0.9f, forwardWeight); rightWeight = MyMath.SmoothStep(0.1f, 0.9f, rightWeight); leftWeight = MyMath.SmoothStep(0.1f, 0.9f, leftWeight); // Adjust weights to sum to 1.0. float total = forwardWeight + backwardsWeight + rightWeight + leftWeight; if (total > 1.0f) { forwardWeight /= total; backwardsWeight /= total; leftWeight /= total; rightWeight /= total; } else { // Fill in with idle. idleWeight = 1.0f - total; } // Set resulting weights on animation set. anims.IdleWeight = idleWeight; anims.ForwardWeight = forwardWeight; anims.BackwardsWeight = backwardsWeight; anims.RightWeight = rightWeight; anims.LeftWeight = leftWeight; } // end of SetLoopedAnimationWeights()
public override void PostCollisionTestUpdate(GameThing thing) { Movement movement = thing.Movement; GameActor.State state = thing.CurrentState; float secs = Time.GameTimeFrameSeconds; if (state == GameActor.State.Active) { // Init the target Altitude. This should only happen the very first time // after a reset. We want to use the bot's initial height as the original // target height but at the time the c'tor is called we don't yet know // where the bot will be positioned. if (startingAltitude == DefaultStartingAltitude) { startingAltitude = Terrain.GetTerrainAndPathHeight(Top(movement.Position)) + EditHeight; if (Parent.StayAboveWater) { startingAltitude = Math.Max(startingAltitude, Terrain.GetWaterHeight(movement.Position) + EditHeight); } } // // Now handle "automatic" vertical movement. We want the charactors to try and stay at their // startingAltitude. // // Look at desiredMovement to see if user is actively changing altitude. If // so then adjust startingAltitude to match. DesiredMovement desiredMovement = Parent.DesiredMovement; bool coastingVertically = desiredMovement.CoastingVertically; if (!coastingVertically) { if (desiredMovement.DesiredAltitude.HasValue) { startingAltitude = desiredMovement.DesiredAltitude.Value; } if (desiredMovement.DesiredVerticalSpeed.HasValue) { // If we're moving up/down, assume currentl altitude is the new target. startingAltitude = movement.Altitude; } } // // From here down is "old" code (pre movement refactor) that still seems to have // the right feel so just leave it. // // Calc the min height so that we don't hit the ground (or water). // Do some look-ahead so we better handle cliffs. float lookAheadHeight = Terrain.GetTerrainAndPathHeight(Top(movement.Position + movement.Facing * 3.0f)); float terrainHeight = Terrain.GetTerrainAndPathHeight(Top(movement.Position)); float waterHeight = Terrain.GetWaterBase(movement.Position); if (waterHeight > 0) { CheckRipples(thing, waterHeight); } float minimumHeight = MathHelper.Max(terrainHeight, lookAheadHeight); if (thing.StayAboveWater) { minimumHeight = MathHelper.Max(minimumHeight, waterHeight); } minimumHeight += MinHeight; minimumHeight += VerticalStoppingDistance(); bool tooLow = movement.Altitude < minimumHeight; bool tooHigh = movement.Altitude > maxAltitude; float goalAltitude = Math.Max(startingAltitude, minimumHeight); if (targetAltitude > 0) { goalAltitude = Math.Max(minimumHeight, targetAltitude); targetAltitude = 0; } // Add a bit of a dead zone around the target altitude. float targetCushion = 0.5f; if (tooLow || movement.Altitude < goalAltitude - targetCushion) { // have increased acceleration upward when near ground. float scaleFactor = tooLow ? 2.0f : 1.0f; // Accelerate upward. deltaAltitude += maxVerticalAcceleration * scaleFactor * secs * VerticalSpeedMultiplier; // Clamp if (deltaAltitude > maxVerticalSpeed * VerticalSpeedMultiplier) { deltaAltitude = maxVerticalSpeed * VerticalSpeedMultiplier; } } else if (tooHigh || movement.Altitude > goalAltitude + targetCushion) { // Accelerate downward. deltaAltitude -= maxVerticalAcceleration * secs * VerticalSpeedMultiplier; // Clamp if (deltaAltitude < -maxVerticalSpeed * 0.7f * VerticalSpeedMultiplier) { deltaAltitude = -maxVerticalSpeed * 0.7f * VerticalSpeedMultiplier; } } else { // If not going up or down, damp out vertical acceleration. deltaAltitude *= 1.0f - secs; } movement.Altitude += deltaAltitude * secs; } // end of if active else { // Still need to adjust the height if in edit mode. float terrainHeight = Terrain.GetTerrainAndPathHeight(Top(movement.Position)); if (thing.StayAboveWater) { float waterHeight = Terrain.GetWaterBase(movement.Position); terrainHeight = MathHelper.Max(terrainHeight, waterHeight); } terrainHeight += EditHeight; movement.Altitude = terrainHeight; } } // end of PreCollisionTestUpdate()
public override void PreCollisionTestUpdate(GameThing thing) { Movement movement = thing.Movement; DesiredMovement desiredMovement = thing.DesiredMovement; GameActor.State state = thing.CurrentState; float secs = Time.GameTimeFrameSeconds; Vector3 waterNormal = Vector3.UnitZ; float waterAltitude = Terrain.GetWaterHeightAndNormal(movement.Position, ref waterNormal); // Only move if active. if (state == GameActor.State.Active) { movement.RotationZRate = rotationRate; // Now that we've updated the rotation rate, apply this to the actual rotation. //movement.Rotation += movement.RotationRate * secs; // Apply DesiredMovement values to current movement. ApplyDesiredMovement(movement, desiredMovement); float height = 0; bool bounce = CollideWithGround(movement, ref height); // Apply external force. movement.Velocity += desiredMovement.ExternalForce.GetValueOrDefault() / thing.Mass * secs; // Apply drag to velocity. ApplyFriction(movement, desiredMovement, applyVertical: true); // Apply velocity to position. movement.Position += movement.Velocity * secs; // If not moving, and on a slope (or a wave), give a nudge. if (movement.Speed < 0.1f) { Vector3 normal = Vector3.UnitZ; if (waterAltitude > 0) { normal = waterNormal; } else { normal = Terrain.GetNormal(movement.Position); } if (normal.Z < slopeThreshold) { Vector3 velocity = movement.Velocity; velocity.X += normal.X * 10.0f * secs; velocity.Y += normal.Y * 10.0f * secs; movement.Velocity = velocity; } } if (waterAltitude > 0.0f) { Vector3 collCenter = thing.WorldCollisionCenter; float collRad = thing.WorldCollisionRadius; if ((waterAltitude - Terrain.WaveHeight < collCenter.Z + collRad) && (waterAltitude > collCenter.Z - collRad)) { CheckRipples(thing, collRad); } } } // end of if state is active. } // end of PreCollisionTestUpdate()
} // end of PreCollisionTestUpdate() public override void PostCollisionTestUpdate(GameThing thing) { Movement movement = thing.Movement; DesiredMovement desiredMovement = thing.DesiredMovement; GameActor.State state = thing.CurrentState; // Zero out the vertical velocity, the puck ignores it. Vector3 velocity = movement.Velocity; Vector3 position = movement.Position; float secs = Time.GameTimeFrameSeconds; // Only zero out velocity.Z if we're not trying to adjust vertical. if (desiredMovement.CoastingVertically) { velocity.Z = 0; } else { // If we're actively moving up/down, we need to adjust height offset. thing.HeightOffset += velocity.Z * secs; } // Adjust height for terrain. We do this outside of the check for Active // so that we stay at the right height if we're being dragged around. // Note that for the position we use the max Z of the current and previous // positions. This way we can always detect the ground without getting // pushed through it. if (position.Z < movement.PrevPosition.Z) { position.Z = movement.PrevPosition.Z; } float terrainAltitude = 0.0f; Vector3 terrainNormal = Vector3.UnitZ; // Feelers aren't valid in edit mode. if (state == GameThing.State.Paused) { terrainAltitude = Terrain.GetTerrainAndPathHeight(Top(position)); } else { GetTerrainAltitudeAndNormalFromFeelers(movement, ref terrainAltitude, ref terrainNormal); } Vector3 waterNormal = Vector3.UnitZ; float waterAltitude = Terrain.GetWaterHeightAndNormal(movement.Position, ref waterNormal); // Decide what the altitude and normal is under us. float baseAltitude = 0.0f; Vector3 baseNormal = Vector3.UnitZ; if (thing.StayAboveWater) { if (terrainAltitude > waterAltitude) { baseAltitude = terrainAltitude; baseNormal = terrainNormal; } else { baseAltitude = waterAltitude; baseNormal = waterNormal; } } else { baseAltitude = terrainAltitude; baseNormal = terrainNormal; } if (waterAltitude > 0) { CheckRipples(thing, waterAltitude); } // If nothing is there or we're already under the terrain, let the bot fall. if (baseAltitude == 0.0f || position.Z < 0.0f) { speedDown += Gravity * secs; movement.Altitude += speedDown * secs; } else { float heightGoal = EditHeight + baseAltitude; heightGoal = Math.Max(heightGoal, targetAltitude); float t = Math.Min(0.2f * 30.0f * MovementSpeedModifier * secs, 1.0f); movement.Altitude = MyMath.Lerp(movement.Altitude, heightGoal, t); // Ensure we don't go under ground. if (movement.Altitude < baseAltitude) { movement.Altitude = baseAltitude; } } if (state == GameThing.State.Active) { // Translate any change in height to a change in velocity but only do so if actually over terrain // and at low altitude, and not touching the ground, and not moving upward. if (terrainAltitude > 0.0f && position.Z > 0.0f && !impactingFloor) { float deltaZ = movement.Position.Z - movement.PrevPosition.Z; if (deltaZ != 0.0f) { float deltaV = (float)Math.Sqrt(Math.Abs(2.0f * Gravity * deltaZ)); // Get direction apply new velocity. baseNormal.Z = 0.0f; velocity += deltaV * baseNormal; } } // If the puck has impacted the ground, reduce lateral velocity if (impactingFloor) { velocity.X *= 0.9f * secs; velocity.Y *= 0.9f * secs; } // Apply the continuous rotational effect. // Apply this rotation to the actor. This rotation just makes the character // spin. It has no effect on the heading or facing direction. float angle = movement.RotationZ + (float)Time.GameTimeTotalSeconds * RotationRate; Matrix mat = Matrix.CreateRotationZ(angle); mat.Translation = movement.Position; // Note that we're just setting the rotation angle back to the // original value. The extra rotation only goes into LocalMatrix. movement.SetLocalMatrixAndRotation(mat, movement.RotationZ); } // If moving too fast, damp down a bit, but only if we have a target velocity. // If there isn't one, just let it move freely. float speed = velocity.Length(); if (speed > 0 && desiredMovement.DesiredVelocity != null && speed > desiredMovement.MaxSpeed) { float delta = (speed - desiredMovement.MaxSpeed) * secs; velocity *= (1 - delta); } movement.Velocity = velocity; // The puck has no real "heading" which is used for Move Forward. // To make movement work correctly, force heading to match the // current moving direction. // We can't set heading directly so set z rotation. // Note that this causes the visual rotation to glitch on bouncing // but at least the overall physical behaviour is correct. movement.RotationZ = MyMath.ZRotationFromDirection(velocity); } // end of PuckChassis PostCollisionTestUpdate()
} // end of PuckChassis PostCollisionTestUpdate() public override void SetLoopedAnimationWeights(AnimationSet anims, Movement movement, DesiredMovement desiredMovement) { float forward = movement.Speed / MaxSpeed; anims.IdleWeight = 1.0f - forward; anims.ForwardWeight = forward; anims.BackwardsWeight = 0.0f; anims.RightWeight = 0.0f; anims.LeftWeight = 0.0f; } // end of SetLoopedAnimationWeights()
} // end of PreCollisionTestUpdate() public override void SetLoopedAnimationWeights(AnimationSet anims, Movement movement, DesiredMovement desiredMovement) { float idleWeight = 0.0f; float forwardWeight = 0.0f; float backwardWeight = 0.0f; float leftWeight = 0.0f; float rightWeight = 0.0f; // Blend animations based only on relationship between // actual facing direction and desired heading. This // ignores current speed or rotation rate. if (desiredMovement.DesiredVelocity.HasValue || desiredMovement.DesiredTargetLocation.HasValue) { Vector3 facing = movement.Facing; Vector3 desiredVelocity = facing; // Value will be overwritten. if (desiredMovement.DesiredVelocity.HasValue) { // Trying to move in explicit direction. desiredVelocity = desiredMovement.DesiredVelocity.Value; } if (desiredMovement.DesiredTargetLocation.HasValue) { // Trying to move toward target. desiredVelocity = desiredMovement.DesiredTargetLocation.Value - movement.Position; } desiredVelocity.Normalize(); if (!float.IsNaN(desiredVelocity.X)) { forwardWeight = Math.Max(0.0f, Vector3.Dot(desiredVelocity, facing)); Vector3 right = Vector3.Cross(facing, Vector3.UnitZ); right.Normalize(); rightWeight = Math.Max(0.0f, Vector3.Dot(right, desiredVelocity)); leftWeight = Math.Max(0.0f, Vector3.Dot(-right, desiredVelocity)); } } else if (desiredMovement.DesiredRotationAngle.HasValue || desiredMovement.DesiredRotationRate.HasValue) { // If we aren't setting values based on velocity changes, maybe set them based on turning. float curHeading = movement.RotationZ; float desiredHeading = curHeading; // Value will be overwritten. if (desiredMovement.DesiredRotationAngle.HasValue) { desiredHeading = desiredMovement.DesiredRotationAngle.Value; } if (desiredMovement.DesiredRotationRate.HasValue) { desiredHeading = curHeading + Math.Sign(desiredMovement.DesiredRotationRate.Value); } float delta = curHeading - desiredHeading; delta = MathHelper.WrapAngle(delta); if (delta > 0.0f) { rightWeight = Math.Min(delta / MathHelper.PiOver2, 1.0f); } else { leftWeight = Math.Min(-delta / MathHelper.PiOver2, 1.0f); } } // Bias weights toward either full or off with a 10% flat area. forwardWeight = MyMath.SmoothStep(0.1f, 0.9f, forwardWeight); rightWeight = MyMath.SmoothStep(0.1f, 0.9f, rightWeight); leftWeight = MyMath.SmoothStep(0.1f, 0.9f, leftWeight); // Adjust weights to sum to 1.0. float total = forwardWeight + rightWeight + leftWeight; if (total > 1.0f) { forwardWeight /= total; leftWeight /= total; rightWeight /= total; } else { // Fill in with idle. idleWeight = 1.0f - total; // If there's any idle, see how much of it should be wind. if (idleWeight > 0.0f) { // For this chassis we assume that the backwards animation represents wind. float wind = Fx.ShaderGlobals.WindAt(movement.Position); if (wind > idleWeight) { backwardWeight = idleWeight; idleWeight = 0.0f; } else { backwardWeight = wind; idleWeight = idleWeight - wind; } } } // Set resulting weights on animation set. anims.IdleWeight = idleWeight; anims.ForwardWeight = forwardWeight; anims.BackwardsWeight = backwardWeight; anims.RightWeight = rightWeight; anims.LeftWeight = leftWeight; } // end of SetLoopedAnimationWeights()
public override void PreCollisionTestUpdate(GameThing thing) { Movement movement = thing.Movement; DesiredMovement desiredMovement = thing.DesiredMovement; GameActor.State state = thing.CurrentState; //string DebugString = "------------- Debug Fish: "+ thing.CreatableName +" --------------"; float secs = Time.GameTimeFrameSeconds; // Only do movement if we are not being held and active. if (state == GameActor.State.Active && thing.ActorHoldingThis == null) { // Test for grounding. float terrainAltitude = Terrain.GetHeight(movement.Position); float waterBaseAltitude = Terrain.GetWaterBase(movement.Position); Vector3 waterNormal = Vector3.UnitZ; float waterAltitude = (waterBaseAltitude > 0) ? Terrain.GetWaterHeightAndNormal(movement.Position, ref waterNormal) : 0.0f; // Note that grounded tells us if there's too little water to float. // grounded should be false if there's no terrain under the bot. float groundedAmount = terrainAltitude - waterAltitude - (ReScale * HullDraft); bool grounded = terrainAltitude != 0 && (waterAltitude == 0.0f || groundedAmount > 0.0f); Vector3 groundedNormal = grounded ? Terrain.GetNormal(movement.Position) : Vector3.UnitZ; // Undo wave offset. movement.Position -= new Vector3(0, 0, waveOffset); if (!grounded) { // Apply DesiredMovement values to current movement. ApplyDesiredMovement(movement, desiredMovement); bodyFlex = movement.RotationZRate / TurningSpeedModifier * 0.5f; float newFlexAmp = MaxRotationRate - Math.Abs(movement.RotationZRate / TurningSpeedModifier); flexAmplitude = MyMath.Lerp(flexAmplitude, newFlexAmp * newFlexAmp, secs); // Wiggle less when turning. // Apply drag to velocity. ApplyFriction(movement, desiredMovement, applyVertical: true); } // end if not grounded. // Now see if we're stuck or hitting shore. if (grounded && !(jumping || landing)) { Vector3 terrainNormal = groundedNormal; // Grounded tells us there's too little water to float, now check if we're // actually touching the ground. float closeToGround = 0.1f; if (movement.Position.Z - hullDraft - terrainAltitude < closeToGround) { // Slide if shallow. if (groundedAmount < 1.0f) { // Slide down hill a bit. terrainNormal.Z = 0.0f; movement.Position += 10.0f * terrainNormal * secs; movement.Velocity = 10.0f * terrainNormal * secs; } else { // High and dry movement.Velocity = Vector3.Zero; } } else { // Decay any horizontal velocity. float friction = GameActor.FrictionDecay(Friction, secs); movement.Velocity *= new Vector3(friction, friction, 1); } } // Move. if (state != GameThing.State.Paused) { // Apply external force. movement.Velocity += desiredMovement.ExternalForce.GetValueOrDefault() / thing.Mass * secs; movement.Position += movement.Velocity * secs; } // Adjust pitch based on vertical movement. float deltaVZ = movement.Altitude - movement.PrevPosition.Z; if (deltaVZ > 0.01f) { pitch = MyMath.Lerp(pitch, MaxPitch, secs); } else if (deltaVZ < -0.01f) { pitch = MyMath.Lerp(pitch, -MaxPitch, secs); } else { pitch = MyMath.Lerp(pitch, 0, secs); } // At the new position, // push bot down due to being above the surface. // push bot up to keep out of mud. // calc waveOffset // calc waveEffect based on how close to surface we are combined with how grounded we are. waterBaseAltitude = Terrain.GetWaterBase(movement.Position); terrainAltitude = Terrain.GetHeight(movement.Position); waterAltitude = (waterBaseAltitude > 0) ? Terrain.GetWaterHeightAndNormal(movement.Position, ref waterNormal) : 0.0f; // Keep bot out of mud. // Calc how deep we are and push us back up. // Need to zero out velocity.Z when we do this. float inMud = (movement.Position.Z + waveOffset - ReScale * HullDraft) - terrainAltitude; if (inMud < 0.0f && terrainAltitude > 0) { movement.Position -= new Vector3(0, 0, inMud); movement.Velocity *= new Vector3(1, 1, 0); } else { // Push bot down if too high at surface (or even in the air). // Only apply this if not inMud. if (movement.Position.Z > waterAltitude - MinDepth || terrainAltitude == 0.0f) { movement.Velocity += new Vector3(0, 0, Gravity * secs); } } // Calc wave effect. We only want the waves to have an effect when the bot is near // the surface. If grounded, then we want the bot to have a chance to slide back // into the water. If just near the surface, the bot should move with the waves. float waveEffect = 0.0f; if (grounded) { waveEffect = 1.0f - groundedAmount; } else { // Make the waveEffect strongest at the surface and fading to 0 as you go deeper. float maxWaveEffectDepth = 3.0f; float depth = waterBaseAltitude - movement.Position.Z; if (depth > maxWaveEffectDepth) { waveEffect = 0.0f; } else { waveEffect = (maxWaveEffectDepth - depth) / maxWaveEffectDepth; } } waveEffect = MathHelper.Clamp(waveEffect, 0.0f, 1.0f); waveOffset = waveEffect * (waterAltitude - waterBaseAltitude); if (grounded) { // Blend between ground normal and wave normal. This allows some minimal // rocking until we're fully grounded. groundedNormal = MyMath.Lerp(groundedNormal, waterNormal, waveEffect); botNormal = MyMath.Lerp(botNormal, groundedNormal, secs); } else { // Blend between vertical and wave normal. This allows the waves to // affect the bot more when it's close to the surface. waterNormal = MyMath.Lerp(Vector3.UnitZ, waterNormal, waveEffect); botNormal = MyMath.Lerp(botNormal, waterNormal, secs); CheckRipples(thing, waveEffect); } // If in edit mode kill any velocity. if (state == GameActor.State.Paused) { movement.Velocity = Vector3.Zero; } // Create a matrix orienting the actor with the surface it's on. botNormal.Normalize(); Vector3 forward = new Vector3(1.0f, 0.0f, 0.0f); Vector3 left = Vector3.Cross(botNormal, forward); forward = Vector3.Cross(left, botNormal); Matrix local = Matrix.Identity; // Ok, this looks strange. This is because XNA GS assumes Y is up and -Z is forward. Argh. local.Backward = botNormal; local.Up = left; local.Right = forward; // Add in Z rotation and pitch. local = Matrix.CreateRotationY(-pitch) * Matrix.CreateRotationZ(movement.RotationZ) * local; // And translate to the right place. local.Translation = movement.Position; // Add in wave offset. local.M43 = movement.Position.Z + waveOffset; // Finally, set the local matrix explicitly movement.SetLocalMatrixAndRotation(local, movement.RotationZ); } //end of If State == Active && State != Held //Debug.WriteLine( DebugString ); } // end of PreCollisionTestUpdate()