Exemple #1
0
        /// <summary>
        /// sets the tile and updates its tileset
        /// </summary>
        /// <returns>The tile.</returns>
        /// <param name="tile">Tile.</param>
        public TiledTile SetTile(TiledTile tile)
        {
            Tiles[tile.X + tile.Y * Width] = tile;
            tile.Tileset = TiledMap.GetTilesetForTileId(tile.Id);

            return(tile);
        }
        public static Edge getNearestEdge(this TiledTile self, int worldPosition)
        {
            var tileWidth = self.tilesetTile.tiledMap.tileWidth;
            var tileMiddleWorldPosition = self.x * tileWidth + tileWidth / 2;

            return(worldPosition < tileMiddleWorldPosition ? Edge.Left : Edge.Right);
        }
Exemple #3
0
        private static TiledTileLayer readTileLayer(ContentReader reader, TiledMap tileMap, string layerName)
        {
            var tileDataCount = reader.ReadInt32();
            var tileData      = new TiledTile[tileDataCount];

            for (var d = 0; d < tileDataCount; d++)
            {
                var tileId             = reader.ReadInt32();
                var flippedHorizonally = reader.ReadBoolean();
                var flippedVertically  = reader.ReadBoolean();
                var flippedDiagonally  = reader.ReadBoolean();

                // dont add null tiles
                if (tileId != 0)
                {
                    tileData[d] = new TiledTile(tileId)
                    {
                        flippedHorizonally = flippedHorizonally,
                        flippedVertically  = flippedVertically,
                        flippedDiagonally  = flippedDiagonally
                    };
                }
                else
                {
                    tileData[d] = null;
                }
            }

            return(tileMap.createTileLayer(
                       name: layerName,
                       width: reader.ReadInt32(),
                       height: reader.ReadInt32(),
                       data: tileData));
        }
        /// <summary>
        /// sets the tile and updates its tileset
        /// </summary>
        /// <returns>The tile.</returns>
        /// <param name="tile">Tile.</param>
        public TiledTile setTile(TiledTile tile)
        {
            tiles[tile.x + tile.y * width] = tile;
            tile.tileset = tiledMap.getTilesetForTileId(tile.id);

            return(tile);
        }
Exemple #5
0
 /// <summary>
 /// returns the bounds Rectangle of the passed in tile
 /// </summary>
 /// <returns>The bounds for tile.</returns>
 /// <param name="tile">Tile.</param>
 /// <param name="tilemap">Tilemap.</param>
 public static Rectangle getBoundsForTile(TiledTile tile, TiledMap tilemap)
 {
     return(new Rectangle(
                tile.x * tilemap.tileWidth,
                tile.y * tilemap.tileHeight,
                tilemap.tileWidth,
                tilemap.tileHeight));
 }
        void renderOrthogonal(Batcher batcher, TiledTile tile, Subtexture region)
        {
            // not exactly sure why we need to compensate 1 pixel here. Could be a bug in MonoGame?
            var tx = tile.x * tiledMap.tileWidth;
            var ty = tile.y * (tiledMap.tileHeight - 1);

            batcher.draw(region.texture2D, new Rectangle(tx, ty, region.sourceRect.Width, region.sourceRect.Height), region.sourceRect, color);
        }
        public static Edge getHighestSlopeEdge(this TiledTile self)
        {
            // left and right already have flipping taken into account. Also remember a lower value means a taller slope since the slope values
            // are in pixels from the top!
            var left  = self.getSlopeTopLeft();
            var right = self.getSlopeTopRight();

            return(right > left ? Edge.Left : Edge.Right);
        }
Exemple #8
0
        void renderOrthogonal(SpriteBatch spriteBatch, TiledTile tile, Subtexture region)
        {
            // not exactly sure why we need to compensate 1 pixel here. Could be a bug in MonoGame?
            var tx = tile.x * tilemap.tileWidth;
            var ty = tile.y * (tilemap.tileHeight - 1);

            spriteBatch.Draw(region.texture2D, new Rectangle(tx, ty, region.sourceRect.Width, region.sourceRect.Height), region.sourceRect, Color.White);
            //spriteBatch.Draw( region, new Rectangle( tx, ty, region.bounds.Width, region.bounds.Height ), Color.White );
        }
