Why is there no LineCast?

Godot Version

4.6

Question

Hi, I’m trying to understand why godot has RayCast and ShapeCast, but there’s no such thing as a LineCast. Put it differently, if ShapeCast3D is used to detect a volume by casting a shape along a vector, couldn’t you detect an area in 2d by casting a line along a vector? Why am I specifying the depth of the line when only the front edge actually matters? If a raycast is casting a point along a vector to get 1D detection, then casting a line along a vector would give you 2D detection no?

Is there some weird reason behind the scenes that you specify not only the leading line but the entire shape? Maybe I’m way off base, but this is something that really irks me because I always end up overthinking how much depth the shape should have when I don’t think it should actually be defined at all in the first place.

Hi,

To me it sounds like you are describing a RayCast. :thinking:
Is it possible, that you used another engine that used different terminolgy for those?

6 Likes

What’s your use case? What are you running into so commonly that requires a new type of ray cast? Can you draw a picture of how line cast differs from a raycast or shapecast? It sounds like a ray cast to me, or a thin-box shape cast?

4 Likes

I think what they’re describing is dragging a segment defined by two points sideways through 2D space, basically this:

I guess the answer would be “ShapeCast2D is already a generalized case of this and there isn’t much benefit to making a specialized version”? As a side note, this also points to a missing “ShapeCast3D2D” sort of primitive, which would involve dragging a flat shape through 3D space.

Here’s a drawn example of what I’m trying to get at. I don’t understand why godot has you define an entire shape (or volume for 3D) for casting when only the leading edge (or plane for 3D) would ever actually hit anything. Why am I defining a depth when that depth is along the same dimension as the ray it’s being cast along?

I’ll be honest, I kinda thought that the Shapecast3D was dragging a flat shape through 3D space, but apparently they just use the word shape to describe a 3D volume.

Someone asked about use case. The use case would be pretty much anything you would use a shapecast for. Usually the primary thing I’m looking to do with this is a specific form of ground detection.

As I said, there might be a good reason for this that I’m just not seeing, and if someone can enlighten me on the subject it would be greatly appreciated, but I don’t really get it right now.

1 Like

There’s no gradual linear movement of the shape/point involved in shape or ray casting, so there’s no “leading edge”. In very simplified terms, the whole sweeping volume is calculated beforehand and intersected with the world. Ditto for ray casting. It’s not moving a point along a line. That’d be inefficient and imprecise. The ray is mathematically defined and intersected with the world instantly.

What you propose is just a special case of casting a box/rectangle. So simply use that with the box/rect shape sized to best fit your purpose, or use multiple raycasts.

If you’re interested in learning how it works in more detail, afaik, Godot/Jolt use GJK (Gilbert-Johnson-Keerthi) algorithm for shape casting. So those are your keywords for looking things up further.

6 Likes

FWIW, under the hood shape casting consists of two calls: cast_motion to find the earliest collision point (skipped if target_position is a zero vector) + get_rest_info to find what you end up colliding with. For a simple case where a shape is a segment in 2D/polygon in 3D you might be able to get away with generating the resulting area/volume shape yourself and calling just get_rest_info on that, although this will give you everything you’d collide with in the resulting volume, not just the closest object (of course, it’s trivial to find that afterwards). And don’t forget to profile.

Thanks for the info. I still don’t quite understand, but I’ll try to get back to you guys after reading up on this some more.

In my head, even if it isn’t a gradual linear motion, there still has to be a part of the shape (or volume) that hits something first, which is the info the cast provides. That first hit(or hits) also would always have to be along this concept of a leading edge/plane that I have.

But as I said, given that this is how both Godot and Unity do this, my mental model is probably just fundamentally misunderstanding something. Given that, I’m going to go read up on this more, thanks for the help guys!

1 Like

Consider as well that it is useful to implement a 3D shape cast, and that functionally the volume of the shape does not matter if sweeped across a parallel axis, the front face will always hit first. So why implement both if a 2D shape sweeping in 3D space is functionally handled as-is?

2 Likes

