private void AdjustVelocityForCollision(IAABBEntity entity, BoundingBox problem) { var velocity = entity.Velocity; if (entity.Velocity.X < 0) { velocity.X = entity.BoundingBox.Min.X - problem.Max.X; } if (entity.Velocity.X > 0) { velocity.X = entity.BoundingBox.Max.X - problem.Min.X; } if (entity.Velocity.Y < 0) { velocity.Y = entity.BoundingBox.Min.Y - problem.Max.Y; } if (entity.Velocity.Y > 0) { velocity.Y = entity.BoundingBox.Max.Y - problem.Min.Y; } if (entity.Velocity.Z < 0) { velocity.Z = entity.BoundingBox.Min.Z - problem.Max.Z; } if (entity.Velocity.Z > 0) { velocity.Z = entity.BoundingBox.Max.Z - problem.Min.Z; } entity.Velocity = velocity; }
private void CheckWithTerrain(IAABBEntity entity, World world) { Vector3 collisionPoint, collisionDirection; if (entity.Position.Y + entity.Velocity.Y >= 0 && entity.Position.Y + entity.Velocity.Y <= 255) // Don't do checks outside the map { bool fireEvent = entity.Velocity != Vector3.Zero; // Do terrain collisions if (AdjustVelocityX(entity, world, out collisionPoint, out collisionDirection)) { if (fireEvent) entity.TerrainCollision(this, collisionPoint, collisionDirection); } if (AdjustVelocityY(entity, world, out collisionPoint, out collisionDirection)) { entity.Velocity *= new Vector3(0.2, 1, 0.2); // TODO: More sophisticated friction if (fireEvent) entity.TerrainCollision(this, collisionPoint, collisionDirection); } if (AdjustVelocityZ(entity, world, out collisionPoint, out collisionDirection)) { if (fireEvent) entity.TerrainCollision(this, collisionPoint, collisionDirection); } } }
private void CheckWithTerrain(IAABBEntity entity, ReadOnlyWorld world) { Vector3 collisionPoint, collisionDirection; if (entity.Position.Y > 0 && entity.Position.Y <= 127) // Don't do checks outside the map { bool fireEvent = entity.Velocity != Vector3.Zero; // Do terrain collisions if (AdjustVelocityX(entity, world, out collisionPoint, out collisionDirection)) { if (fireEvent) { entity.TerrainCollision(collisionPoint, collisionDirection); } } if (AdjustVelocityY(entity, world, out collisionPoint, out collisionDirection)) { entity.Velocity *= new Vector3(0.1, 1, 0.1); // TODO: More sophisticated friction if (fireEvent) { entity.TerrainCollision(collisionPoint, collisionDirection); } } if (AdjustVelocityZ(entity, world, out collisionPoint, out collisionDirection)) { if (fireEvent) { entity.TerrainCollision(collisionPoint, collisionDirection); } } } }
public void Initialize(Camera camera, IAABBEntity playerEntity) { m_Camera = camera; m_PlayerEntity = playerEntity; m_DestroyRaycastSelector = DestroyRaycastSelect; m_PlaceRaycastSelector = PlaceRaycastSelect; }
public void DestroyEntity(IAABBEntity entity) { entity.OnRecycle(); if (entity is IRenderableEntity renderableEntity) { m_RenderableEntities.Remove(renderableEntity); } if (entity is LuaBlockEntity blockEntity) { blockEntity.gameObject.SetActive(false); m_BlockEntityPool.Enqueue(blockEntity); } }
private BoundingBox GetAABBVelocityBox(IAABBEntity entity) { var min = new Vector3( Math.Min(entity.BoundingBox.Min.X, entity.BoundingBox.Min.X + entity.Velocity.X), Math.Min(entity.BoundingBox.Min.Y, entity.BoundingBox.Min.Y + entity.Velocity.Y), Math.Min(entity.BoundingBox.Min.Z, entity.BoundingBox.Min.Z + entity.Velocity.Z) ); var max = new Vector3( Math.Max(entity.BoundingBox.Max.X, entity.BoundingBox.Max.X + entity.Velocity.X), Math.Max(entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Y + entity.Velocity.Y), Math.Max(entity.BoundingBox.Max.Z, entity.BoundingBox.Max.Z + entity.Velocity.Z) ); return(new BoundingBox(min, max)); }
public bool TestTerrainCollisionCylinder(IAABBEntity entity, out Vector3 collisionPoint) { collisionPoint = Vector3.Zero; var testBox = GetAABBVelocityBox(entity); var testCylinder = new BoundingCylinder(testBox.Min, testBox.Max, entity.BoundingBox.Min.DistanceTo(entity.BoundingBox.Max)); var collision = false; for (var x = (int)Math.Floor(testBox.Min.X); x <= (int)Math.Ceiling(testBox.Max.X); x++) { for (var z = (int)Math.Floor(testBox.Min.Z); z <= (int)Math.Ceiling(testBox.Max.Z); z++) { for (var y = (int)Math.Floor(testBox.Min.Y); y <= (int)Math.Ceiling(testBox.Max.Y); y++) { var coords = new Coordinates3D(x, y, z); if (!World.IsValidPosition(coords)) { continue; } var _box = BlockPhysicsProvider.GetBoundingBox(World, coords); if (_box == null) { continue; } var box = _box.Value.OffsetBy(coords.AsVector3()); if (testCylinder.Intersects(box)) { if (testBox.Intersects(box)) { collision = true; AdjustVelocityForCollision(entity, box); testBox = GetAABBVelocityBox(entity); testCylinder = new BoundingCylinder(testBox.Min, testBox.Max, entity.BoundingBox.Min.DistanceTo(entity.BoundingBox.Max)); collisionPoint = coords.AsVector3(); } } } } } return(collision); }
public bool TestTerrainCollisionCylinder(IAABBEntity entity, out Vector3 collisionPoint) { collisionPoint = Vector3.Zero; var testBox = GetAABBVelocityBox(entity); var testCylinder = new BoundingCylinder(testBox.Min, testBox.Max, entity.BoundingBox.Min.DistanceTo(entity.BoundingBox.Max)); bool collision = false; for (int x = (int)(Math.Floor(testBox.Min.X)); x <= (int)(Math.Ceiling(testBox.Max.X)); x++) { for (int z = (int)(Math.Floor(testBox.Min.Z)); z <= (int)(Math.Ceiling(testBox.Max.Z)); z++) { for (int y = (int)(Math.Floor(testBox.Min.Y)); y <= (int)(Math.Ceiling(testBox.Max.Y)); y++) { var coords = new Coordinates3D(x, y, z); if (!World.IsValidPosition(coords)) continue; var _box = BlockPhysicsProvider.GetBoundingBox(World, coords); if (_box == null) continue; var box = _box.Value.OffsetBy(coords); if (testCylinder.Intersects(box)) { if (testBox.Intersects(box)) { collision = true; AdjustVelocityForCollision(entity, box); testBox = GetAABBVelocityBox(entity); testCylinder = new BoundingCylinder(testBox.Min, testBox.Max, entity.BoundingBox.Min.DistanceTo(entity.BoundingBox.Max)); collisionPoint = coords; } } } } } return collision; }
private float CheckBody(IAABBEntity entity) { AABB aabb = entity.BoundingBox + entity.Position; Vector3Int center = aabb.Center.FloorToInt(); int minY = Mathf.FloorToInt(aabb.Min.y); int maxY = Mathf.FloorToInt(aabb.Max.y); BlockData blockData = null; int index = int.MaxValue; for (int y = minY; y < maxY; y++) { BlockData block = entity.World.RWAccessor.GetBlock(center.x, y, center.z); // 根据 m_Fluids 数组元素的顺序来确定方块 for (int i = 0; i < m_Fluids.Length; i++) { // 越靠前,优先级越高 if (i >= index) { break; } if (block.InternalName == m_Fluids[i].BlockName) { blockData = block; index = i; break; } } } if (blockData != null && m_BlockAtBody != blockData.InternalName) { m_BlockAtBody = blockData.InternalName; return(m_Fluids[index].VelocityMultiplier); } return(m_FluidMap.TryGetValue(m_BlockAtBody, out FluidInfo info) ? info.VelocityMultiplier : 1); // default is 1 }
private void CheckHead(IAABBEntity entity, Transform camera) { Vector3 pos = camera.position; int y = Mathf.FloorToInt(pos.y); if (y < 0 || y >= ChunkHeight) { return; } int x = Mathf.FloorToInt(pos.x); int z = Mathf.FloorToInt(pos.z); BlockData block = entity.World.RWAccessor.GetBlock(x, y, z); if (block.InternalName != m_BlockAtHead && m_FluidMap.TryGetValue(block.InternalName, out FluidInfo info)) { m_BlockAtHead = block.InternalName; ShaderUtility.ViewDistance = info.ViewDistance; ShaderUtility.WorldAmbientColorDay = info.AmbientColorDay; ShaderUtility.WorldAmbientColorNight = info.AmbientColorNight; } }
/// <summary> /// Performs terrain collision tests and adjusts the Y-axis velocity accordingly /// </summary> /// <returns>True if the entity collides with the terrain</returns> private bool AdjustVelocityY(IAABBEntity entity, World world, out Vector3 collision, out Vector3 collisionDirection) { collision = Vector3.Zero; collisionDirection = Vector3.Zero; if (entity.Velocity.Y == 0) return false; // Do some enviornment guessing to improve speed int minX = (int)entity.Position.X - (entity.Position.X < 0 ? 1 : 0); int maxX = (int)(entity.Position.X + entity.Size.Width) - (entity.Position.X < 0 ? 1 : 0); int minZ = (int)entity.Position.Z - (entity.Position.Z < 0 ? 1 : 0); int maxZ = (int)(entity.Position.Z + entity.Size.Depth) - (entity.Position.Z < 0 ? 1 : 0); int minY, maxY; // Expand bounding box to include area to be tested if (entity.Velocity.Y < 0) { TempBoundingBox = new BoundingBox( new Vector3(entity.BoundingBox.Min.X, entity.BoundingBox.Min.Y + entity.Velocity.Y, entity.BoundingBox.Min.Z) - (entity.Size / 2), new Vector3(entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z) - (entity.Size / 2) ); maxY = (int)(TempBoundingBox.Max.Y); minY = (int)(TempBoundingBox.Min.Y + entity.Velocity.Y); } else { TempBoundingBox = new BoundingBox( entity.BoundingBox.Min - (entity.Size / 2), new Vector3( entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y + entity.Velocity.Y, entity.BoundingBox.Max.Z) - (entity.Size / 2) ); minY = (int)(entity.BoundingBox.Min.Y); maxY = (int)(entity.BoundingBox.Max.Y + entity.Velocity.Y); } // Clamp Y into map boundaries if (minY < 0) minY = 0; if (minY >= World.Height) minY = World.Height - 1; if (maxY < 0) maxY = 0; if (maxY >= World.Height) maxY = World.Height - 1; // Do terrain checks double? collisionPoint = null; BoundingBox blockBox; for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { for (int z = minZ; z <= maxZ; z++) { var position = new Vector3(x, y, z); var boundingBox = BlockPhysicsProvider.GetBoundingBox(world, (Coordinates3D)position); if (boundingBox == null) continue; blockBox = boundingBox.Value.OffsetBy(position); if (TempBoundingBox.Intersects(blockBox)) { if (entity.Velocity.Y < 0) { if (!collisionPoint.HasValue) collisionPoint = blockBox.Max.Y; else if (collisionPoint.Value < blockBox.Max.Y) collisionPoint = blockBox.Max.Y; } else { if (!collisionPoint.HasValue) collisionPoint = blockBox.Min.Y; else if (collisionPoint.Value > blockBox.Min.Y) collisionPoint = blockBox.Min.Y; } collision = position; } } } } if (collisionPoint != null) { if (entity.Velocity.Y < 0) { // TODO: Do block event //var block = world.GetBlock(collision); //block.OnBlockWalkedOn(world, collision, this); entity.Velocity = new Vector3(entity.Velocity.X, entity.Velocity.Y + (collisionPoint.Value - TempBoundingBox.Min.Y), entity.Velocity.Z); collisionDirection = Vector3.Down; } else if (entity.Velocity.Y > 0) { entity.Velocity = new Vector3(entity.Velocity.X, entity.Velocity.Y - (TempBoundingBox.Max.Y - collisionPoint.Value), entity.Velocity.Z); collisionDirection = Vector3.Up; } return true; } return false; }
private BoundingBox GetAABBVelocityBox(IAABBEntity entity) { var min = new Vector3( Math.Min(entity.BoundingBox.Min.X, entity.BoundingBox.Min.X + entity.Velocity.X), Math.Min(entity.BoundingBox.Min.Y, entity.BoundingBox.Min.Y + entity.Velocity.Y), Math.Min(entity.BoundingBox.Min.Z, entity.BoundingBox.Min.Z + entity.Velocity.Z) ); var max = new Vector3( Math.Max(entity.BoundingBox.Max.X, entity.BoundingBox.Max.X + entity.Velocity.X), Math.Max(entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Y + entity.Velocity.Y), Math.Max(entity.BoundingBox.Max.Z, entity.BoundingBox.Max.Z + entity.Velocity.Z) ); return new BoundingBox(min, max); }
private void AdjustVelocityForCollision(IAABBEntity entity, BoundingBox problem) { var velocity = entity.Velocity; if (entity.Velocity.X < 0) velocity.X = entity.BoundingBox.Min.X - problem.Max.X; if (entity.Velocity.X > 0) velocity.X = entity.BoundingBox.Max.X - problem.Min.X; if (entity.Velocity.Y < 0) velocity.Y = entity.BoundingBox.Min.Y - problem.Max.Y; if (entity.Velocity.Y > 0) velocity.Y = entity.BoundingBox.Max.Y - problem.Min.Y; if (entity.Velocity.Z < 0) velocity.Z = entity.BoundingBox.Min.Z - problem.Max.Z; if (entity.Velocity.Z > 0) velocity.Z = entity.BoundingBox.Max.Z - problem.Min.Z; entity.Velocity = velocity; }
public bool TestTerrainCollisionZ(IAABBEntity entity, out Vector3 collisionPoint) { // Things we need to do: // 1 - expand bounding box to include the destination and everything within // 2 - collect all blocks within that area // 3 - test bounding boxes in direction of motion collisionPoint = Vector3.Zero; if (entity.Velocity.Z == 0) return false; bool negative; BoundingBox testBox; if (entity.Velocity.Z < 0) { testBox = new BoundingBox( new Vector3( entity.BoundingBox.Min.X, entity.BoundingBox.Min.Y, entity.BoundingBox.Min.Z + entity.Velocity.Z), entity.BoundingBox.Max); negative = true; } else { testBox = new BoundingBox( entity.BoundingBox.Min, new Vector3( entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z + entity.Velocity.Z)); negative = false; } double? collisionExtent = null; for (int x = (int)(Math.Floor(testBox.Min.X)); x <= (int)(Math.Ceiling(testBox.Max.X)); x++) { for (int z = (int)(Math.Floor(testBox.Min.Z)); z <= (int)(Math.Ceiling(testBox.Max.Z)); z++) { for (int y = (int)(Math.Floor(testBox.Min.Y)); y <= (int)(Math.Ceiling(testBox.Max.Y)); y++) { var coords = new Coordinates3D(x, y, z); if (!World.IsValidPosition(coords)) continue; var _box = BlockPhysicsProvider.GetBoundingBox(World, coords); if (_box == null) continue; var box = _box.Value.OffsetBy(coords); if (testBox.Intersects(box)) { if (negative) { if (collisionExtent == null || collisionExtent.Value < box.Max.Z) { collisionExtent = box.Max.Z; collisionPoint = coords; } } else { if (collisionExtent == null || collisionExtent.Value > box.Min.Z) { collisionExtent = box.Min.Z; collisionPoint = coords; } } } } } } if (collisionExtent != null) // Collision detected, adjust accordingly { var extent = collisionExtent.Value; double diff; if (negative) diff = -(entity.BoundingBox.Min.Z - extent); else diff = extent - entity.BoundingBox.Max.Z; entity.Velocity = new Vector3(entity.Velocity.X, entity.Velocity.Y, diff); return true; } return false; }
/// <summary> /// Performs terrain collision tests and adjusts the Z-axis velocity accordingly /// </summary> /// <returns>True if the entity collides with the terrain</returns> private bool AdjustVelocityZ(IAABBEntity entity, ReadOnlyWorld world, out Vector3 collision, out Vector3 collisionDirection) { collision = Vector3.Zero; collisionDirection = Vector3.Zero; if (entity.Velocity.Z == 0) { return(false); } // Do some enviornment guessing to improve speed int minX = (int)entity.Position.X - (entity.Position.X < 0 ? 1 : 0); int maxX = (int)(entity.Position.X + entity.Size.Depth) - (entity.Position.X < 0 ? 1 : 0); int minY = (int)entity.Position.Y - (entity.Position.Y < 0 ? 1 : 0); int maxY = (int)(entity.Position.Y + entity.Size.Width) - (entity.Position.Y < 0 ? 1 : 0); int minZ, maxZ; // Expand bounding box to include area to be tested if (entity.Velocity.Z < 0) { TempBoundingBox = new BoundingBox( new Vector3(entity.BoundingBox.Min.X, entity.BoundingBox.Min.Y, entity.BoundingBox.Min.Z + entity.Velocity.Z), entity.BoundingBox.Max); maxZ = (int)(TempBoundingBox.Max.Z); minZ = (int)(TempBoundingBox.Min.Z + entity.Velocity.Z) - 1; } else { TempBoundingBox = new BoundingBox( entity.BoundingBox.Min, new Vector3(entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z + entity.Velocity.Z) ); minZ = (int)(entity.BoundingBox.Min.Z); maxZ = (int)(entity.BoundingBox.Max.Z + entity.Velocity.Z) + 1; } // Do terrain checks double? collisionPoint = null; BoundingBox blockBox; for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { for (int z = minZ; z <= maxZ; z++) { var position = new Coordinates3D(x, y, z); var boundingBox = BlockPhysicsProvider.GetBoundingBox(world.World, position); if (boundingBox == null) { continue; } blockBox = boundingBox.Value.OffsetBy(position + new Vector3(0.5)); if (TempBoundingBox.Intersects(blockBox)) { if (entity.Velocity.Z < 0) { if (!collisionPoint.HasValue) { collisionPoint = blockBox.Max.Z; } else if (collisionPoint.Value < blockBox.Max.Z) { collisionPoint = blockBox.Max.Z; } } else { if (!collisionPoint.HasValue) { collisionPoint = blockBox.Min.Z; } else if (collisionPoint.Value > blockBox.Min.Z) { collisionPoint = blockBox.Min.Z; } } collision = position; } } } } if (collisionPoint != null) { if (entity.Velocity.Z < 0) { entity.Velocity = new Vector3( entity.Velocity.X, entity.Velocity.Y, entity.Velocity.Z - (TempBoundingBox.Min.Z - collisionPoint.Value)); collisionDirection = Vector3.Backwards; } else if (entity.Velocity.Z > 0) { entity.Velocity = new Vector3( entity.Velocity.X, entity.Velocity.Y, entity.Velocity.Z - (TempBoundingBox.Max.Z - collisionPoint.Value)); collisionDirection = Vector3.Forwards; } return(true); } return(false); }
/// <summary> /// Performs terrain collision tests and adjusts the Y-axis velocity accordingly /// </summary> /// <returns>True if the entity collides with the terrain</returns> private bool AdjustVelocityY(IAABBEntity entity, ReadOnlyWorld world, out Vector3 collision, out Vector3 collisionDirection) { collision = Vector3.Zero; collisionDirection = Vector3.Zero; if (entity.Velocity.Y == 0) { return(false); } // Do some enviornment guessing to improve speed int minX = (int)entity.Position.X - (entity.Position.X < 0 ? 1 : 0); int maxX = (int)(entity.Position.X + entity.Size.Width) - (entity.Position.X < 0 ? 1 : 0); int minZ = (int)entity.Position.Z - (entity.Position.Z < 0 ? 1 : 0); int maxZ = (int)(entity.Position.Z + entity.Size.Depth) - (entity.Position.Z < 0 ? 1 : 0); int minY, maxY; // Expand bounding box to include area to be tested if (entity.Velocity.Y < 0) { TempBoundingBox = new BoundingBox( new Vector3(entity.BoundingBox.Min.X, entity.BoundingBox.Min.Y + entity.Velocity.Y, entity.BoundingBox.Min.Z), entity.BoundingBox.Max); maxY = (int)(TempBoundingBox.Max.Y); minY = (int)(TempBoundingBox.Min.Y + entity.Velocity.Y) - 1; } else { TempBoundingBox = new BoundingBox( entity.BoundingBox.Min, new Vector3(entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y + entity.Velocity.Y, entity.BoundingBox.Max.Z)); minY = (int)(entity.BoundingBox.Min.Y); maxY = (int)(entity.BoundingBox.Max.Y + entity.Velocity.Y) + 1; } // Clamp Y into map boundaries if (minY < 0) { minY = 0; } if (minY >= TrueCraft.Core.World.World.Height) { minY = TrueCraft.Core.World.World.Height - 1; } if (maxY < 0) { maxY = 0; } if (maxY >= TrueCraft.Core.World.World.Height) { maxY = TrueCraft.Core.World.World.Height - 1; } // Do terrain checks double? collisionPoint = null; BoundingBox blockBox; for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { for (int z = minZ; z <= maxZ; z++) { var position = new Coordinates3D(x, y, z); if (!World.IsValidPosition(position)) { continue; } var boundingBox = BlockPhysicsProvider.GetBoundingBox(world.World, position); if (boundingBox == null) { continue; } blockBox = boundingBox.Value.OffsetBy(position + new Vector3(0.5)); if (TempBoundingBox.Intersects(blockBox)) { if (entity.Velocity.Y < 0) { if (!collisionPoint.HasValue) { collisionPoint = blockBox.Max.Y; } else if (collisionPoint.Value < blockBox.Max.Y) { collisionPoint = blockBox.Max.Y; } } else { if (!collisionPoint.HasValue) { collisionPoint = blockBox.Min.Y; } else if (collisionPoint.Value > blockBox.Min.Y) { collisionPoint = blockBox.Min.Y; } } collision = position; } } } } if (collisionPoint != null) { if (entity.Velocity.Y < 0) { // TODO: Do block event //var block = world.GetBlock(collision); //block.OnBlockWalkedOn(world, collision, this); entity.Velocity = new Vector3(entity.Velocity.X, entity.Velocity.Y + (collisionPoint.Value - TempBoundingBox.Min.Y), entity.Velocity.Z); collisionDirection = Vector3.Down; } else if (entity.Velocity.Y > 0) { entity.Velocity = new Vector3(entity.Velocity.X, entity.Velocity.Y - (TempBoundingBox.Max.Y - collisionPoint.Value), entity.Velocity.Z); collisionDirection = Vector3.Up; } return(true); } return(false); }
public void UpdateState(IAABBEntity entity, Transform camera, out float velocityMultiplier) { CheckHead(entity, camera); velocityMultiplier = CheckBody(entity); }
public static void EntityDestroy(this BlockData block, IWorld world, IAABBEntity entity, LuaTable context) { world.BlockDataTable.GetBlockBehaviour(block.ID)?.entity_destroy(entity, context); }
public static void EntityFixedUpdate(this BlockData block, IWorld world, IAABBEntity entity, LuaTable context) { world.BlockDataTable.GetBlockBehaviour(block.ID)?.entity_fixed_update(entity, context); }
public static void EntityOnCollisions(this BlockData block, IWorld world, IAABBEntity entity, CollisionFlags flags, LuaTable context) { world.BlockDataTable.GetBlockBehaviour(block.ID)?.entity_on_collisions(entity, flags, context); }
// TODO: There's a lot of code replication here, perhaps it can be consolidated /// <summary> /// Performs terrain collision tests and adjusts the X-axis velocity accordingly /// </summary> /// <returns>True if the entity collides with the terrain</returns> private bool AdjustVelocityX(IAABBEntity entity, World world, out Vector3 collision, out Vector3 collisionDirection) { collision = Vector3.Zero; collisionDirection = Vector3.Zero; if (entity.Velocity.X == 0) return false; // Do some enviornment guessing to improve speed int minY = (int)entity.Position.Y - (entity.Position.Y < 0 ? 1 : 0); int maxY = (int)(entity.Position.Y + entity.Size.Width) - (entity.Position.Y < 0 ? 1 : 0); int minZ = (int)entity.Position.Z - (entity.Position.Z < 0 ? 1 : 0); int maxZ = (int)(entity.Position.Z + entity.Size.Depth) - (entity.Position.Z < 0 ? 1 : 0); int minX, maxX; // Expand bounding box to include area to be tested if (entity.Velocity.X < 0) { TempBoundingBox = new BoundingBox( new Vector3(entity.BoundingBox.Min.X + entity.Velocity.X, entity.BoundingBox.Min.Y, entity.BoundingBox.Min.Z) - (entity.Size / 2), new Vector3(entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z) - (entity.Size / 2) ); maxX = (int)(TempBoundingBox.Max.X); minX = (int)(TempBoundingBox.Min.X + entity.Velocity.X); } else { TempBoundingBox = new BoundingBox( entity.BoundingBox.Min - (entity.Size / 2), new Vector3( entity.BoundingBox.Max.X + entity.Velocity.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z) - (entity.Size / 2) ); minX = (int)(entity.BoundingBox.Min.X); maxX = (int)(entity.BoundingBox.Max.X + entity.Velocity.X); } // Do terrain checks double? collisionPoint = null; BoundingBox blockBox; for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { for (int z = minZ; z <= maxZ; z++) { var position = new Vector3(x, y, z); var boundingBox = BlockPhysicsProvider.GetBoundingBox(world, (Coordinates3D)position); if (boundingBox == null) continue; blockBox = boundingBox.Value.OffsetBy(position); if (TempBoundingBox.Intersects(blockBox)) { if (entity.Velocity.X < 0) { if (!collisionPoint.HasValue) collisionPoint = blockBox.Max.X; else if (collisionPoint.Value < blockBox.Max.X) collisionPoint = blockBox.Max.X; } else { if (!collisionPoint.HasValue) collisionPoint = blockBox.Min.X; else if (collisionPoint.Value > blockBox.Min.X) collisionPoint = blockBox.Min.X; } collision = position; } } } } if (collisionPoint != null) { if (entity.Velocity.X < 0) { entity.Velocity = new Vector3( entity.Velocity.X - (TempBoundingBox.Min.X - collisionPoint.Value), entity.Velocity.Y, entity.Velocity.Z); collisionDirection = Vector3.Left; } else if (entity.Velocity.X > 0) { entity.Velocity = new Vector3( entity.Velocity.X - (TempBoundingBox.Max.X - collisionPoint.Value), entity.Velocity.Y, entity.Velocity.Z); collisionDirection = Vector3.Right; } return true; } return false; }
public bool TestTerrainCollisionZ(IAABBEntity entity, out Vector3 collisionPoint) { // Things we need to do: // 1 - expand bounding box to include the destination and everything within // 2 - collect all blocks within that area // 3 - test bounding boxes in direction of motion collisionPoint = Vector3.Zero; if (entity.Velocity.Z == 0) { return(false); } bool negative; BoundingBox testBox; if (entity.Velocity.Z < 0) { testBox = new BoundingBox( new Vector3( entity.BoundingBox.Min.X, entity.BoundingBox.Min.Y, entity.BoundingBox.Min.Z + entity.Velocity.Z), entity.BoundingBox.Max); negative = true; } else { testBox = new BoundingBox( entity.BoundingBox.Min, new Vector3( entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z + entity.Velocity.Z)); negative = false; } double?collisionExtent = null; for (var x = (int)Math.Floor(testBox.Min.X); x <= (int)Math.Ceiling(testBox.Max.X); x++) { for (var z = (int)Math.Floor(testBox.Min.Z); z <= (int)Math.Ceiling(testBox.Max.Z); z++) { for (var y = (int)Math.Floor(testBox.Min.Y); y <= (int)Math.Ceiling(testBox.Max.Y); y++) { var coords = new Coordinates3D(x, y, z); if (!World.IsValidPosition(coords)) { continue; } var _box = BlockPhysicsProvider.GetBoundingBox(World, coords); if (_box == null) { continue; } var box = _box.Value.OffsetBy(coords.AsVector3()); if (testBox.Intersects(box)) { if (negative) { if (collisionExtent == null || collisionExtent.Value < box.Max.Z) { collisionExtent = box.Max.Z; collisionPoint = coords.AsVector3(); } } else { if (collisionExtent == null || collisionExtent.Value > box.Min.Z) { collisionExtent = box.Min.Z; collisionPoint = coords.AsVector3(); } } } } } } if (collisionExtent != null) // Collision detected, adjust accordingly { var extent = collisionExtent.Value; double diff; if (negative) { diff = -(entity.BoundingBox.Min.Z - extent); } else { diff = extent - entity.BoundingBox.Max.Z; } entity.Velocity = new Vector3(entity.Velocity.X, entity.Velocity.Y, (float)diff); return(true); } return(false); }