I’m facing an issue with entity selection using raycast in the PlayCanvas Engine (code, not editor).
Setup:
- Entities (plane or any mesh) with material/texture applied.
- Using
app.systems.rigidbody.raycastFirst()
/app.systems.collision.raycastFirst()
(or similar) to detect clicks/touches on entities. - On selection, I attach a gizmo to manipulate the entity (scale, move, rotate).
Problem:
After scaling an entity with the gizmo, the raycast only detects the entity when I click near its center. Clicking near the top, bottom, or edges does not register — it seems like the raycast is still using the original bounds before scaling.
Question:
- Do I need to manually update the collision component or bounding box after scaling?
- Is there a recommended way in PlayCanvas Engine to ensure raycasts reflect the entity’s new size after transformations?
Any pointers, examples, or workarounds would be really helpful!
EntitySelector.prototype.performRaycast = function (screenPosition) {
if (!this.cameraEntity || !this.cameraEntity.camera) {
console.error('🚫 Camera entity or camera component missing');
return;
}
// Create ray from camera through screen position
const from = this.cameraEntity.getPosition();
const to = this.cameraEntity.camera.screenToWorld(
screenPosition.x,
screenPosition.y,
this.cameraEntity.camera.farClip,
);
//console.log('🎯 Performing raycast from:', from, 'to:', to);
// Perform raycast - this will hit collision components
const result = this.app.systems.rigidbody.raycastFirst(from, to);
if (result && result.entity) {
const hitEntity = result.entity;
// Check if entity should be ignored
if (this.shouldIgnoreEntity(hitEntity)) {
//console.log('🚫 Entity ignored:', hitEntity.name);
return;
}
// Find the correct entity to select
let entityToSelect = this.resolveEntityToSelect(hitEntity);
//console.log('✅ Selected entity resolved to:', entityToSelect.name);
this.selectEntity(entityToSelect, result.point);
} else {
this.deselectEntity();
//console.log('❌ No entity hit by raycast');
}
};
const collisionHalfExtents = calculateCollisionHalfExtents(glbEntity);
glbEntity.addComponent('rigidbody', {
type: 'static',
});
// Add collision component using calculated half extents
glbEntity.addComponent('collision', {
type: 'box',
halfExtents: [
collisionHalfExtents.x * 2,
collisionHalfExtents.y * 2,
collisionHalfExtents.z * 2,
],
});
const calculateCollisionHalfExtents = (entity) => {
let foundMesh = false;
const a = entity.render.meshInstances[0].mesh.aabb;
let b;
if (a) {
foundMesh = true;
b = a.halfExtents;
}
if (!foundMesh) {
console.warn('No mesh instances found, using default collision half extents');
return new pc.Vec3(0.5, 0.05, 0.5); // Default fallback
}
return b;
};