Exemplo n.º 1
0
        //=====================================================================================
        /// <summary>
        /// Collision query function. If an object is collideable it should implement it's collision 
        /// testing code here and return the result of the collision. This collision test is for 
        /// a bounding elipse against a piece of geometry, such as a collection of lines etc. 
        /// </summary>
        /// <param name="elipsePosition">   Position of the bounding elipse                 </param>
        /// <param name="elipseDimensions"> Dimensions of the bounding elipse               </param>
        /// <param name="elipseRotation">   Amount the elipse is rotated by in radians.     </param>
        /// <param name="otherObject">      Object making the collision query, may be null. </param>
        /// <param name="results">          Array to save the results to.                   </param>
        /// <param name="results_index">    Index in the array to save the results to.      </param>
        /// <returns>                       Number of results from the collision.           </returns>
        //=====================================================================================
        public override int OnCollisionQuery( 
            Vector2                 elipsePosition      ,
            Vector2                 elipseDimensions    ,
            float                   elipseRotation      ,
            GameObject              otherObject         ,
            CollisionQueryResult[]  results             ,
            int                     results_index
        )
        {
            // Get height of the top texture (if any). the ground plane is at the bottom of this.

            int top_tex_height = 0;

            if ( m_top_texture != null ) top_tex_height = m_top_texture.Height;

            // Take two points along the ground surface:

            Vector2 ground_p1 = new Vector2( elipsePosition.X - 10 , PositionY + BoxDimensionsY - top_tex_height );
            Vector2 ground_p2 = new Vector2( elipsePosition.X + 10 , PositionY + BoxDimensionsY - top_tex_height );

            // Transform into the correct form so that the ellipse is a circle, it's rotations are undone and it's center is the center of the world

            ground_p1 = Vector2.Transform( ground_p1 , LevelCollisionQuery.CollisionCache.ToEllipseLocal );
            ground_p2 = Vector2.Transform( ground_p2 , LevelCollisionQuery.CollisionCache.ToEllipseLocal );

            /* OLD UNOPTIMIZED COLLISION CODE: WHICH IS MANUALLY DOING THESE TRANSFORMS

                // Get both relative to the center of the elipse:

                ground_p1 = ground_p1 - elipsePosition;
                ground_p2 = ground_p2 - elipsePosition;

                // Rotate them according to the elipse rotation: we are essentially rotating the world so the elipse is not rotated anymore

                Matrix rot = Matrix.CreateRotationZ( - elipseRotation );

                ground_p1 = Vector2.Transform( ground_p1 , rot );
                ground_p2 = Vector2.Transform( ground_p2 , rot );

                // Scale the worlds y coordinates so that the elipse becomes a circle:

                float world_y_scale = elipseDimensions.X / elipseDimensions.Y;

                ground_p1.Y *= world_y_scale;
                ground_p2.Y *= world_y_scale;

             @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ */

            // Get the line normal:

            Vector2 ground_normal = new Vector2
            (
                - ( ground_p2.Y - ground_p1.Y  ) ,
                + ( ground_p2.X - ground_p1.X  )
            );

            ground_normal.Normalize();

            // Get distance to the ground:

            float distance = - Vector2.Dot( ground_p1 , ground_normal );

            // See if we are within range for a collision:

            if ( distance < elipseDimensions.X )
            {
                // Calculate penetration amount:

                float penetration = elipseDimensions.X - distance;

                // Calculate the collision point relative to the elipse center:

                Vector2 collision_point = - distance * ground_normal;

                // Calculate how much the elipse must move by to avoid collision:

                Vector2 resolve_vec = penetration * ground_normal;

                // Transform the collision point and resolve direction back into normal coordinates

                {
                    Vector4 v = Vector4.Zero;

                    v.X = resolve_vec.X;
                    v.Y = resolve_vec.Y;

                    v = Vector4.Transform( v , LevelCollisionQuery.CollisionCache.FromEllipseLocal );

                    resolve_vec.X = v.X;
                    resolve_vec.Y = v.Y;
                }

                collision_point = Vector2.Transform
                (
                    collision_point ,
                    LevelCollisionQuery.CollisionCache.FromEllipseLocal
                );

                /* OLD UNOPTIMIZED COLLISION CODE: WHICH IS MANUALLY DOING THESE TRANSFORMS

                // Now undo the previous scaling:

                resolve_vec.Y       /= world_y_scale;
                collision_point.Y   /= world_y_scale;

                // Undo the previous rotation:

                Matrix rot_inverse = Matrix.CreateRotationZ(elipseRotation);

                    resolve_vec     = Vector2.Transform(resolve_vec,rot_inverse);
                    collision_point = Vector2.Transform(collision_point,rot_inverse);

                // Make collision point back to world coordinates

                collision_point += elipsePosition;

                @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ */

                // Get penetration amount

                penetration = resolve_vec.Length();

                // If the penetration is very small then abort:

                if ( penetration < 0.001f ) return 0;

                // Normalise resolve direction:

                resolve_vec /= penetration;

                // Save the result:

                if ( results_index < results.Length )
                {
                    // Make up the result:

                    CollisionQueryResult c;

                    c.ValidResult       = true;
                    c.QueryObject       = this;
                    c.Point             = collision_point;
                    c.Penetration       = penetration;
                    c.ResolveDirection  = resolve_vec;
                    c.Normal            = Vector2.UnitY;
                    c.IsPointCollision  = false;

                    // Save the result

                    results[results_index] = c;

                    // Got a valid result:

                    return 1;
                }
            }

            // If we got to here there was no result

            return 0;
        }
