private void GenerateTileGeometry() { // We only care about layers that are solid. Cache which are. Tile[][,] SolidTiles = Scene.Layers.Where(c => c.IsSolid).Select(c => c.Tiles).ToArray(); // We'll check if tiles are checked already, as it could get a bit complicated to keep track. // To check if a tile is checked, we'll simply store if it's X and Y coordinate was checked. // Using a bool array of hundreds of thousands of elements seems a bit wasteful however. BitArray CheckedTiles = new BitArray((int)(Scene.TilesInMap.Y * Scene.TilesInMap.X), false); for (int y = 0; y < Scene.TilesInMap.Y; y++) { for (int x = 0; x < Scene.TilesInMap.X; x++) { if (!Exists(SolidTiles, CheckedTiles, x, y)) { continue; } int BitIndex = y * (int)Scene.TilesInMap.X + x; bool AlreadyChecked = CheckedTiles.Get(BitIndex); if (AlreadyChecked) { continue; } //CheckedTiles.Set(BitIndex, true); // When we have a tile we need to check, find the largest rectangle that encompasses it. Rectangle TileBoundaries = GetLargestSolidTileRectangle(SolidTiles, CheckedTiles, x, y); Rectangle WorldBoundaries = new Rectangle(TileBoundaries.X * (int)Scene.TileSize.X, TileBoundaries.Y * (int)Scene.TileSize.Y, TileBoundaries.Width * (int)Scene.TileSize.X, TileBoundaries.Height * (int)Scene.TileSize.Y); TiledPlatformerGeometryObject Obj = new TiledPlatformerGeometryObject(WorldBoundaries); AddGeometry(Obj); } } }
/// <summary> /// Indicates whether the given Entity can reach the second object, starting from the first, in one jump. /// </summary> public bool CanEntityReachObject(Entity Entity, TiledPlatformerGeometryObject First, TiledPlatformerGeometryObject Second) { var PhysicsComponent = Entity.GetComponent <PhysicsComponent>(); var PhysicsSystem = Scene.GetSystem <PhysicsSystem>(); var MovementComponent = Entity.GetComponent <MovementComponent>(); if (PhysicsComponent == null || PhysicsSystem == null || MovementComponent == null) { throw new InvalidOperationException("Unable to calculate a path for an Entity that has no physics component, movement component, or if a physics system is not set."); } double Gravity = PhysicsSystem.Gravity * PhysicsComponent.GravityCoefficient; double Drag = PhysicsSystem.HorizontalDrag * PhysicsComponent.HorizontalDragCoefficient; double JumpAccel = MovementComponent.JumpSpeed; TimeSpan TimeForTopHeight = TimeSpan.FromSeconds(JumpAccel / Gravity); double JumpHeight = JumpAccel / TimeForTopHeight.TotalSeconds / 2; if (First.Location.Top + JumpHeight < Second.Location.Top) { return(false); } // Another alternative to all this below stuff is to determine the time for the next tile and evaluate it using the Curve class. // Then check if there's a geometry object there. double HeightDifference = Second.Location.Top - First.Location.Top; // Next, we need to determine the amount of time it would take to hit the ground. // This is done in the same way as above, added on to time for top. // This is technically wrong, because it assumes equal height and not ground. // But we'll keep it for now. The alternative is much, much, less pleasant curve calculations. TimeSpan TimeToSecondHeight = TimeSpan.FromSeconds((JumpHeight - HeightDifference) / Gravity) + TimeForTopHeight; /*// Calculate how long it'll take us to reach our max walking speed to determine X distance. * TimeSpan TimeToMaxWalkingSpeed = TimeSpan.FromSeconds((MovementComponent.MaxWalkingSpeed - PhysicsComponent.VelocityX) / (MovementComponent.WalkAcceleration - Drag));*/ // Turns out we don't need that; instead we'll assume constant speed because we're going to get a running start when jumping off the platform. // Probably. // Thus, our max horizontal distance travelled just our total velocity * time. double HorizDistance = TimeToSecondHeight.TotalSeconds * MovementComponent.MaxWalkingSpeed; if (Second.Location.Left > First.Location.Right - (Scene.TileSize.X / 2) + HorizDistance) { return(false); } // So we now know that there is a theoretical possibility that we can reach that object. // The next step is to use a curve and determine if we're going to collide with anything. // One approach would be to determine the amount of time until reaching the next tile vertically and check if object at X location there. // This is a pretty painfully expensive approach however. // But what do we do if we are? // Perhaps if we jumped a few steps early we wouldn't be colliding with anything. // Perhaps we could slightly stop while in the air to prevent it. // Perhaps we'd hit a barrier that we wouldn't if we were going slower. // Much unpleasantness. // For now... close enough, we'll pretend we can reach. return(true); }
private void GenerateTileGeometry() { // We only care about layers that are solid. Cache which are. Tile[][,] SolidTiles = Scene.Layers.Where(c => c.IsSolid).Select(c => c.Tiles).ToArray(); // We'll check if tiles are checked already, as it could get a bit complicated to keep track. // To check if a tile is checked, we'll simply store if it's X and Y coordinate was checked. // Using a bool array of hundreds of thousands of elements seems a bit wasteful however. BitArray CheckedTiles = new BitArray((int)(Scene.TilesInMap.Y * Scene.TilesInMap.X), false); for(int y = 0; y < Scene.TilesInMap.Y; y++) { for(int x = 0; x < Scene.TilesInMap.X; x++) { if(!Exists(SolidTiles, CheckedTiles, x, y)) continue; int BitIndex = y * (int)Scene.TilesInMap.X + x; bool AlreadyChecked = CheckedTiles.Get(BitIndex); if(AlreadyChecked) continue; //CheckedTiles.Set(BitIndex, true); // When we have a tile we need to check, find the largest rectangle that encompasses it. Rectangle TileBoundaries = GetLargestSolidTileRectangle(SolidTiles, CheckedTiles, x, y); Rectangle WorldBoundaries = new Rectangle(TileBoundaries.X * (int)Scene.TileSize.X, TileBoundaries.Y * (int)Scene.TileSize.Y, TileBoundaries.Width * (int)Scene.TileSize.X, TileBoundaries.Height * (int)Scene.TileSize.Y); TiledPlatformerGeometryObject Obj = new TiledPlatformerGeometryObject(WorldBoundaries); AddGeometry(Obj); } } }
/// <summary> /// Indicates whether the given Entity can reach the second object, starting from the first, in one jump. /// </summary> public bool CanEntityReachObject(Entity Entity, TiledPlatformerGeometryObject First, TiledPlatformerGeometryObject Second) { var PhysicsComponent = Entity.GetComponent<PhysicsComponent>(); var PhysicsSystem = Scene.GetSystem<PhysicsSystem>(); var MovementComponent = Entity.GetComponent<MovementComponent>(); if(PhysicsComponent == null || PhysicsSystem == null || MovementComponent == null) throw new InvalidOperationException("Unable to calculate a path for an Entity that has no physics component, movement component, or if a physics system is not set."); double Gravity = PhysicsSystem.Gravity * PhysicsComponent.GravityCoefficient; double Drag = PhysicsSystem.HorizontalDrag * PhysicsComponent.HorizontalDragCoefficient; double JumpAccel = MovementComponent.JumpSpeed; TimeSpan TimeForTopHeight = TimeSpan.FromSeconds(JumpAccel / Gravity); double JumpHeight = JumpAccel / TimeForTopHeight.TotalSeconds / 2; if(First.Location.Top + JumpHeight < Second.Location.Top) return false; // Another alternative to all this below stuff is to determine the time for the next tile and evaluate it using the Curve class. // Then check if there's a geometry object there. double HeightDifference = Second.Location.Top - First.Location.Top; // Next, we need to determine the amount of time it would take to hit the ground. // This is done in the same way as above, added on to time for top. // This is technically wrong, because it assumes equal height and not ground. // But we'll keep it for now. The alternative is much, much, less pleasant curve calculations. TimeSpan TimeToSecondHeight = TimeSpan.FromSeconds((JumpHeight - HeightDifference) / Gravity) + TimeForTopHeight; /*// Calculate how long it'll take us to reach our max walking speed to determine X distance. TimeSpan TimeToMaxWalkingSpeed = TimeSpan.FromSeconds((MovementComponent.MaxWalkingSpeed - PhysicsComponent.VelocityX) / (MovementComponent.WalkAcceleration - Drag));*/ // Turns out we don't need that; instead we'll assume constant speed because we're going to get a running start when jumping off the platform. // Probably. // Thus, our max horizontal distance travelled just our total velocity * time. double HorizDistance = TimeToSecondHeight.TotalSeconds * MovementComponent.MaxWalkingSpeed; if(Second.Location.Left > First.Location.Right - (Scene.TileSize.X / 2) + HorizDistance) return false; // So we now know that there is a theoretical possibility that we can reach that object. // The next step is to use a curve and determine if we're going to collide with anything. // One approach would be to determine the amount of time until reaching the next tile vertically and check if object at X location there. // This is a pretty painfully expensive approach however. // But what do we do if we are? // Perhaps if we jumped a few steps early we wouldn't be colliding with anything. // Perhaps we could slightly stop while in the air to prevent it. // Perhaps we'd hit a barrier that we wouldn't if we were going slower. // Much unpleasantness. // For now... close enough, we'll pretend we can reach. return true; }