Yeah so this is actually really explained by physics, and how RayCasts and ShapeCasts work. As @normalized said, you are thinking they both move through space. They don’t. They just exist. Let me explain.

Godot has a physics process that is framerate dependent and runs 60 times per second - or every ~16.6667 milliseconds. Each of those is called a frame. Every frame, the entire world is frozen in time, like in a movie.

During each frame, a RayCast checks if anything along it collides. A RayCast is not a point in space that moves, but a line that exists between two points, and collisions are checked starting at the origin and moving outward.

During each frame, a ShapeCast checks if anything inside the area it describes (whether 2D or 3D) collides with it, doing a sweep.

During that frame, if you query either object for its collision data, you can specify how many collisions you want, from zero to all of them.


So from the point of view of a player, anything that intersects those (invisible) projects collides instantaneously.

To do ground detection, you just shoot a RayCast down into the ground. Except, for most common cases, you don’t need to do this. Godot PhysicsBody nodes know what direction the ground is because of gravity and know how to interact with both gravity and the ground without you doing anything.

Instead, a RayCast is more often used for edge detection. For example, you want to see if an enemy has reached the edge of a cliff so you can turn them around. Or the player has reached the edge of stairs and you want to fake them being able to smoothly walk up them. They are also great for simple games where things like bullets and lasers hit instantaneously.

A ShapeCast is most useful when you want to detect something inside the area it describes, instead of on the edge. (Though it can be used for both.) So lets say you have a slow cloud spell, and every round, you want to see if someone is still in the area of the spell, and if so reduce their movement rate by half. A ShapeCast can be queried every round to say, “Who’s still here?”

Like @gertkeno said, you can use a ShapeCast for edge detection, but Godot also has Area2D and Area3D nodes. I think they are much more what you are describing. Lets say you have an Enemy and you want to know if someone gets within 1500 pixels (2D) or 15 meters (3D) of them. You can create an Area2D with a CircleShape or an Area3D with a SphereShape. When the Player breaks that edge, they will trigger a body_entered event. Using the same example above, we could just apply a slow effect to the Player, and leave it there until the spell expires, or the Player triggers a body_exited event. This means we don’t have to query collisions in the shape every frame. We are just doing edge detection - which I think is what you want.

There are a few cases in which edge detection with an Area2D does not work, and are related to the object it is trying to detect moving so fast that it teleports over it. In these cases, it’s usually better to use PhysicsBody objects.

Conclusion

Basically, you are thinking of RayCasts and ShapeCasts as moving through time, and they function as frozen objects in time 60 times a second. The only way they move is if you move them.

Okay, I’ll try to identify where I’m still struggling.

I understand that RayCast runs every physics step, and that during so everything is frozen. I also understand that RayCast is not a point, but instead a line between two points, the origin of the ray and the endpoint of the ray. However, as you said, it checks for collisions moving from the origin to the endpoint. I was equating this collision check to moving a point from the origin to the endpoint and returning the first spot where something hits it.

Correct me if I’m wrong, but it would be silly to define a depth along with the origin and endpoint, no? Essentially defining the origin, the length of a line from the origin, and then the endpoint of the ray. That wouldn’t make any sense to define an arbitrary length from the origin along with an endpoint.

From my perspective ShapeCast2D (and 3D) are doing exactly that for the equivalent areas. They’re over-defining what is actually needed. They define a full shape (which has depth along the same line it’s being cast), and then cast it along that direction and finding the first spot where it would hit something. Keep in mind I know it’s not actually moving, that’s just equivalent to what the check each frame is doing.

After considering it more, I think the most logical way to fix this would be changing the ray to a direction rather than the shape to a line. The user could define a shape and a direction, not an entire ray. This would let you still be able to sweep literally any shape without over-defining the cast.

Here’s a concrete example of what I mean. Maybe this will either help me clarify what I’m talking about or help you clarify what I’m missing. Here are two shape casts, is there literally any difference between these two practically speaking (apart from them being on two different x coordinates)?

The shape is not automatically aligned to the cast direction.