Exemple #9
0
        static TiledLayer readTileLayer(ContentReader reader, TiledMap tileMap, string layerName)
        {
            var tileCount = reader.ReadInt32();
            var tiles     = new TiledTile[tileCount];

            for (var i = 0; i < tileCount; i++)
            {
                var tileId             = reader.ReadInt32();
                var flippedHorizonally = reader.ReadBoolean();
                var flippedVertically  = reader.ReadBoolean();
                var flippedDiagonally  = reader.ReadBoolean();

                // dont add null tiles
                if (tileId != 0)
                {
                    var tilesetTile = tileMap.getTilesetTile(tileId);
                    if (tilesetTile != null && tilesetTile.animationFrames != null)
                    {
                        if (tilesetTile.animationFrames.Count > 0)
                        {
                            tiles[i] = new TiledAnimatedTile(tileId, tilesetTile)
                            {
                                flippedHorizonally =
                                    flippedHorizonally,
                                flippedVertically =
                                    flippedVertically,
                                flippedDiagonally =
                                    flippedDiagonally
                            };
                            tileMap._animatedTiles.Add(tiles[i] as TiledAnimatedTile);
                        }
                    }
                    else
                    {
                        tiles[i] = new TiledTile(tileId)
                        {
                            flippedHorizonally = flippedHorizonally,
                            flippedVertically  = flippedVertically,
                            flippedDiagonally  = flippedDiagonally
                        };
                    }

                    tiles[i].tileset = tileMap.getTilesetForTileId(tileId);
                }
                else
                {
                    tiles[i] = null;
                }
            }

            return(tileMap.createTileLayer(
                       name: layerName,
                       width: reader.ReadInt32(),
                       height: reader.ReadInt32(),
                       tiles: tiles));
        }
Exemple #10
0
        public TiledTileLayer( TiledMap map, string name, int width, int height, TiledTile[] tiles )
            : base(name)
        {
            this.width = width;
            this.height = height;
            this.tiles = tiles;

            tiledMap = map;
            tiles = populateTilePositions();
        }
Exemple #11
0
        public static Edge getHighestSlopeEdge(this TiledTile self)
        {
            var left  = self.getSlopeTopLeft();
            var right = self.getSlopeTopRight();

            if (self.flippedVertically)
            {
                return(right > left ? Edge.Right : Edge.Left);
            }
            return(right > left ? Edge.Left : Edge.Right);
        }
Exemple #12
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);
        }
        void renderIsometric(Batcher batcher, TiledTile tile, Subtexture region)
        {
            var tx = (tile.x * (tiledMap.tileWidth / 2)) - (tile.y * (tiledMap.tileWidth / 2))
                     //Center
                     + (tiledMap.width * (tiledMap.tileWidth / 2))
                     //Compensate Bug?
                     - (tiledMap.tileWidth / 2);

            var ty = (tile.y * (tiledMap.tileHeight / 2)) + (tile.x * (tiledMap.tileHeight / 2))
                     //Compensate Bug?
                     - (tiledMap.tileWidth + tiledMap.tileHeight);

            batcher.draw(region.texture2D, new Rectangle(tx, ty, region.sourceRect.Width, region.sourceRect.Height), region.sourceRect, color);
        }
