Add collision detection and ground probing functionality in RapierCollisionWorld
This commit is contained in:
@@ -412,6 +412,8 @@ export class RapierCollisionWorld {
|
|||||||
z: feetPosition.z + motion.z
|
z: feetPosition.z + motion.z
|
||||||
},
|
},
|
||||||
grounded: false,
|
grounded: false,
|
||||||
|
collisionCount: 0,
|
||||||
|
groundCollisionNormal: null,
|
||||||
collidedAxes: {
|
collidedAxes: {
|
||||||
x: false,
|
x: false,
|
||||||
y: false,
|
y: false,
|
||||||
@@ -420,6 +422,8 @@ export class RapierCollisionWorld {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.syncPlayerColliderShape(shape);
|
||||||
|
|
||||||
const currentCenter = feetPositionToColliderCenter(feetPosition, shape);
|
const currentCenter = feetPositionToColliderCenter(feetPosition, shape);
|
||||||
this.playerCollider.setTranslation(currentCenter);
|
this.playerCollider.setTranslation(currentCenter);
|
||||||
this.characterController.computeColliderMovement(this.playerCollider, motion);
|
this.characterController.computeColliderMovement(this.playerCollider, motion);
|
||||||
@@ -435,16 +439,127 @@ export class RapierCollisionWorld {
|
|||||||
y: currentCenter.y + correctedMovement.y,
|
y: currentCenter.y + correctedMovement.y,
|
||||||
z: currentCenter.z + correctedMovement.z
|
z: currentCenter.z + correctedMovement.z
|
||||||
};
|
};
|
||||||
|
const collisionCount = this.characterController.numComputedCollisions();
|
||||||
|
let groundCollisionNormal: Vec3 | null = null;
|
||||||
|
|
||||||
|
for (let index = 0; index < collisionCount; index += 1) {
|
||||||
|
const collision = this.characterController.computedCollision(index);
|
||||||
|
|
||||||
|
if (
|
||||||
|
collision === null ||
|
||||||
|
collision.normal1.y < GROUND_NORMAL_Y_THRESHOLD ||
|
||||||
|
(groundCollisionNormal !== null &&
|
||||||
|
collision.normal1.y <= groundCollisionNormal.y)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
groundCollisionNormal = toVec3(collision.normal1);
|
||||||
|
}
|
||||||
|
|
||||||
this.playerCollider.setTranslation(nextCenter);
|
this.playerCollider.setTranslation(nextCenter);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
feetPosition: colliderCenterToFeetPosition(nextCenter, shape),
|
feetPosition: colliderCenterToFeetPosition(nextCenter, shape),
|
||||||
grounded: this.characterController.computedGrounded() || (motion.y < 0 && collidedAxes.y),
|
grounded:
|
||||||
|
this.characterController.computedGrounded() ||
|
||||||
|
groundCollisionNormal !== null ||
|
||||||
|
(motion.y < 0 && collidedAxes.y),
|
||||||
|
collisionCount,
|
||||||
|
groundCollisionNormal,
|
||||||
collidedAxes
|
collidedAxes
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
probePlayerGround(
|
||||||
|
feetPosition: Vec3,
|
||||||
|
shape: FirstPersonPlayerShape,
|
||||||
|
maxDistance: number
|
||||||
|
): PlayerGroundProbeResult {
|
||||||
|
if (
|
||||||
|
this.playerCollider === null ||
|
||||||
|
shape.mode === "none" ||
|
||||||
|
maxDistance <= COLLISION_EPSILON
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
grounded: false,
|
||||||
|
distance: null,
|
||||||
|
normal: null,
|
||||||
|
slopeDegrees: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.syncPlayerColliderShape(shape);
|
||||||
|
|
||||||
|
const hit = this.world.castShape(
|
||||||
|
feetPositionToColliderCenter(feetPosition, shape),
|
||||||
|
IDENTITY_ROTATION,
|
||||||
|
{
|
||||||
|
x: 0,
|
||||||
|
y: -maxDistance,
|
||||||
|
z: 0
|
||||||
|
},
|
||||||
|
this.playerCollider.shape,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
true,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
this.playerCollider
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hit === null) {
|
||||||
|
return {
|
||||||
|
grounded: false,
|
||||||
|
distance: null,
|
||||||
|
normal: null,
|
||||||
|
slopeDegrees: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const normal = toVec3(hit.normal1);
|
||||||
|
|
||||||
|
return {
|
||||||
|
grounded: normal.y >= GROUND_NORMAL_Y_THRESHOLD,
|
||||||
|
distance: maxDistance * hit.time_of_impact,
|
||||||
|
normal,
|
||||||
|
slopeDegrees:
|
||||||
|
(Math.acos(Math.max(-1, Math.min(1, normal.y))) * 180) / Math.PI
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
canOccupyPlayerShape(
|
||||||
|
feetPosition: Vec3,
|
||||||
|
shape: FirstPersonPlayerShape
|
||||||
|
): boolean {
|
||||||
|
if (shape.mode === "none") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryShape = createPlayerQueryShape(shape);
|
||||||
|
|
||||||
|
if (queryShape === null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let intersects = false;
|
||||||
|
|
||||||
|
this.world.intersectionsWithShape(
|
||||||
|
feetPositionToColliderCenter(feetPosition, shape),
|
||||||
|
IDENTITY_ROTATION,
|
||||||
|
queryShape,
|
||||||
|
() => {
|
||||||
|
intersects = true;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
this.playerCollider ?? undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
return !intersects;
|
||||||
|
}
|
||||||
|
|
||||||
resolveThirdPersonCameraCollision(
|
resolveThirdPersonCameraCollision(
|
||||||
pivot: Vec3,
|
pivot: Vec3,
|
||||||
desiredCameraPosition: Vec3,
|
desiredCameraPosition: Vec3,
|
||||||
|
|||||||
Reference in New Issue
Block a user