Exemplo n.º 2
0
        //=========================================================================================
        /// <summary>
        /// Does rumbles for when the player jumps and hits a surface, and also for when the player 
        /// is hit.
        /// </summary>
        //=========================================================================================
        private void UpdateRumbles()
        {
            // Make sure the gampad is connected:

            if ( GamePad.GetState(PlayerIndex.One).IsConnected == false ) return;

            // If the player is dead then set impact rumbles to off:

            if ( Health <= 0 )
            {
                GamePad.SetVibration( PlayerIndex.One , 0 , 0 ); return;
            }

            // Figure out the new amount of rumble due to velocity change:

                // Get our current total velocity:

                Vector2 total_velocity = Velocity + MoveVelocity;

                // See if we have left or just hit a surface:

                if ( FlattestContactSurface.ValidResult != m_last_contact_surface.ValidResult )
                {
                    // Jumped or left a surface: get the difference in velocity between the last frame

                    Vector2 velocity_difference = total_velocity - m_last_total_velocity;

                    // Add to the current impact rumbling by this amount:

                    m_current_impact_rumble += velocity_difference.Length() * IMPACT_RUMBLE_SCALE;
                }

                // Slow down impact rumbling

                m_current_impact_rumble *= IMPACT_RUMBLE_SLOWDOWN;

                // Do impact rumble dead zoning:

                if ( m_current_impact_rumble < IMPACT_RUMBLE_DEAD_ZONE ) m_current_impact_rumble = 0;

                // Save the current contact surface as the last:

                m_last_contact_surface = FlattestContactSurface;

                // Save the current total velocity as the last:

                m_last_total_velocity = total_velocity;

            // Figure out the amount of rumble due to pain:

                // Get the difference in health from the last frame:

                float health_difference = Health - m_last_health;

                // Negate and if negative then zero since that means we got health rather than lose it

                health_difference *= -1;

                if ( health_difference < 0 ){ health_difference = 0; }

                // Increase pain rumbling due to this change:

                m_current_pain_rumble += health_difference * PAIN_RUMBLE_SCALE;

                // Slow down pain rumbling

                m_current_pain_rumble *= PAIN_RUMBLE_SLOWDOWN;

                // Do pain rumble dead zoning:

                if ( m_current_pain_rumble < PAIN_RUMBLE_DEAD_ZONE ) m_current_pain_rumble = 0;

                // Save current health as the last:

                m_last_health = Health;

            // Set the rumble on the gamepad:

            GamePad.SetVibration(PlayerIndex.One,m_current_pain_rumble,m_current_impact_rumble);
        }
