在3D场景中,通过鼠标点击选取物体,有时会出现明明点选了上层物体,程序却反馈点选了下层物体。
鼠标点击选取要先启用Camera的对象拾取(physics_object_picking)功能,从视口相机向屏幕位置投射一条射线,如果这条射线命中了某个对象,就会调用对象的 CollisionObject._input_event() 函数。所以这个问题和碰撞功能相关。
如果两个碰撞盒(CollisionShape)非常接近,可能会出现投射光线的错误穿透,触发了下层碰撞盒(却没有触发上层碰撞盒)。
碰撞盒的Margin值最小只能设置到0.001(1毫米)。
经测试,对于厚度2毫米(Extents.y=0.001)的碰撞盒,如果两个碰撞盒中心距离小于0.008,都可能会出现错误。
具体原因要调查引擎的源代码实现。
找到godot\modules\bullet\godot_ray_world_algorithm.cpp:
// Epsilon to account for floating point inaccuracies
#define RAY_PENETRATION_DEPTH_EPSILON 0.01
...
void GodotRayWorldAlgorithm::processCollision(...
...
const btRayShape *ray_shape;
btTransform ray_transform;
...
btTransform to(ray_transform * ray_shape->getSupportPoint());
btCollisionWorld::ClosestRayResultCallback btResult(ray_transform.getOrigin(), to.getOrigin());
m_world->rayTestSingleInternal(ray_transform, to, other_co_wrapper, btResult);
if (btResult.hasHit()) {
btScalar depth(ray_shape->getScaledLength() * (btResult.m_closestHitFraction - 1));
if (depth > -RAY_PENETRATION_DEPTH_EPSILON) {
depth = 0.0;
}
depth是两个数的乘积,当(depth > -0.01)就清零。
设置里0.001的细腻那都是骗人的,0.01的粗糙才是真实。
如果使用到屏幕光线投射功能,对于小物件的最小长度和物体间隔要以厘米级来设计。