/// <summary> /// Berechnet die Geschwindigkeit einer <see cref="Entity"/> nach der Kollision mit der Welt. (Original Lassi) /// </summary> /// <param name="gameTime">Simulation time</param> /// <param name="position">Position der <see cref="Entity"/></param> /// <param name="cache"><see cref="ILocalChunkCache"/> as workspace</param> /// <param name="radius">Radius der <see cref="Entity"/></param> /// <param name="height">Höhe der <see cref="Entity"/></param> /// <param name="deltaPosition">Positionsänderung zwischen zwei Simulationsdurchläufen</param> /// <param name="velocity">Berechnete Geschwindigkeit</param> /// <exception cref="ArgumentNullException">Cache</exception> /// <returns>Geschwindigkeit der <see cref="Entity"/> nach der Killisionsprüfung</returns> public Vector3 WorldCollision(GameTime gameTime, Coordinate position, ILocalChunkCache cache, float radius, float height, Vector3 deltaPosition, Vector3 velocity) { if (cache == null) { throw new ArgumentNullException(nameof(cache)); } Vector3 move = deltaPosition; //Blocks finden die eine Kollision verursachen könnten int minx = (int)Math.Floor(Math.Min( position.BlockPosition.X - radius, position.BlockPosition.X - radius + deltaPosition.X)); int maxx = (int)Math.Ceiling(Math.Max( position.BlockPosition.X + radius, position.BlockPosition.X + radius + deltaPosition.X)); int miny = (int)Math.Floor(Math.Min( position.BlockPosition.Y - radius, position.BlockPosition.Y - radius + deltaPosition.Y)); int maxy = (int)Math.Ceiling(Math.Max( position.BlockPosition.Y + radius, position.BlockPosition.Y + radius + deltaPosition.Y)); int minz = (int)Math.Floor(Math.Min( position.BlockPosition.Z, position.BlockPosition.Z + deltaPosition.Z)); int maxz = (int)Math.Ceiling(Math.Max( position.BlockPosition.Z + height, position.BlockPosition.Z + height + deltaPosition.Z)); //Beteiligte Flächen des Spielers var playerplanes = CollisionPlane.GetEntityCollisionPlanes(radius, height, velocity, position); for (int z = minz; z <= maxz; z++) { for (int y = miny; y <= maxy; y++) { for (int x = minx; x <= maxx; x++) { move = velocity * (float)gameTime.ElapsedGameTime.TotalSeconds; Index3 pos = new Index3(x, y, z); Index3 blockPos = pos + position.GlobalBlockIndex; ushort block = cache.GetBlock(blockPos); if (block == 0) { continue; } var blockplanes = CollisionPlane.GetBlockCollisionPlanes(pos, velocity); foreach (var playerPlane in playerplanes) { foreach (var blockPlane in blockplanes) { if (!CollisionPlane.Intersect(blockPlane, playerPlane)) { continue; } var distance = CollisionPlane.GetDistance(blockPlane, playerPlane); if (!CollisionPlane.CheckDistance(distance, move)) { continue; } var subvelocity = (distance / (float)gameTime.ElapsedGameTime.TotalSeconds); var diff = velocity - subvelocity; float vx; float vy; float vz; if (blockPlane.normal.X != 0 && (velocity.X > 0 && diff.X >= 0 && subvelocity.X >= 0 || velocity.X < 0 && diff.X <= 0 && subvelocity.X <= 0)) { vx = subvelocity.X; } else { vx = velocity.X; } if (blockPlane.normal.Y != 0 && (velocity.Y > 0 && diff.Y >= 0 && subvelocity.Y >= 0 || velocity.Y < 0 && diff.Y <= 0 && subvelocity.Y <= 0)) { vy = subvelocity.Y; } else { vy = velocity.Y; } if (blockPlane.normal.Z != 0 && (velocity.Z > 0 && diff.Z >= 0 && subvelocity.Z >= 0 || velocity.Z < 0 && diff.Z <= 0 && subvelocity.Z <= 0)) { vz = subvelocity.Z; } else { vz = velocity.Z; } velocity = new Vector3(vx, vy, vz); if (vx == 0 && vy == 0 && vz == 0) { return(velocity); } } } } } } return(velocity); }
private void CheckBoxCollision(GameTime gameTime, Entity entity, MoveableComponent movecomp, PositionComponent poscomp) { if (!entity.Components.ContainsComponent <BodyComponent>()) { return; } BodyComponent bc = entity.Components.GetComponent <BodyComponent>(); Coordinate position = poscomp.Position; Vector3 move = movecomp.PositionMove; //Blocks finden die eine Kollision verursachen könnten int minx = (int)Math.Floor(Math.Min( position.BlockPosition.X - bc.Radius, position.BlockPosition.X - bc.Radius + movecomp.PositionMove.X)); int maxx = (int)Math.Ceiling(Math.Max( position.BlockPosition.X + bc.Radius, position.BlockPosition.X + bc.Radius + movecomp.PositionMove.X)); int miny = (int)Math.Floor(Math.Min( position.BlockPosition.Y - bc.Radius, position.BlockPosition.Y - bc.Radius + movecomp.PositionMove.Y)); int maxy = (int)Math.Ceiling(Math.Max( position.BlockPosition.Y + bc.Radius, position.BlockPosition.Y + bc.Radius + movecomp.PositionMove.Y)); int minz = (int)Math.Floor(Math.Min( position.BlockPosition.Z, position.BlockPosition.Z + movecomp.PositionMove.Z)); int maxz = (int)Math.Ceiling(Math.Max( position.BlockPosition.Z + bc.Height, position.BlockPosition.Z + bc.Height + movecomp.PositionMove.Z)); //Beteiligte Flächen des Spielers var playerplanes = CollisionPlane.GetEntityCollisionPlanes(bc.Radius, bc.Height, movecomp.Velocity, poscomp.Position); bool abort = false; var cache = entity.Components.GetComponent <LocalChunkCacheComponent>().LocalChunkCache; for (int z = minz; z <= maxz && !abort; z++) { for (int y = miny; y <= maxy && !abort; y++) { for (int x = minx; x <= maxx && !abort; x++) { move = movecomp.Velocity * (float)gameTime.ElapsedGameTime.TotalSeconds; Index3 pos = new Index3(x, y, z); Index3 blockPos = pos + position.GlobalBlockIndex; ushort block = cache.GetBlock(blockPos); if (block == 0) { continue; } var blockplane = CollisionPlane.GetBlockCollisionPlanes(pos, movecomp.Velocity).ToList(); var planes = from pp in playerplanes from bp in blockplane where CollisionPlane.Intersect(bp, pp) let distance = CollisionPlane.GetDistance(bp, pp) where CollisionPlane.CheckDistance(distance, move) select new { BlockPlane = bp, PlayerPlane = pp, Distance = distance }; foreach (var plane in planes) { var subvelocity = (plane.Distance / (float)gameTime.ElapsedGameTime.TotalSeconds); var diff = movecomp.Velocity - subvelocity; float vx; float vy; float vz; if (plane.BlockPlane.normal.X != 0 && (movecomp.Velocity.X > 0 && diff.X >= 0 && subvelocity.X >= 0 || movecomp.Velocity.X < 0 && diff.X <= 0 && subvelocity.X <= 0)) { vx = subvelocity.X; } else { vx = movecomp.Velocity.X; } if (plane.BlockPlane.normal.Y != 0 && (movecomp.Velocity.Y > 0 && diff.Y >= 0 && subvelocity.Y >= 0 || movecomp.Velocity.Y < 0 && diff.Y <= 0 && subvelocity.Y <= 0)) { vy = subvelocity.Y; } else { vy = movecomp.Velocity.Y; } if (plane.BlockPlane.normal.Z != 0 && (movecomp.Velocity.Z > 0 && diff.Z >= 0 && subvelocity.Z >= 0 || movecomp.Velocity.Z < 0 && diff.Z <= 0 && subvelocity.Z <= 0)) { vz = subvelocity.Z; } else { vz = movecomp.Velocity.Z; } movecomp.Velocity = new Vector3(vx, vy, vz); if (vx == 0 && vy == 0 && vz == 0) { abort = true; break; } } } } } // TODO: Was ist für den Fall Gravitation = 0 oder im Scheitelpunkt des Sprungs? //movecomp.OnGround = Player.Velocity.Z == 0f; movecomp.PositionMove = movecomp.Velocity * (float)gameTime.ElapsedGameTime.TotalSeconds; }