예제 #1
0
		/// <summary>
		/// gets a list of all the tiles intersecting bounds. The returned list is ordered for collision detection based on the
		/// direction passed in so they can be processed in order.
		/// </summary>
		/// <returns>The colliding tiles.</returns>
		/// <param name="bounds">Bounds.</param>
		/// <param name="direction">Direction.</param>
		void populateCollidingTiles( Rectangle bounds, Edge direction )
		{
			_collidingTiles.Clear();
			var isHorizontal = direction.isHorizontal();
			var primaryAxis = isHorizontal ? Axis.X : Axis.Y;
			var oppositeAxis = primaryAxis == Axis.X ? Axis.Y : Axis.X;

			var oppositeDirection = direction.oppositeEdge();
			var firstPrimary = worldToTilePosition( bounds.getSide( oppositeDirection ), primaryAxis );
			var lastPrimary = worldToTilePosition( bounds.getSide( direction ), primaryAxis );
			var primaryIncr = direction.isMax() ? 1 : -1;

			var min = worldToTilePosition( isHorizontal ? bounds.Top : bounds.Left, oppositeAxis );
			var mid = worldToTilePosition( isHorizontal ? bounds.getCenter().Y : bounds.getCenter().X, oppositeAxis );
			var max = worldToTilePosition( isHorizontal ? bounds.Bottom : bounds.Right, oppositeAxis );

			var isPositive = mid - min < max - mid;
			var secondaryIncr = isPositive ? 1 : -1;
			var firstSecondary = isPositive ? min : max;
			var lastSecondary = !isPositive ? min : max;

			for( var primary = firstPrimary; primary != lastPrimary + primaryIncr; primary += primaryIncr )
			{
				for( var secondary = firstSecondary; secondary != lastSecondary + secondaryIncr; secondary += secondaryIncr )
				{
					var col = isHorizontal ? primary : secondary;
					var row = !isHorizontal ? primary : secondary;
					_collidingTiles.Add( collisionLayer.getTile( col, row ) );

					#if DEBUG_MOVER
					if( direction.isHorizontal() )
					{
						var pos = tiledMap.tileToWorldPosition( new Point( col, row ) );
						_debugTiles.Add( new Rectangle( (int)pos.X, (int)pos.Y, 16, 16 ) );
					}
					#endif
				}
			}
		}
예제 #2
0
		bool testMapCollision( Rectangle collisionRect, Edge direction, CollisionState collisionState, out int collisionResponse )
		{
			collisionResponse = 0;
			var side = direction.oppositeEdge();
			var perpindicularPosition = side.isVertical() ? collisionRect.Center.X : collisionRect.Center.Y;
			var leadingPosition = collisionRect.getSide( direction );
			var shouldTestSlopes = side.isVertical();
			populateCollidingTiles( collisionRect, direction );

			for( var i = 0; i < _collidingTiles.Count; i++ )
			{
				if( _collidingTiles[i] == null )
					continue;

				// disregard horizontal collisions if the last tile we were grounded on was a slope. Our y movement will push us up on the slope.
				// this is not a fantastic solution
				if( direction.isHorizontal() && collisionState._lastGroundTile != null && collisionState._lastGroundTile.isSlope() && _collidingTiles[i].isSlope() )
					return false;
				
				if( testTileCollision( _collidingTiles[i], side, perpindicularPosition, leadingPosition, shouldTestSlopes, out collisionResponse ) )
				{
					// store off our last ground tile if we collided below
					if( direction == Edge.Bottom )
					{
						collisionState._lastGroundTile = _collidingTiles[i];
						collisionState.isGroundedOnOneWayPlatform = collisionState._lastGroundTile.isOneWayPlatform();
					}
					
					return true;
				}

				// special case for sloped ground tiles
				if( collisionState._lastGroundTile != null && direction == Edge.Bottom )
				{
					// if grounded on a slope and intersecting a slope or if grounded on a wall and intersecting a tall slope we go sticky.
					// tall slope here means one where the the slopeTopLeft/Right is 0, i.e. it connects to a wall
					var isHighSlopeNearest = _collidingTiles[i].getNearestEdge( perpindicularPosition ) == _collidingTiles[i].getHighestSlopeEdge();
					if( ( collisionState._lastGroundTile.isSlope() && _collidingTiles[i].isSlope() ) || ( !collisionState._lastGroundTile.isSlope() && isHighSlopeNearest ) )
					{
						// store off our last ground tile if we collided below
						collisionState._lastGroundTile = _collidingTiles[i];
						return true;
					}
				}
			}

			return false;
		}
