public static bool RaycastBlock(Vector3 origin, Vector3 direction, float maxDistance, IWorld world, Func <BlockData, bool> selectBlock, out BlockRaycastHit hit)
 {
     return(RaycastBlock(origin, direction * maxDistance, world, selectBlock, out hit));
 }
        public static bool RaycastBlock(Vector3 start, Vector3 end, IWorld world, Func <BlockData, bool> selectBlock, out BlockRaycastHit hit)
        {
            int startX = Mathf.FloorToInt(start.x);
            int startY = Mathf.FloorToInt(start.y);
            int startZ = Mathf.FloorToInt(start.z);

            int endX = Mathf.FloorToInt(end.x);
            int endY = Mathf.FloorToInt(end.y);
            int endZ = Mathf.FloorToInt(end.z);

            int count = 200; // 上限200个方块,防止死循环

            while ((startX != endX || startY != endY || startZ != endZ) && (count-- > 0))
            {
                float newX = startX;
                float newY = startY;
                float newZ = startZ;

                if (endX > startX)
                {
                    newX += 1;
                }

                if (endY > startY)
                {
                    newY += 1;
                }

                if (endZ > startZ)
                {
                    newZ += 1;
                }

                float xt = float.PositiveInfinity;
                float yt = float.PositiveInfinity;
                float zt = float.PositiveInfinity;

                float dx = end.x - start.x;
                float dy = end.y - start.y;
                float dz = end.z - start.z;

                // 向X方向选了候选方块
                if (endX != startX)
                {
                    xt = (newX - start.x) / dx;
                }

                if (endY != startY)
                {
                    yt = (newY - start.y) / dy;
                }

                if (endZ != startZ)
                {
                    zt = (newZ - start.z) / dz;
                }

                // 最终选了哪个方向的候选方块
                BlockFace direction;

                // 选出候选方块中离起点(当前)最近的,更新起点、要检测的方块坐标
                if (xt < yt && xt < zt)
                {
                    start.x  = newX;
                    start.y += dy * xt;
                    start.z += dz * xt;

                    direction = endX > startX ? BlockFace.PositiveX : BlockFace.NegativeX;
                }
                else if (yt < zt)
                {
                    start.x += dx * yt;
                    start.y  = newY;
                    start.z += dz * yt;

                    direction = endY > startY ? BlockFace.PositiveY : BlockFace.NegativeY;
                }
                else
                {
                    start.x += dx * zt;
                    start.y += dy * zt;
                    start.z  = newZ;

                    direction = endZ > startZ ? BlockFace.PositiveZ : BlockFace.NegativeZ;
                }

                startX = Mathf.FloorToInt(start.x);
                startY = Mathf.FloorToInt(start.y);
                startZ = Mathf.FloorToInt(start.z);

                switch (direction)
                {
                case BlockFace.NegativeX:
                    startX--;     // 以方块内各轴最小坐标为方块坐标,这里得到的是X上最大坐标所以要-1
                    break;

                case BlockFace.NegativeY:
                    startY--;
                    break;

                case BlockFace.NegativeZ:
                    startZ--;
                    break;
                }

                // 检测新起点方块
                Vector3Int pos   = new Vector3Int(startX, startY, startZ);
                BlockData  block = world.RWAccessor.GetBlock(startX, startY, startZ);
                // AABB? boundingBox = block.GetBoundingBox(pos, world);

                if (!selectBlock(block))
                {
                    continue;
                }

                Vector3 normal = direction switch
                {
                    BlockFace.PositiveX => Vector3.left,
                    BlockFace.NegativeX => Vector3.right,
                    BlockFace.PositiveY => Vector3.down,
                    BlockFace.NegativeY => Vector3.up,
                    BlockFace.PositiveZ => Vector3.back,
                    BlockFace.NegativeZ => Vector3.forward,
                    _ => default
                };

                hit = new BlockRaycastHit(pos, normal, world, block);
                return(true);
            }

            hit = default;
            return(false);
        }
 /// <summary>
 /// 方块射线检测 (start, end]
 /// </summary>
 /// <param name="ray"></param>
 /// <param name="maxDistance"></param>
 /// <param name="world"></param>
 /// <param name="selectBlock"></param>
 /// <param name="hit"></param>
 /// <see cref="https://blog.csdn.net/xfgryujk/article/details/52948543"/>
 /// <returns></returns>
 public static bool RaycastBlock(Ray ray, float maxDistance, IWorld world, Func <BlockData, bool> selectBlock, out BlockRaycastHit hit)
 {
     return(RaycastBlock(ray.origin, ray.origin + ray.direction * maxDistance, world, selectBlock, out hit));
 }