Exemple #1
0
        protected override void BounceOffGround(GameThing thing, float terrainHeight, Vector3 terraNormal)
        {
            Movement movement = thing.Movement;

            float secs = Time.GameTimeFrameSeconds;

            Vector3 position = movement.Position;
            Vector3 velocity = movement.Velocity;

            // Move the thing back above ground level, but only a bit at a time to keep it smooth.
            float deltaZ = terrainHeight + MinHeight - position.Z;

            //deltaZ *= secs;

            position.Z += deltaZ;   // Move position back to just contacting terrain.

            // Remove effect of gravity for ths frame.
            // This helps the jitters that occur when almost standing still.
            if (velocity.Z < 0.0f)
            {
                velocity.Z -= Gravity * secs;
            }

            // Translate any change in height to a change in velocity.
            // This causes things rolling downhill to speed up.
            // Note that this only affects the XY axes, not the vertical.
            float deltaV = (float)Math.Sqrt(Math.Abs(2.0f * Gravity * deltaZ));

            // Get direction apply new velocity.  The 0.5 factor is not "correct"
            // but the movement looks better with it in.
            Vector3 slopeNormal = Terrain.GetNormal(position);

            slopeNormal.Z = 0.0f;
            velocity     += 0.5f * deltaV * slopeNormal;

            float velDotNorm = Vector3.Dot(velocity, terraNormal);

            if (velDotNorm < 0)
            {
                // For tumbling objects ignore the friction value and just
                // use the CoR on all components of the velocity.  For back
                // compat we now set the friction for tumbling objects to
                // 0.0f.  This way, previously saved levels with different
                // settings will still act as they used to.
                if (Tumbles && Friction == 0.0f)
                {
                    // Dampen velocity.
                    // Use a higher CoR for XY so things roll better.
                    float xyCoR = CoefficientOfRestitution + (1 - CoefficientOfRestitution) * 0.8f;
                    velocity.X *= xyCoR;
                    velocity.Y *= xyCoR;
                    velocity.Z *= CoefficientOfRestitution;

                    // Bounce
                    velocity = Vector3.Reflect(velocity, terraNormal);
                }
                else
                {
                    Vector3 velNormal  = velDotNorm * terraNormal;
                    Vector3 velTangent = velocity - velNormal;

                    velNormal *= CoefficientOfRestitution;
                    // The default friction of 0.0 causes knocked out objects
                    // to slide endlessly across the ground.  So, bump up the
                    // friction so it looks reasonable.
                    velTangent *= GameActor.FrictionDecay(0.99f, secs);

                    // Really damp down squashed objects.
                    if (Parent.CurrentState == GameThing.State.Squashed)
                    {
                        velNormal  *= CoefficientOfRestitution;
                        velTangent *= GameActor.FrictionDecay(0.99f, secs);
                    }

                    velocity = velTangent - velNormal;
                }

                // Check if we've stopped moving.
                if (velocity.LengthSquared() < kSmallMove)
                {
                    if (stopMovingCount >= 12)
                    {
                        Moving = false;
                    }
                    else
                    {
                        ++stopMovingCount;
                    }
                }
                else
                {
                    stopMovingCount = 0;
                }

                // Trigger a dust emitter puff.
                if (!inWater && !thing.Invisible)
                {
                    MakeDustPuff(position, terrainHeight, velocity.Length());
                }

                // Apply changes to movement object.
                movement.Velocity = velocity;
                movement.Position = position;
            }
        }
        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()