예제 #3
0
        bool testMapCollision( Rectangle collisionRect, Edge direction, out int collisionResponse )
        {
            collisionResponse = 0;
            var side = direction.oppositeEdge();
            var perpindicularPosition = side.isVertical() ? collisionRect.Center.X : collisionRect.Center.Y;
            var leadingPosition = collisionRect.getSide( direction );
            var shouldTestSlopes = side.isVertical();
            populateCollidingTiles( collisionRect, direction );

            for( var i = 0; i < _collidingTiles.Count; i++ )
            {
                if( _collidingTiles[i] == null )
                    continue;

                // TODO: is this necessary? seems to work without it
                // disregard horizontal collisions  if the last tile we were grounded on was a slope
                // this is not a fantastic solution
                if( direction.isHorizontal() && _lastGroundTile != null && _lastGroundTile.isSlope() )
                    continue;

                if( testTileCollision( _collidingTiles[i], side, perpindicularPosition, leadingPosition, shouldTestSlopes, out collisionResponse ) )
                {
                    // store off our last ground tile if we collided below
                    if( direction == Edge.Bottom )
                        _lastGroundTile = _collidingTiles[i];

                    return true;
                }

                // special case for sloped ground tiles
                if( _lastGroundTile != null && direction == Edge.Bottom )
                {
                    // if grounded on a slope and intersecting a slope or if grounded on a wall and intersecting a tall slope we go sticky
                    // tall slope here means one where the the slopeTopLeft/Right is 0, i.e. it connects to a wall
                    if( ( _lastGroundTile.isSlope() && _collidingTiles[i].isSlope() ) || ( !_lastGroundTile.isSlope() && _collidingTiles[i].isSlope() /* should be tall slope check */ ) )
                    {
                        // store off our last ground tile if we collided below
                        _lastGroundTile = _collidingTiles[i];

                        return true;
                    }
                }
            }

            return false;
        }