Exemple #14
0
        void renderIsometric(SpriteBatch spriteBatch, TiledTile tile, Subtexture region)
        {
            var tx = (tile.x * (tilemap.tileWidth / 2)) - (tile.y * (tilemap.tileWidth / 2))
                     //Center
                     + (tilemap.width * (tilemap.tileWidth / 2))
                     //Compensate Bug?
                     - (tilemap.tileWidth / 2);

            var ty = (tile.y * (tilemap.tileHeight / 2)) + (tile.x * (tilemap.tileHeight / 2))
                     //Compensate Bug?
                     - (tilemap.tileWidth + tilemap.tileHeight);

            spriteBatch.Draw(region.texture2D, new Rectangle(tx, ty, region.sourceRect.Width, region.sourceRect.Height), region.sourceRect, Color.White);
            //spriteBatch.Draw( region, new Rectangle( tx, ty, region.bounds.Width, region.bounds.Height ), Color.White );
        }
        void renderLayer(Batcher batcher, TiledMap map, TiledTile tile, Subtexture region)
        {
            switch (map.orientation)
            {
            case TiledMapOrientation.Orthogonal:
                renderOrthogonal(batcher, tile, region);
                break;

            case TiledMapOrientation.Isometric:
                renderIsometric(batcher, tile, region);
                break;

            case TiledMapOrientation.Staggered:
                throw new NotImplementedException("Staggered maps are currently not supported");
            }
        }
        public static int getSlopeTopRight(this TiledTile self)
        {
            if (self.flippedHorizonally && self.flippedVertically)
            {
                return(self.tilesetTile.slopeTopRight);
            }
            if (self.flippedHorizonally)
            {
                return(self.tilesetTile.slopeTopLeft);
            }
            if (self.flippedVertically)
            {
                return(self.tilesetTile.tiledMap.tileWidth - self.tilesetTile.slopeTopRight);
            }

            return(self.tilesetTile.slopeTopRight);
        }
        public static float getSlope(this TiledTile self)
        {
            var tileSize = self.tilesetTile.tiledMap.tileWidth;

            // flip our slopes sign if the tile is flipped horizontally or vertically
            if (self.flippedHorizonally)
            {
                tileSize *= -1;
            }

            if (self.flippedVertically)
            {
                tileSize *= -1;
            }

            // rise over run
            return(((float)self.tilesetTile.slopeTopRight - (float)self.tilesetTile.slopeTopLeft) / (float)tileSize);
        }
Exemple #18
0
        /// <summary>
        /// moves the Entity taking into account the tiled map
        /// </summary>
        /// <param name="motion">Motion.</param>
        /// <param name="deltaTime">Delta time.</param>
        public void move( Vector2 motion, float deltaTime )
        {
            // save off our current grounded state which we will use for wasGroundedLastFrame and becameGroundedThisFrame
            collisionState.wasGroundedLastFrame = collisionState.below;

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

            // deal with subpixel movement, storing off any non-integar remainder for the next frame
            _movementRemainderX += motion.X;
            var motionX = Mathf.truncateToInt( _movementRemainderX );
            _movementRemainderX -= motionX;
            motion.X = motionX;

            _movementRemainderY += motion.Y;
            var motionY = Mathf.truncateToInt( _movementRemainderY );
            _movementRemainderY -= motionY;
            motion.Y = motionY;

            // store off the bounds so we can move it around without affecting the actual Transform position
            var colliderBounds = _collider.bounds;

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

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

            // next, check movement in the vertical dir
            {
                // HACK: when motionY is 0 we shouldnt check anything and we should retain collision state
                if( motionY == 0 )
                    motionY = 1;
                var direction = motionY > 0 ? Edge.Bottom : Edge.Top;
                var sweptBounds = collisionRectForSide( direction, motionY );
                sweptBounds.X += (int)motion.X;

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

                // when moving down we also check for collisions in the opposite direction
                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, out collisionResponse ) )
                    {
                        // react to collision. get the distance between our leading edge and what we collided with
                        motion.Y = collisionResponse - colliderBounds.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;
                    }
                }
            }

            // move then update our state
            _collider.unregisterColliderWithPhysicsSystem();
            transform.position += motion;
            _collider.registerColliderWithPhysicsSystem();

            // only calculate velocity if we have a non-zero deltaTime
            if( deltaTime > 0f )
                velocity = motion / deltaTime;

            if( collisionState.below || collisionState.above )
            {
                _movementRemainderY = 0;
                velocity.Y = 0;
            }

            if( collisionState.right || collisionState.left )
            {
                _movementRemainderX = 0;
                velocity.X = 0;
            }

            // set our becameGrounded state based on the previous and current collision state
            if( !collisionState.wasGroundedLastFrame && collisionState.below )
                collisionState.becameGroundedThisFrame = true;
        }
