/// <summary> /// Tests the tile for a collision. Returns via out the position in world space where the collision occured. /// </summary> /// <returns>The tile collision.</returns> /// <param name="tile">Tile.</param> /// <param name="edgeToTest">the opposite side of movement, the side the leading edge will collide with</param> /// <param name="perpindicularPosition">Perpindicular position.</param> /// <param name="leadingPosition">Leading position.</param> /// <param name="shouldTestSlopes">Should test slopes.</param> /// <param name="collisionResponse">Collision response.</param> bool testTileCollision(TiledTile tile, Edge edgeToTest, int perpindicularPosition, int leadingPosition, bool shouldTestSlopes, out int collisionResponse) { collisionResponse = leadingPosition; // one way platforms are only collideable from the top when the player is already above them if (tile.isOneWayPlatform()) { // only the top edge of one way platforms are checked for collisions if (edgeToTest != Edge.Top) { return(false); } // our response should be the top of the platform collisionResponse = tiledMap.tileToWorldPositionX(tile.y); return(_boxColliderBounds.Bottom <= collisionResponse); } var forceSlopedTileCheckAsWall = false; // when moving horizontally the only time a slope is considered for collision testing is when its closest side is the tallest side // and we were not intesecting the tile before moving. // this prevents clipping through a tile when hitting its edge: -> |\ if (edgeToTest.isHorizontal() && tile.isSlope() && tile.getNearestEdge(leadingPosition) == tile.getHighestSlopeEdge()) { var moveDir = edgeToTest.oppositeEdge(); var leadingPositionPreMovement = _boxColliderBounds.getSide(moveDir); // we need the tile x position that is on the opposite side of our move direction. Moving right we want the left edge var tileX = moveDir == Edge.Right ? tiledMap.tileToWorldPositionX(tile.x) : tiledMap.tileToWorldPositionX(tile.x + 1); // using the edge before movement, we see if we were colliding before moving. var wasCollidingBeforeMove = moveDir == Edge.Right ? leadingPositionPreMovement > tileX : leadingPositionPreMovement < tileX; // if we were not colliding before moving we need to consider this tile for a collision check as if it were a wall tile forceSlopedTileCheckAsWall = !wasCollidingBeforeMove; } if (forceSlopedTileCheckAsWall || !tile.isSlope()) { switch (edgeToTest) { case Edge.Top: collisionResponse = tiledMap.tileToWorldPositionY(tile.y); break; case Edge.Bottom: collisionResponse = tiledMap.tileToWorldPositionY(tile.y + 1); break; case Edge.Left: collisionResponse = tiledMap.tileToWorldPositionX(tile.x); break; case Edge.Right: collisionResponse = tiledMap.tileToWorldPositionX(tile.x + 1); break; } return(true); } if (shouldTestSlopes) { var tileWorldX = tiledMap.tileToWorldPositionX(tile.x); var tileWorldY = tiledMap.tileToWorldPositionX(tile.y); var slope = tile.getSlope(); var offset = tile.getSlopeOffset(); // calculate the point on the slope at perpindicularPosition collisionResponse = (int)(edgeToTest.isVertical() ? slope * (perpindicularPosition - tileWorldX) + offset + tileWorldY : (perpindicularPosition - tileWorldY - offset) / slope + tileWorldX); var isColliding = edgeToTest.isMax() ? leadingPosition <= collisionResponse : leadingPosition >= collisionResponse; // this code ensures that we dont consider collisions on a slope while jumping up that dont intersect our collider. // It also makes sure when testing the bottom edge that the leadingPosition is actually above the collisionResponse. // HACK: It isn't totally perfect but it does the job if (isColliding && edgeToTest == Edge.Bottom && leadingPosition <= collisionResponse) { isColliding = false; } return(isColliding); } return(false); }
/// <summary> /// Tests the tile for a collision. Returns via out the position in world space where the collision occured. /// </summary> /// <returns>The tile collision.</returns> /// <param name="tile">Tile.</param> /// <param name="edgeToTest">the opposite side of movement, the side the leading edge will collide with</param> /// <param name="perpindicularPosition">Perpindicular position.</param> /// <param name="leadingPosition">Leading position.</param> /// <param name="shouldTestSlopes">Should test slopes.</param> /// <param name="collisionResponse">Collision response.</param> bool testTileCollision( TiledTile tile, Edge edgeToTest, int perpindicularPosition, int leadingPosition, bool shouldTestSlopes, out int collisionResponse ) { collisionResponse = leadingPosition; // one way platforms are only collideable from the top when the player is already above them if( tile.isOneWayPlatform() ) { // only the top edge of one way platforms are checked for collisions if( edgeToTest != Edge.Top ) return false; // our response should be the top of the platform collisionResponse = tiledMap.tileToWorldPositionX( tile.y ); return _boxColliderBounds.Bottom <= collisionResponse; } var forceSlopedTileCheckAsWall = false; // when moving horizontally the only time a slope is considered for collision testing is when its closest side is the tallest side // and we were not intesecting the tile before moving. // this prevents clipping through a tile when hitting its edge: -> |\ if( edgeToTest.isHorizontal() && tile.isSlope() && tile.getNearestEdge( leadingPosition ) == tile.getHighestSlopeEdge() ) { var moveDir = edgeToTest.oppositeEdge(); var leadingPositionPreMovement = _boxColliderBounds.getSide( moveDir ); // we need the tile x position that is on the opposite side of our move direction. Moving right we want the left edge var tileX = moveDir == Edge.Right ? tiledMap.tileToWorldPositionX( tile.x ) : tiledMap.tileToWorldPositionX( tile.x + 1 ); // using the edge before movement, we see if we were colliding before moving. var wasCollidingBeforeMove = moveDir == Edge.Right ? leadingPositionPreMovement > tileX : leadingPositionPreMovement < tileX; // if we were not colliding before moving we need to consider this tile for a collision check as if it were a wall tile forceSlopedTileCheckAsWall = !wasCollidingBeforeMove; } if( forceSlopedTileCheckAsWall || !tile.isSlope() ) { switch( edgeToTest ) { case Edge.Top: collisionResponse = tiledMap.tileToWorldPositionY( tile.y ); break; case Edge.Bottom: collisionResponse = tiledMap.tileToWorldPositionY( tile.y + 1 ); break; case Edge.Left: collisionResponse = tiledMap.tileToWorldPositionX( tile.x ); break; case Edge.Right: collisionResponse = tiledMap.tileToWorldPositionX( tile.x + 1 ); break; } return true; } if( shouldTestSlopes ) { var tileWorldX = tiledMap.tileToWorldPositionX( tile.x ); var tileWorldY = tiledMap.tileToWorldPositionX( tile.y ); var slope = tile.getSlope(); var offset = tile.getSlopeOffset(); // calculate the point on the slope at perpindicularPosition collisionResponse = (int)( edgeToTest.isVertical() ? slope * ( perpindicularPosition - tileWorldX ) + offset + tileWorldY : ( perpindicularPosition - tileWorldY - offset ) / slope + tileWorldX ); var isColliding = edgeToTest.isMax() ? leadingPosition <= collisionResponse : leadingPosition >= collisionResponse; // this code ensures that we dont consider collisions on a slope while jumping up that dont intersect our collider. // It also makes sure when testing the bottom edge that the leadingPosition is actually above the collisionResponse. // HACK: It isn't totally perfect but it does the job if( isColliding && edgeToTest == Edge.Bottom && leadingPosition <= collisionResponse ) isColliding = false; return isColliding; } return false; }