The same shape can be used for static collisions too so why introduce more types of “cross-section” shapes when the set of existing ones can serve well for both - static collisions as well as shapecasts. So there are really no redundancies in the whole system.

Shapecasts do represent shape motion and are used for collision of moving shapes, just that the underlying math doesn’t do any actual quantized motion. It instead uses a clever algorithm to represent completely continuous motion for the reasons of efficiency and accuracy.

I don’t follow. I’m not suggesting to introduce a new set of “cross section” shapes, I’m just suggesting that rather than using a ray and having the shape “move” along that ray, instead just define the full area of the cast with a shape and what direction it’s checking with a direction (instead of a full vector). That way there’s no redundancy/multiple ways to do the same thing as represented in the image at the end of my previous reply.

I know originally I was suggesting to give people a way to define lines and then move those lines along a ray, but I think just changing the ray to be only a direction is a much smarter way of going about it.

This would also have the added benefit of being able to use the exact same shape for an Area2D as you use for the ShapeCast in case you want to make it directional later on in a project.

I would just impose this on myself for any shapecasts I do in the future, but given how it’s programmed, I can’t since currently the vector represents a position rather than a multiplication. (so even if it’s normalized it still moves the shape). This might be enough of a lead for me to develop my own version of shapecast though now that I’ve thought through it.

That’s exactly what happens under the hood. But you suggest to define the sweeping shape explicitly which de facto is introducing a new type of shape. Remember to generalize, not just think about this in the case of a rectangular shape moving parallel to one of its axes. How would you specify a sweeping shape for a circle, or for a polygonal collider? The solution the engine currently uses is fully generalized and can sweep practically any shape.

What exact improvements would you proposal bring compared to what the engine has now?

I don’t quite understand… Why couldn’t you use the current shapes to get a “sweeped shape”? a sweeped rectangle is just a longer rectangle or a parallelogram. a sweeped circle is just a capsule. A sweeped polygon is just a polygon that’s longer on on two sides no?

The improvement is that you’re no longer making an arbitrary decision for how much depth the shape should have. I’ve seen this depth factor lead to confusion for several people in the past. The proposed suggestion is just a simpler and more direct way of defining a shapecast.

Or at least, I think it is.

Parallelogram is a new shape so you do introduce new shapes with this. And btw sweeped rectangle doesn’t form a parallelogram, it forms an irregular hexagon.

If you want to cast a “wide line” (so it seems from your examples so far), just make a very thin box and orient it to be parallel to the cast direction. It’s quite simple.

Parallelogram is just a type of convex polygon. Also yeah you’re right, it doesn’t, it forms a hexagon, which still would be a convex polygon.

Regardless, more than anything I was trying to figure out A) how exactly the shapecast works, and B) if there’s any actual reason why it seems to be over-defined, which as far as I can tell there isn’t, but that may just be me being stubborn.

It’s not over-defined. It just may look like that from your perspective, because you seem to be narrowly focused on a very specific use case, which is, if I guessed correctly, doing a “thick” raycast.

I don’t know about silly, you’d just be defining a new, closer endpoint.

Yup. It’s checking the whole shape every frame.

I believe you are seeing there is a problem where there is not one. If you want to check a line, make a ShapeCast2D one pixel wide. If you want to make a plane, use a ShapeCast3D 0.001 meters deep.

I do not understand the difference in your diagram.

They already do. They use Shape2D and Shape3D objects, and if you copy and paste a Shape2D from an Area2D to a ShapeCast2D, they share it. Editing the Shape2D in the inspector edits it for both.

The key words for me here are “arbitrary decision”. If someone is making an arbitrary decision about something like this, then the problem is that they do not understand how ShapeCasts should be used. The solution is to educate them, not create another way to use them.

In Godot, most of the time I see ShapeCast2D/3D nodes being used in a tutorial, it’s by someone coming from another engine and not understanding how Godot Area2D/3D objects work. Very rarely do you need that level of collision detection. Which I think is the central point you are trying to make.

Which is why Area2D/3D nodes exist - to solve the problem I think you’re having.