-3

I am currently writing a Minecraft clone in Unity as my pet project, and I'm trying to implement raycasts for it, so that I can know which block the player looks at (I will use raycasts for other purposes too). The world of the game is a 3D grid of perfect unit cubes. Each element of a grid is either solid or not. I want to be able to shoot a ray from any point of my world and get the point where that ray hits the surface of a first solid block in it's way.

Here's a c# pseudocode of what my game aproximately looks like:

// An aproximation of what Unity's Vector3 looks like.
public struct Vector3 
{ 
  public float x, y, z;
}

public class World 
{
  public bool[,,] blocks;
  
  public bool IsSolid(Vector3 pos)  // i.e. if pos is inside a solid block
  {
    return blocks[Math.Floor(pos.x), Math.Floor(pos.y), Math.Floor(pos.z)]
  }
  
  public Vector3 Raycast(Vector3 origin,  Vector3 direction) 
  {
    // some algorithm, that returns the point at which ray hits a solid block
  }
}
  

Note, that coordintaes of any Vector3 may not be whole nubers, it is entirely possible for rays to start (or end) at fractional coordinates. For simplicity you may (or may not) assume that world is infinite and a ray will always hit some block eventually. Remeber, that Raycast() should return the point at which the ray hits the surface of a cube.

What is the best algorithm I can use for this? My priorities (in order) are:

  1. Speed - making a raycast should be fast
  2. Elegance - the algorithm should reasonably straightforward and concise
  3. Generality - the algorithm should be easy to modify (i.e. add some extra functionality to it.)

Here's a Q/A for some possible questions:

Q: Why not use Unity's native colliders and raycasts?

A: Unity's colliders and raycast are too slow and aren't optimized for my needs, furthermore, that is by no means elegant of generic.

Q: Do you want an implementation of an algorithm or just the basic concept?

A: I'm fine with just understanding the basis of an aglorithm, but I would really apreciate an implementaion (preferably in C#).

  • What have you tried / researched so far? Share your ideas / findings / code. – MrSmith42 Nov 01 '22 at 13:38
  • 1
    You probably don't want a faster ray caster, but a better data structure, such as an oct tree to cut down in the number of intersection tests. – rotgers Nov 01 '22 at 13:47
  • @MrSmith42 Well, it's quite complicated. I do have a working solution, but it's quite messy and hard to understand. Sharing it would only confuse everyone, so I'd prefer not to, – K. A. Kusakov Nov 01 '22 at 13:49
  • @rotgers I don't really understand what do you mean exactly, could you tell me more about that, please? – K. A. Kusakov Nov 01 '22 at 13:53
  • Tbh if you had solid and nonsolid layers and raycast only on solid layers. The hit then tells you what and where it hit. Why reinvent the wheel – BugFinder Nov 01 '22 at 14:16

1 Answers1

1

Here are some basic principles. You will need to be fairly comfortable with linear algebra to even attempt this, and read and understand how ray-plane intersection works.

Your ray will start inside a cube, and it will hit one of the 6 faces on its way out of the cube. In normal cases we can quickly eliminate three of the faces by just checking if the ray direction points in the same direction as the cube-face. This is done by checking the sign of the dot product between the vectors. To find the first hit of the three remaining faces we do an intersection test to the corresponding plane, and pick the closest one, if you know the hit face you know what cube it hit. If that cube is empty you repeat the process until you find a non-empty cube. You may also add some checks to avoid edge cases, like eliminating all planes that are parallel to your ray as early as possible.

However, to get any real speed you really need some kind of tree structure to reduce the number of checks done. There are many alternatives, kd-trees, r-trees etc, but in this specific case I would probably consider a sparse octree. This means your cubes will be part of larger 2x2x2 sections, where the whole section can be tagged as empty, filled, partial filled etc. These sections will also be grouped into larger 2x2x2 sections and so on, until you hit some max size that either contains your entire play area, or one independently loadable unit of your play area, and have some logic to test multiple units if needed.

Raycasting is done more or less the same way with a octree as in the simple case, except you now have variable sized cubes. And when you hit a face you need to traverse the tree to find the next cube to test.

But to actually make a well optimized implementation of this requires quite a bit of experience, both in the concepts involved, and in the language and hardware. You may also need insight in the structure of your actual game-world, since there might be non obvious shortcuts you can take that significantly help speedup performance. So it is not guaranteed that your implementation will be faster than unitys built in.

JonasH
  • 28,608
  • 2
  • 10
  • 23