Exemplo n.º 3
0
        //=========================================================================================
        /// <summary>
        /// Orients the character according to the surfaces they are in contact with. 
        /// This will cause the characters to run slightly up walls and orient to underlying terrain.
        /// Also determines a surface to jump off while its at it.
        /// </summary>
        //=========================================================================================
        private void OrientToSurface()
        {
            // Clear the jump surface:

            m_jump_surface.ValidResult = false;

            // Makeup a new bounding elipse for the character and expand it slightly:

            Vector2 expanded_elipse_dimensions = EllipseDimensions;

            expanded_elipse_dimensions.X += SURFACE_ORIENT_DISTANCE;
            expanded_elipse_dimensions.Y += SURFACE_ORIENT_DISTANCE;

            // Shorter:

            LevelCollisionQuery c = Core.Level.Collision;

            // Do collision detection with the level:

            c.Collide( Position , expanded_elipse_dimensions , m_surface_rotation , this );

            // If the character is not in contact with any surfaces then straighten it's orientation

            if ( c.CollisionResultCount <= 0 )
            {
                // See which way the surface angle of the character is:

                if ( m_surface_rotation < 0 )
                {
                    // Straigten up:

                    m_surface_rotation += AIR_ORIENT_STRAIGHTEN;

                    // Don't over do it:

                    if ( m_surface_rotation > 0 ) m_surface_rotation = 0;
                }
                else
                {
                    // Straigten up:

                    m_surface_rotation -= AIR_ORIENT_STRAIGHTEN;

                    // Don't over do it:

                    if ( m_surface_rotation < 0 ) m_surface_rotation = 0;
                }

                // Bail out after this: surface orientation is done

                return;
            }

            /******* OLD WAY OF DOING THINGS ***********************************************************************************************

            // Make a weighted average resolve direction for all the things collided with:

            Vector2 average_resolve_dir = Vector2.Zero;

            for ( int i = 0 ; i < c.CollisionResultCount ; i++ )
            {
                // Ignore this surface if it is almost or is a ceiling:

                if ( c.CollisionResults[i].Normal.Y < MIN_ORIENT_SURFACE_NORMAL_Y ) continue;

                // - old way -
                //
                // Get the speed of the character in relation to the surface normal:
                //
                // float dot = Math.Abs( Vector2.Dot( - m_velocity - m_move_velocity , c.CollisionResults[i].ResolveDirection ) );
                //

                // Get the distance to the collision point:

                float d = Vector2.Distance( Position , c.CollisionResults[i].Point );

                // Save this surface also as the jump surface if there is none or if it is flatter than the current:

                if ( m_jump_surface.ValidResult == false || m_jump_surface.Normal.Y < c.CollisionResults[i].Normal.Y )
                {
                    // New jump surface: save

                    m_jump_surface = c.CollisionResults[i];
                }

                // Add to the results:

                // - old way -
                //
                // average_resolve_dir += c.CollisionResults[i].ResolveDirection * dot;
                //

                average_resolve_dir += c.CollisionResults[i].ResolveDirection;
            }

            // If the resolve dir is still zero then bail:

            if ( average_resolve_dir.Length() == 0 ) return;

            // Otherwise normalise:

            average_resolve_dir.Normalize();

            // Turn it into a surface direction:

            Vector2 surf_dir = new Vector2
            (
                + ( average_resolve_dir.Y ) ,
                - ( average_resolve_dir.X )
            );

            // Get an up vector for the character rotated by the current surface rotation:

            Vector2 char_up_vec = Vector2.UnitY;

            // Make a rotation matrix:

            Matrix rot = Matrix.CreateRotationZ(m_surface_rotation);

            // Do the rotation:

            char_up_vec = Vector2.Transform( char_up_vec , rot );

            //'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
            // Get the cosine of the angle between the average resolve direction and rotated character
            // up vector: should be zero when character is perfectly oriented with the surface
            //'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

            float cos = Vector2.Dot( surf_dir , char_up_vec );

            // Get this cosine as an angle: but use sine function instead to get angular diference from the two vectors being perpendicular

            float angle = (float) Math.Asin(cos);

            // Now rotate the player towards being perpendicular with the underlying suface

            {
                // Save this variable temporarily:

                float t = m_surface_rotation;

                // Orient towards new orientation:

                m_surface_rotation *= SURFACE_ORIENT_SPEED; m_surface_rotation += ( t + angle ) * ( 1.0f - SURFACE_ORIENT_SPEED );
            }

            *****************************************************************************************************************************/

            // Store the average angle difference between the player and each surface here:

            float average_angle_difference = 0;

            // Find the average angle difference:

            for ( int i = 0 ; i < c.CollisionResultCount ; i++ )
            {
                // Ignore this surface if the resolve dir is not right

                if ( c.CollisionResults[i].ResolveDirection.Y < MIN_ORIENT_SURFACE_NORMAL_Y ) continue;

                // Save this surface also as the jump surface if there is none or if it is flatter than the current:

                if ( m_jump_surface.ValidResult == false || m_jump_surface.Normal.Y < c.CollisionResults[i].Normal.Y )
                {
                    // New jump surface: save

                    m_jump_surface = c.CollisionResults[i];
                }

                // Get the angle of this surfaces normal:

                float angle = (float) Math.Acos( c.CollisionResults[i].ResolveDirection.X );

                // Make so that up is angle zero:

                angle -= MathHelper.PiOver2;

                // Add to the average angle difference:

                average_angle_difference += angle - m_surface_rotation;
            }

            // Find the average angle difference:

            average_angle_difference /= c.CollisionResultCount;

            // Need this temporary:

            float t = m_surface_rotation;

            // Interpolate towards our new orientation:

            m_surface_rotation *= SURFACE_ORIENT_SPEED; m_surface_rotation += ( t + average_angle_difference ) * ( 1.0f - SURFACE_ORIENT_SPEED );
        }