예제 #4
0
		public void testCollisions( ref Vector2 motion, Rectangle boxColliderBounds, CollisionState collisionState )
		{
			_boxColliderBounds = boxColliderBounds;

			// save off our current grounded state which we will use for wasGroundedLastFrame and becameGroundedThisFrame
			collisionState.wasGroundedLastFrame = collisionState.below;

			// reset our collisions state
			collisionState.reset( ref motion );

			// reset rounded motion for us while dealing with subpixel movement so fetch the rounded values to use for our actual detection
			var motionX = (int)motion.X;
			var motionY = (int)motion.Y;

			// first, check movement in the horizontal dir
			if( motionX != 0 )
			{
				var direction = motionX > 0 ? Edge.Right : Edge.Left;
				var sweptBounds = collisionRectForSide( direction, motionX );

				int collisionResponse;
				if( testMapCollision( sweptBounds, direction, collisionState, out collisionResponse ) )
				{
					// react to collision. get the distance between our leading edge and what we collided with
					motion.X = collisionResponse - boxColliderBounds.getSide( direction );
					collisionState.left = direction == Edge.Left;
					collisionState.right = direction == Edge.Right;
					collisionState._movementRemainderX.reset();
				}
				else
				{
					collisionState.left = false;
					collisionState.right = false;
				}
			}

			// next, check movement in the vertical dir
			{
				var direction = motionY >= 0 ? Edge.Bottom : Edge.Top;
				var sweptBounds = collisionRectForSide( direction, motionY );
				sweptBounds.X += (int)motion.X;

				int collisionResponse;
				if( testMapCollision( sweptBounds, direction, collisionState, out collisionResponse ) )
				{
					// react to collision. get the distance between our leading edge and what we collided with
					motion.Y = collisionResponse - boxColliderBounds.getSide( direction );
					collisionState.above = direction == Edge.Top;
					collisionState.below = direction == Edge.Bottom;
					collisionState._movementRemainderY.reset();

					if( collisionState.below && collisionState._lastGroundTile != null && collisionState._lastGroundTile.isSlope() )
						collisionState.slopeAngle = MathHelper.ToDegrees( (float)Math.Atan( collisionState._lastGroundTile.getSlope() ) );
				}
				else
				{
					collisionState.above = false;
					collisionState.below = false;
					collisionState._lastGroundTile = null;
				}


				// when moving down we also check for collisions in the opposite direction. this needs to be done so that ledge bumps work when
				// a jump is made but misses by the colliderVerticalInset
				if( direction == Edge.Bottom )
				{
					direction = direction.oppositeEdge();
					sweptBounds = collisionRectForSide( direction, 0 );
					sweptBounds.X += (int)motion.X;
					sweptBounds.Y += (int)motion.Y;

					if( testMapCollision( sweptBounds, direction, collisionState, out collisionResponse ) )
					{
						// react to collision. get the distance between our leading edge and what we collided with
						motion.Y = collisionResponse - boxColliderBounds.getSide( direction );
						// if we collide here this is an overlap of a slope above us. this small bump down will prevent hitches when hitting
						// our head on a slope that connects to a solid tile. It puts us below the slope when the normal response would put us
						// above it
						motion.Y += 2;
						collisionState.above = true;
					}
				}
			}

			// set our becameGrounded state based on the previous and current collision state
			if( !collisionState.wasGroundedLastFrame && collisionState.below )
				collisionState.becameGroundedThisFrame = true;
		}
예제 #5
0
        /// <summary>
        /// gets a list of all the tiles intersecting bounds. The returned list is ordered for collision detection based on the
        /// direction passed in so they can be processed in order.
        /// </summary>
        /// <returns>The colliding tiles.</returns>
        /// <param name="bounds">Bounds.</param>
        /// <param name="direction">Direction.</param>
        void populateCollidingTiles( Rectangle bounds, Edge direction )
        {
            _collidingTiles.Clear();
            var isHorizontal = direction.isHorizontal();
            var primaryAxis = isHorizontal ? Axis.X : Axis.Y;
            var oppositeAxis = primaryAxis == Axis.X ? Axis.Y : Axis.X;

            // if we are going up or left we subtract 1 from our opposite edge so that it doesnt get the column/row of tiles when we are against them
            var oppositeDirection = direction.oppositeEdge();
            var minSidePad = direction.isMin() ? -1 : 0;
            var firstPrimary = worldToTilePosition( bounds.getSide( oppositeDirection ) + minSidePad, primaryAxis );
            var lastPrimary = worldToTilePosition( bounds.getSide( direction ), primaryAxis );
            var primaryIncr = direction.isMax() ? 1 : -1;

            var min = worldToTilePosition( isHorizontal ? bounds.Top : bounds.Left, oppositeAxis );
            var mid = worldToTilePosition( isHorizontal ? bounds.getCenter().Y : bounds.getCenter().X, oppositeAxis );
            // same as above here. we subtract 1 to not grab an extra column/row of tiles which will mess up collisions
            var max = worldToTilePosition( isHorizontal ? bounds.Bottom - 1 : bounds.Right - 1, oppositeAxis );

            var isPositive = mid - min < max - mid;
            var secondaryIncr = isPositive ? 1 : -1;
            var firstSecondary = isPositive ? min : max;
            var lastSecondary = !isPositive ? min : max;

            for( var primary = firstPrimary; primary != lastPrimary + primaryIncr; primary += primaryIncr )
            {
                for( var secondary = firstSecondary; secondary != lastSecondary + secondaryIncr; secondary += secondaryIncr )
                {
                    var col = isHorizontal ? primary : secondary;
                    var row = !isHorizontal ? primary : secondary;
                    _collidingTiles.Add( _collisionLayer.getTile( col, row ) );
                }
            }
        }