Exemple #19
0
 /// <summary>
 /// returns the bounds Rectangle of the passed in tile
 /// </summary>
 /// <returns>The bounds for tile.</returns>
 /// <param name="tile">Tile.</param>
 /// <param name="tilemap">Tilemap.</param>
 public static Rectangle getBoundsForTile( TiledTile tile, TiledMap tilemap )
 {
     return new Rectangle( tile.x * tilemap.tileWidth, tile.y * tilemap.tileHeight, tilemap.tileWidth, tilemap.tileHeight );
 }
 public static bool isOneWayPlatform(this TiledTile self)
 {
     return(self.tilesetTile != null && self.tilesetTile.isOneWayPlatform);
 }
Exemple #21
0
			public void clearLastGroundTile()
			{
				_lastGroundTile = null;
			}
 public static bool isSlope(this TiledTile self)
 {
     return(self.tilesetTile != null && self.tilesetTile.isSlope);
 }
 public static float getSlopeOffset(this TiledTile self)
 {
     return((float)self.getSlopeTopLeft());
 }
Exemple #24
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;
        }
Exemple #25
0
        /// <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);
        }
Exemple #26
0
        /// <summary>
        /// moves the Entity taking into account the tiled map
        /// </summary>
        /// <param name="motion">Motion.</param>
        /// <param name="deltaTime">Delta time.</param>
        public void move(Vector2 motion, float deltaTime)
        {
            // save off our current grounded state which we will use for wasGroundedLastFrame and becameGroundedThisFrame
            collisionState.wasGroundedLastFrame = collisionState.below;

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

            // deal with subpixel movement, storing off any non-integar remainder for the next frame
            _movementRemainderX += motion.X;
            var motionX = Mathf.truncateToInt(_movementRemainderX);

            _movementRemainderX -= motionX;
            motion.X             = motionX;

            _movementRemainderY += motion.Y;
            var motionY = Mathf.truncateToInt(_movementRemainderY);

            _movementRemainderY -= motionY;
            motion.Y             = motionY;

            // store off the bounds so we can move it around without affecting the actual Transform position
            var colliderBounds = _collider.bounds;

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

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

            // next, check movement in the vertical dir
            {
                // HACK: when motionY is 0 we shouldnt check anything and we should retain collision state
                if (motionY == 0)
                {
                    motionY = 1;
                }
                var direction   = motionY > 0 ? Edge.Bottom : Edge.Top;
                var sweptBounds = collisionRectForSide(direction, motionY);
                sweptBounds.X += (int)motion.X;

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

                // when moving down we also check for collisions in the opposite direction
                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, out collisionResponse))
                    {
                        // react to collision. get the distance between our leading edge and what we collided with
                        motion.Y = collisionResponse - colliderBounds.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;
                    }
                }
            }

            // move then update our state
            _collider.unregisterColliderWithPhysicsSystem();
            transform.position += motion;
            _collider.registerColliderWithPhysicsSystem();

            // only calculate velocity if we have a non-zero deltaTime
            if (deltaTime > 0f)
            {
                velocity = motion / deltaTime;
            }

            if (collisionState.below || collisionState.above)
            {
                _movementRemainderY = 0;
                velocity.Y          = 0;
            }

            if (collisionState.right || collisionState.left)
            {
                _movementRemainderX = 0;
                velocity.X          = 0;
            }

            // set our becameGrounded state based on the previous and current collision state
            if (!collisionState.wasGroundedLastFrame && collisionState.below)
            {
                collisionState.becameGroundedThisFrame = true;
            }
        }
Exemple #27
0
 public void clearLastGroundTile()
 {
     _lastGroundTile = null;
 }
Exemple #28
0
		/// <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;
		}