protected virtual void GenerateCollisionBoxList(IBlockAccessor blockAccessor, double motionX, double motionY, double motionZ, float stepHeight, float yExtra) { minPos.Set( (int)(entityBox.X1 + Math.Min(0, motionX)), (int)(entityBox.Y1 + Math.Min(0, motionY) - yExtra), // yExtra for the extra high collision box of fences (int)(entityBox.Z1 + Math.Min(0, motionZ)) ); double y2 = Math.Max(entityBox.Y1 + stepHeight, entityBox.Y2); maxPos.Set( (int)(entityBox.X2 + Math.Max(0, motionX)), (int)(y2 + Math.Max(0, motionY)), (int)(entityBox.Z2 + Math.Max(0, motionZ)) ); CollisionBoxList.Clear(); blockAccessor.WalkBlocks(minPos, maxPos, (block, bpos) => { Cuboidf[] collisionBoxes = block.GetCollisionBoxes(blockAccessor, bpos); if (collisionBoxes != null) { for (int i = 0; i < collisionBoxes.Length; i++) { CollisionBoxList.Add(collisionBoxes[i], bpos, block); } } }, true); }
/// <summary> /// Get all blocks colliding with entityBoxRel /// </summary> /// <param name="blockAccessor"></param> /// <param name="entityBoxRel"></param> /// <param name="pos"></param> /// <param name="blocks">The found blocks</param> /// <param name="alsoCheckTouch"></param> /// <returns>If any blocks have been found</returns> public bool GetCollidingCollisionBox(IBlockAccessor blockAccessor, Cuboidf entityBoxRel, Vec3d pos, out CachedCuboidList blocks, bool alsoCheckTouch = true) { blocks = new CachedCuboidList(); BlockPos blockPos = new BlockPos(); Vec3d blockPosVec = new Vec3d(); Cuboidd entityBox = entityBoxRel.ToDouble().Translate(pos); int minX = (int)(entityBoxRel.MinX + pos.X); int minY = (int)(entityBoxRel.MinY + pos.Y - 1); // -1 for the extra high collision box of fences int minZ = (int)(entityBoxRel.MinZ + pos.Z); int maxX = (int)Math.Ceiling(entityBoxRel.MaxX + pos.X); int maxY = (int)Math.Ceiling(entityBoxRel.MaxY + pos.Y); int maxZ = (int)Math.Ceiling(entityBoxRel.MaxZ + pos.Z); for (int y = minY; y <= maxY; y++) { for (int x = minX; x <= maxX; x++) { for (int z = minZ; z <= maxZ; z++) { Block block = blockAccessor.GetBlock(x, y, z); blockPos.Set(x, y, z); blockPosVec.Set(x, y, z); Cuboidf[] collisionBoxes = block.GetCollisionBoxes(blockAccessor, blockPos); for (int i = 0; collisionBoxes != null && i < collisionBoxes.Length; i++) { Cuboidf collBox = collisionBoxes[i]; if (collBox == null) { continue; } bool colliding = alsoCheckTouch ? entityBox.IntersectsOrTouches(collBox, blockPosVec) : entityBox.Intersects(collBox, blockPosVec); if (colliding) { blocks.Add(collBox, x, y, z, block); } } } } } return(blocks.Count > 0); }
public void ApplyTerrainCollision(Entity entity, EntityPos entitypos, float dtFac, ref Vec3d outposition, float stepHeight = 1) { IWorldAccessor worldaccess = entity.World; Vec3d pos = entitypos.XYZ; // Full stop when already inside a collisionbox, but allow a small margin of error // Disabled. Causes too many issues /*(if (blockWhenInside && IsColliding(worldaccess.BlockAccessor, entity.CollisionBox, pos, false)) * { * entity.CollidedVertically = true; * entity.CollidedVertically = true; * outposition.Set(pos); * return; * }*/ // entitypos.Motion.X = GameMath.Clamp(entitypos.Motion.X, -1, 1); // entitypos.Motion.Y = GameMath.Clamp(entitypos.Motion.Y, -1, 1); // entitypos.Motion.Z = GameMath.Clamp(entitypos.Motion.Z, -1, 1); tmpPositionVec.Set(pos); entityBox.Set( entity.CollisionBox.X1 + tmpPositionVec.X, entity.CollisionBox.Y1 + tmpPositionVec.Y, entity.CollisionBox.Z1 + tmpPositionVec.Z, entity.CollisionBox.X2 + tmpPositionVec.X, entity.CollisionBox.Y2 + tmpPositionVec.Y, entity.CollisionBox.Z2 + tmpPositionVec.Z ); CollisionBoxList.Clear(); outposition.Set(tmpPositionVec.X + entitypos.Motion.X * dtFac, tmpPositionVec.Y + entitypos.Motion.Y * dtFac, tmpPositionVec.Z + entitypos.Motion.Z * dtFac); minPos.Set( (int)(entityBox.X1 + Math.Min(0, entitypos.Motion.X * dtFac)), (int)(entityBox.Y1 + Math.Min(0, entitypos.Motion.Y * dtFac) - 1), // -1 for the extra high collision box of fences (int)(entityBox.Z1 + Math.Min(0, entitypos.Motion.Z * dtFac)) ); double y2 = Math.Max(entityBox.Y1 + stepHeight, entityBox.Y2); maxPos.Set( (int)(entityBox.X2 + Math.Max(0, entitypos.Motion.X * dtFac)), (int)(y2 + Math.Max(0, entitypos.Motion.Y * dtFac)), (int)(entityBox.Z2 + Math.Max(0, entitypos.Motion.Z * dtFac)) ); worldaccess.BlockAccessor.WalkBlocks(minPos, maxPos, (block, bpos) => { Cuboidf[] collisionBoxes = block.GetCollisionBoxes(worldaccess.BlockAccessor, bpos); for (int i = 0; collisionBoxes != null && i < collisionBoxes.Length; i++) { CollisionBoxList.Add(collisionBoxes[i], bpos, block); } }, true); tmpPosDelta.Set(entitypos.Motion.X * dtFac, entitypos.Motion.Y * dtFac, entitypos.Motion.Z * dtFac); // Y - Collision (Vertical) bool collided = false; for (int i = 0; i < CollisionBoxList.Count; i++) { tmpPosDelta.Y = CollisionBoxList.cuboids[i].pushOutY(entityBox, tmpPosDelta.Y, ref pushDirection); if (pushDirection == EnumPushDirection.None) { continue; } collided = true; CollisionBoxList.blocks[i].OnEntityCollide( worldaccess, entity, CollisionBoxList.positions[i], pushDirection == EnumPushDirection.Negative ? BlockFacing.UP : BlockFacing.DOWN, tmpPosDelta, !entity.CollidedVertically ); } entity.CollidedVertically = collided; entityBox.Translate(0, tmpPosDelta.Y, 0); var horizontalBlocked = false; var entityBoxMovedXZ = entityBox.OffsetCopy(tmpPosDelta.X, 0, tmpPosDelta.Z); foreach (var cuboid in CollisionBoxList.cuboids) { if (cuboid.Intersects(entityBoxMovedXZ)) { horizontalBlocked = true; break; } } // No collisions for the entity found when testing horizontally, so skip this. // This allows entities to move around corners without falling down on a certain axis. collided = false; if (horizontalBlocked) { // X - Collision (Horizontal) for (int i = 0; i < CollisionBoxList.Count; i++) { tmpPosDelta.X = CollisionBoxList.cuboids[i].pushOutX(entityBox, tmpPosDelta.X, ref pushDirection); if (pushDirection == EnumPushDirection.None) { continue; } collided = true; CollisionBoxList.blocks[i].OnEntityCollide( worldaccess, entity, CollisionBoxList.positions[i], pushDirection == EnumPushDirection.Negative ? BlockFacing.EAST : BlockFacing.WEST, tmpPosDelta, !entity.CollidedHorizontally ); } entityBox.Translate(tmpPosDelta.X, 0, 0); // Z - Collision (Horizontal) for (int i = 0; i < CollisionBoxList.Count; i++) { tmpPosDelta.Z = CollisionBoxList.cuboids[i].pushOutZ(entityBox, tmpPosDelta.Z, ref pushDirection); if (pushDirection == EnumPushDirection.None) { continue; } collided = true; CollisionBoxList.blocks[i].OnEntityCollide( worldaccess, entity, CollisionBoxList.positions[i], pushDirection == EnumPushDirection.Negative ? BlockFacing.SOUTH : BlockFacing.NORTH, tmpPosDelta, !entity.CollidedHorizontally ); } } entity.CollidedHorizontally = collided; //fix for player on ladder clipping into block above issue (caused by the .CollisionBox not always having height precisely 1.85) if (entity.CollidedVertically && tmpPosDelta.Y > 0) { tmpPosDelta.Y -= entity.LadderFixDelta; } outposition.Set(tmpPositionVec.X + tmpPosDelta.X, tmpPositionVec.Y + tmpPosDelta.Y, tmpPositionVec.Z + tmpPosDelta.Z); }
/// <summary> /// Updates the velocity vector according to the amount of passed time, gravity and terrain collision. /// </summary> /// <param name="pos"></param> /// <param name="motion"></param> /// <param name="size"></param> /// <returns></returns> public EnumCollideFlags UpdateMotion(Vec3d pos, Vec3f motion, float size) { particleCollBox.Set( pos.X - size / 2, pos.Y - 0 / 2, pos.Z - size / 2, pos.X + size / 2, pos.Y + size / 2, pos.Z + size / 2 ); motion.X = GameMath.Clamp(motion.X, -MotionCap, MotionCap); motion.Y = GameMath.Clamp(motion.Y, -MotionCap, MotionCap); motion.Z = GameMath.Clamp(motion.Z, -MotionCap, MotionCap); EnumCollideFlags flags = 0; minPos.Set( (int)(particleCollBox.X1 + Math.Min(0, motion.X)), (int)(particleCollBox.Y1 + Math.Min(0, motion.Y) - 1), // -1 for the extra high collision box of fences (int)(particleCollBox.Z1 + Math.Min(0, motion.Z)) ); maxPos.Set( (int)(particleCollBox.X2 + Math.Max(0, motion.X)), (int)(particleCollBox.Y2 + Math.Max(0, motion.Y)), (int)(particleCollBox.Z2 + Math.Max(0, motion.Z)) ); CollisionBoxList.Clear(); BlockAccess.WalkBlocks(minPos, maxPos, (cblock, bpos) => { Cuboidf[] collisionBoxes = cblock.GetParticleCollisionBoxes(BlockAccess, bpos); for (int i = 0; collisionBoxes != null && i < collisionBoxes.Length; i++) { CollisionBoxList.Add(collisionBoxes[i], bpos, cblock); } }, false); // Y - Collision (Vertical) EnumPushDirection pushDirection = EnumPushDirection.None; for (int i = 0; i < CollisionBoxList.Count; i++) { blockCollBox = CollisionBoxList.cuboids[i]; motion.Y = (float)blockCollBox.pushOutY(particleCollBox, motion.Y, ref pushDirection); if (pushDirection != EnumPushDirection.None) { flags |= EnumCollideFlags.CollideY; } } particleCollBox.Translate(0, motion.Y, 0); // X - Collision (Horizontal) for (int i = 0; i < CollisionBoxList.Count; i++) { blockCollBox = CollisionBoxList.cuboids[i]; motion.X = (float)blockCollBox.pushOutX(particleCollBox, motion.X, ref pushDirection); if (pushDirection != EnumPushDirection.None) { flags |= EnumCollideFlags.CollideX; } } particleCollBox.Translate(motion.X, 0, 0); // Z - Collision (Horizontal) for (int i = 0; i < CollisionBoxList.Count; i++) { blockCollBox = CollisionBoxList.cuboids[i]; motion.Z = (float)blockCollBox.pushOutZ(particleCollBox, motion.Z, ref pushDirection); if (pushDirection != EnumPushDirection.None) { flags |= EnumCollideFlags.CollideZ; } } return(flags); }