Intersect Shape - correct way to use it

Godot Version

4.2.2

Question

I made it work, but I’m not sure if that is how it should be done. I would be grateful if someone could explain what is the correct way to use it. I’m especially worried about setting the origin of the shape - tbh I have no clue what I’m doing in that part. Setting the origin for ray-casting is way easier so I thought that there must be a clearer way to do the same for IntersectShape.

Here is the code:

private void TestCast()
{
    var shape = new CircleShape2D
    {
        Radius = AlarmRadius,
    };
    var spaceState = character.GetWorld2D().DirectSpaceState;
    var query = new PhysicsShapeQueryParameters2D();
    query.Shape = shape;
    query.CollisionMask = CollisionMasks.EnemyAlarm;
    query.CollideWithAreas = false;
    query.CollideWithBodies = true;
    query.Transform = new Transform2D(0, character.GlobalPosition); // <--- is this ok? 

    var results = spaceState.IntersectShape(query, MaxAlarmed);
    foreach (var result in results)
    {
        GD.Print(result.GetCollider<Node2D>().Name);
    }
}

My use case is very simple: “Notify nearby enemies that player approached”. I know I can use other things like Area2D or ShapeCast2D to accomplish the same, but IntersectShape seemed like the best choice to me.

Hmm. It seems like very few people use Intersect Shape. Should I avoid it in favor of alternatives?

There are multiple ways to check for collisions with a shape (see this):

  • PhysicsDirectSpaceState2D.CastMotion()
  • PhysicsDirectSpaceState2D.CollideShape()
  • PhysicsDirectSpaceState2D.GetRestInfo()
  • PhysicsDirectSpaceState2D.IntersectShape()

They exhibit minor differences and I can’t say I’ve used more than one of them. I’ve come across some problems with editor scripts (see post) but I am unaware of any problems at runtime. There’s evidence that the method(s) is inaccurate (#66072, #85314) but there’s not much to do about that as a rookie user.

Whether you should use nodes or scripted shapecasting depends entirely on what you’re comfortable with and what fits your solution. If this works for you then you’re all set!

Well, you’re not doing it wrong. It’s not the position of the shape, per-say, that you’re setting; it’s the position (and orientation) of the shape query. Shapes are resources and they don’t have a transform i.e. they don’t have a position and orientation. Transform data resides in the script/node/method using the shape.

Don’t worry - you won’t accidentally reposition a node in your game.


For your own sake, I would recommend you create an extension method so you don’t have to write 15 lines of code whenever you want to perform a shapecast. You can find the extension method I use below. Feel free to modify it to fit your needs.

Shapecast extension method

This method is for 3D use. Method must be rewritten for 2D use.

	/// <summary>
	/// A Node3D extension that simplifies the usage of shapecasts.<br />
	/// NOTE: Automatically excludes this node from the shapecast.
	/// </summary>
	/// <param name="n">A node fromt the target 3DWorld.</param>
	/// <param name="shape">The Rid of the shape used for the shapecast.</param>
	/// <param name="start">The start point.</param>
	/// <param name="end">The end point.</param>
	/// <param name="excludes">An array of specific objects to exclude from the collision query.</param>
	/// <param name="collisionMask">The physics layers that the collision query tests against.</param>
	/// <returns></returns>
	public static Godot.Collections.Array<Godot.Collections.Dictionary> Shapecast(this Node3D n, Rid shapeRid, Vector3 start, Rid[] excludes = null, uint collisionMask = uint.MaxValue)
	{
		// Create a raycast query
		var spaceState = n.GetWorld3D().DirectSpaceState;
		var query = new PhysicsShapeQueryParameters3D()
		{
			ShapeRid = shapeRid,
			CollideWithBodies = true,
			Transform = new Transform3D(Basis.Identity, start),
			Exclude = new Godot.Collections.Array<Rid>(excludes ?? new Rid[0]),
			CollisionMask = collisionMask,
			// Motion = end - start,       // Not used for IntersectShape
		};

		// Exclude this object from the raycast
		if (n is CollisionObject3D co3d)
			query.Exclude.Add(co3d.GetRid());

		var result = spaceState.IntersectShape(query);

		return result;
	}

Usage example

var shape = new BoxShape3D();
var pos = new Vector3(0f, 0f, 0f);
var result = this.Shapecast(shape.GetRid(), pos); 
1 Like

Thanks for your reply, it is super helpful.

If that is the way to do it I will leave it like that :slight_smile:

Also thanks for extension method. I will sure make one for 2D when I start using Intersect Shape more.
(I actually forgot to mention that result.GetCollider<Node2D>() is my own extension method :sweat_smile: )

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.