A questions about PhysicsDirectShapeState3D.IntersectShape()

Godot Version

4.3.stable.mono

Question

Chinese Version(there is an English version below)

我正在设计一个把任意的场景映射到体素网格中的算法。我实现的基本思路是遍历网格的每一个单元格,检测单元格是否与场景(一个Area3D节点)发生碰撞,如果存在碰撞,则将当前正在检测的单元格标记为“被占用”,反之不然。

基于上面的思路,我先写了一个用于检测单个单元格的函数,叫做UnitMapping,它返回一个布尔值来表示单元格与场景是否有碰撞。代码段1给出了该函数的实现代码。

为了测试UnitMapping函数,我在场景根节点的_PhysicsProcess()中写下了代码段2中所示的代码。如果检测到了碰撞,那么就在当前单元格的位置绘制一个绿色的方体,否则就绘制一个红色的方体。

其中detectorPosition是一个Vector3I类型的变量,它表示当前正在检测的单元格在网格中的坐标(显然它的x, y, z都应该是整数)。它可以通过UI进行控制。

GridToWorld函数用于将整数构成的网格坐标转换为浮点数构成的世界坐标,方便碰撞检测时的定位。

MappingMask是场景(一个Area3D节点)所在的物理层。它通过[Export](GDScript中的@export)在编辑器中配置。

当我运行场景并使用UI定位了一个detectorPosition时,我得到了如图1所示的结果。这个单元格显然和场景有重叠的部分,但是检测的结果是“没有碰撞”。我已经被这个问题困扰了好久了,希望有好心人解答我的疑惑。

English Version

I am designing an algorithm to map any scene to a voxel grid. The basic idea I have implemented is to traverse every cell of the grid, detecting whether a cell collides with the scene (an Area3D node). If there is a collision, the cell currently being detected is marked as “occupied”, otherwise it is not.

Based on the above idea, I first wrote a function called UnitMapping() for detecting a single cell, which returns a Boolean value to indicate whether the cell collides with the scene. Code snippet 1 provides the implementation code for this function.

To test the UnitMapping() function, I wrote the code shown in code snippet 2 in the _PhysicsProcess() of the scene root node. If a collision is detected, draw a green cube at the current cell position; otherwise, draw a red cube.

Among them, detectorPosition is a Vector3I variable that represents the coordinates of the cell being detected in the grid (obviously, its x, y, z should all be integers). It can be controlled through the UI.

The GridToWorld() function is used to convert grid coordinates composed of integers into world coordinates composed of floating-point numbers, facilitating collision detection localization.

MappingMask is the physical layer where the scene (an Area3D node) is located. It is configured in the editor through [Export] (@ export in GDScript).

When I ran the scene and used the UI to locate a detectorPosition, I obtained the result shown in Figure 1. This cell clearly overlaps with the scene, but the detection result is no collision. I have been troubled by this question for a long time, and I hope someone kind can answer my doubts.

代码段1(Code snippet 1)

bool UnitMapping(Vector3 center, Vector3 unitSize, uint collisionMask){
        bool result = false;

        BoxShape3D shape = new BoxShape3D();
        shape.Size = unitSize;

        Transform3D transform = new Transform3D();
        transform.Basis = new Basis();
        transform.Origin = center;

        PhysicsShapeQueryParameters3D query = new PhysicsShapeQueryParameters3D();
        query.CollideWithAreas = true;
        query.CollideWithBodies = false;
        query.CollisionMask = collisionMask;
        query.Shape = shape;
        query.Transform = transform;

        PhysicsDirectSpaceState3D spaceState = GetWorld3D().DirectSpaceState;
        Array<Dictionary> checkResult = spaceState.IntersectShape(query);

        if(checkResult.Count == 0) result = false;
        else result = true;

        return result;
    }

代码段2(Code snippet 2)

public override void _PhysicsProcess(double delta)
  {
      base._PhysicsProcess(delta);

      //Test UnitMapping()
      Vector3 center = GridToWorld(detectorPosition, UnitSize, Offset);
      bool mappingResult = UnitMapping(center, UnitSize, MappingMask);
      detectorMeshInstance.QueueFree();
      if(mappingResult){
          detectorMeshInstance = DrawCube3D(center, UnitSize.X, new Color(0, 1, 0, 0.6f));
      } else {
          detectorMeshInstance = DrawCube3D(center, UnitSize.X, new Color(1, 0, 0, 0.6f));
      }
  }