Exemplo n.º 4
0
        //=====================================================================================
        /// <summary>
        /// Collision query function. If an object is collideable it should implement it's collision 
        /// testing code here and return the result of the collision. This collision test is for 
        /// a bounding elipse against a piece of geometry, such as a collection of lines etc. 
        /// </summary>
        /// <param name="elipsePosition">   Position of the bounding elipse                 </param>
        /// <param name="elipseDimensions"> Dimensions of the bounding elipse               </param>
        /// <param name="elipseRotation">   Amount the elipse is rotated by in radians.     </param>
        /// <param name="otherObject">      Object making the collision query, may be null. </param>
        /// <param name="results">          Array to save the results to.                   </param>
        /// <param name="results_index">    Index in the array to save the results to.      </param>
        /// <returns>                       Number of results from the collision.           </returns>
        //=====================================================================================
        public override int OnCollisionQuery( 
            Vector2                 elipsePosition      ,
            Vector2                 elipseDimensions    ,
            float                   elipseRotation      ,
            GameObject              otherObject         ,
            CollisionQueryResult[]  results             ,
            int                     results_index
        )
        {
            // If the index is past the end of the results array then do nothing:

            if ( results_index >= results.Length ) return 0;

            // Check out the collision cache and see if the ellipse is within bounds of our lines:

            if ( LevelCollisionQuery.CollisionCache.EllipseBoxTopLeft.X         > m_lines_bb_bottom_right.X ) return 0;
            if ( LevelCollisionQuery.CollisionCache.EllipseBoxBottomRight.X     < m_lines_bb_top_left.X     ) return 0;
            if ( LevelCollisionQuery.CollisionCache.EllipseBoxTopLeft.Y         < m_lines_bb_bottom_right.Y ) return 0;
            if ( LevelCollisionQuery.CollisionCache.EllipseBoxBottomRight.Y     > m_lines_bb_top_left.Y     ) return 0;

            // Store the number of results here:

            int num_results = 0;

            // Run through all the lines looking for a collision:

            for ( int i = 0 ; i < m_collision_lines.Length ; i++ )
            {
                // Store collision results here:

                Vector2 collide_point       = Vector2.Zero;
                Vector2 resolve_dir         = Vector2.Zero;
                Vector2 collide_normal      = Vector2.Zero;
                float   penetration         = 0;
                bool    point_collision     = false;

                // Test against this line:

                bool collision = m_collision_lines[i].FastCollide
                (
                    elipseDimensions.X                  ,
                    ref collide_point                   ,
                    ref resolve_dir                     ,
                    ref collide_normal                  ,
                    ref penetration                     ,
                    ref point_collision
                );

                // See if there was a collision

                if ( collision )
                {
                    // Increment number of results:

                    num_results++;

                    // Makeup the result:

                    CollisionQueryResult c;

                    c.ValidResult       = true;
                    c.QueryObject       = this;
                    c.Point             = collide_point;
                    c.Normal            = collide_normal;
                    c.Penetration       = penetration;
                    c.ResolveDirection  = resolve_dir;
                    c.IsPointCollision  = point_collision;

                    // Save the result:

                    results[results_index] = c;

                    // Increment results index:

                    results_index++;

                    // If past the end then return number of results:

                    if ( results_index >= results.Length ) return num_results;
                }

            }

            // Return the number of collision results

            return num_results;
        }
Exemplo n.º 5
0
        //=========================================================================================
        /// <summary>
        /// Checks to see if the character is on the ground or touching a surface such as a wall. 
        /// This fucntion saves both the flatest and steepest surfaces.
        /// </summary>
        //=========================================================================================
        private void FindContactSurfaces()
        {
            // Makeup a new bounding elipse for the character and expand it slightly:

            Vector2 expanded_elipse_dimensions = EllipseDimensions;

            expanded_elipse_dimensions.X += CONTACT_SURFACE_DISTANCE;
            expanded_elipse_dimensions.Y += CONTACT_SURFACE_DISTANCE;

            // Shorten things:

            LevelCollisionQuery c = Core.Level.Collision;

            // Do collision detection with the level:

            c.Collide( Position , expanded_elipse_dimensions , m_surface_rotation , this );

            // See if there is any results:

            if ( c.CollisionResultCount > 0 )
            {
                // Got results: save the result with the flatest surface. Use the normal to determine this.

                int flattest_s_index = -1;

                for ( int i = 0 ; i < c.CollisionResultCount ; i++ )
                {
                    // See if this surface is flatter

                    if ( flattest_s_index == -1 || c.CollisionResults[i].Normal.Y > c.CollisionResults[flattest_s_index].Normal.Y )
                    {
                        // Only do if we are travelling towards the surface:

                        if ( Vector2.Dot( m_velocity + m_move_velocity , c.CollisionResults[i].Normal ) <= 0.001f )
                        {
                            // Flatter: save it's index

                            flattest_s_index = i;
                        }
                    }
                }

                // Save the flattest contact surface found:

                if ( flattest_s_index >= 0 )
                {
                    m_flattest_contact_surface = Core.Level.Collision.CollisionResults[flattest_s_index];
                }
                else
                {
                    m_flattest_contact_surface = CollisionQueryResult.NoResult;
                }

                // Now find the steepest surface: again use the normal

                int steepest_s_index = -1;

                for ( int i = 0 ; i < Core.Level.Collision.CollisionResultCount ; i++ )
                {
                    // See if this surface is steeper

                    if ( steepest_s_index == -1 || c.CollisionResults[i].Normal.Y < c.CollisionResults[steepest_s_index].Normal.Y )
                    {
                        // Only do if we are travelling towards the surface:

                        if ( Vector2.Dot( m_velocity + m_move_velocity , c.CollisionResults[i].Normal ) <= 0.001f )
                        {
                            // Steeper: save it's index

                            steepest_s_index = i;
                        }
                    }
                }

                // Save the steepest surface found:

                if ( steepest_s_index >= 0 )
                {
                    m_steepest_contact_surface = Core.Level.Collision.CollisionResults[steepest_s_index];
                }
                else
                {
                    m_steepest_contact_surface = CollisionQueryResult.NoResult;
                }
            }
            else
            {
                // No collision results: not in contact with anything

                m_flattest_contact_surface  = CollisionQueryResult.NoResult;
                m_steepest_contact_surface  = CollisionQueryResult.NoResult;
            }
        }