My project involved third person controller. Here is the script i have been using til now, plus some changes I tried to make to avoid the trouble of seing through solids objects, but nothing seems to work properly when i am too close from a wall, I can see through the wall… The script I based my work is the playcanvas classic one, there is a RayCastEndPoint entity, which is not used in the script??? I wonder why it is there? Is there a way to avoid this see through annoying problem, beause I am thinking right now th create a second camera, in front of the player to switch to one person style when such collision are detected.
Actual script : var CameraMovement = pc.createScript(‘cameraMovement’);
// ---------- Paramètres exposés ----------
CameraMovement.attributes.add(‘mouseSpeed’, {
type: ‘number’,
default: 1.4,
description: ‘Sensibilité souris’
});
CameraMovement.attributes.add(‘distance’, {
type: ‘number’,
default: 4,
description: ‘Recul derrière le joueur (m)’
});
CameraMovement.attributes.add(‘height’, {
type: ‘number’,
default: 1.6,
description: ‘Hauteur au-dessus du pivot (m)’
});
// “épaisseur” virtuelle de la caméra
CameraMovement.attributes.add(‘cameraRadius’, {
type: ‘number’,
default: 0.7,
description: ‘Rayon de collision caméra’
});
/* ---------- Initialisation ---------- */
CameraMovement.prototype.initialize = function () {
this.eulers = new pc.Vec3(); // (x = yaw, y = pitch)
this.touchCoords = new pc.Vec2();
var app = this.app;
app.mouse.on('mousemove', this.onMouseMove, this);
app.mouse.on('mousewheel', this.onMouseWheel, this);
this.camera = this.entity.camera;
this.initialFov = this.camera.fov;
this.minFov = this.initialFov - 20;
this.maxFov = this.initialFov + 20;
this.on('destroy', function () {
app.mouse.off('mousemove', this.onMouseMove, this);
app.mouse.off('mousewheel', this.onMouseWheel, this);
}, this);
this.rayEnd = app.root.findByName('RaycastEndPoint');
};
/* ---------- Variation FOV avec molette ---------- */
CameraMovement.prototype.onMouseWheel = function (event) {
if (!this.camera) return;
this.camera.fov = pc.math.clamp(
this.camera.fov - event.wheel * 1,
this.minFov,
this.maxFov
);
};
/* ---------- Mise à jour après simulation ---------- */
CameraMovement.prototype.postUpdate = function (dt) {
var pivot = this.entity.parent;
var targetY = this.eulers.x + 180;
var targetX = this.eulers.y;
pivot.setEulerAngles(-targetX, targetY, 0);
this.entity.setPosition(this.getWorldPoint());
this.entity.lookAt(
pivot.getPosition().x,
pivot.getPosition().y + this.height,
pivot.getPosition().z
);
};
/* ---------- Déplacement souris ---------- */
CameraMovement.prototype.onMouseMove = function (e) {
if (pc.Mouse.isPointerLocked()) {
this.eulers.x -= (this.mouseSpeed * e.dx) / 60;
this.eulers.y += (this.mouseSpeed * e.dy) / 60;
this.eulers.x = (this.eulers.x + 360) % 360;
this.eulers.y = pc.math.clamp(this.eulers.y, -60, 45);
}
};
/* ---------- Calcule la position finale de la caméra (avec “épaisseur”) ---------- */
CameraMovement.prototype.getWorldPoint = function () {
var pivot = this.entity.parent;
// Position de la tête du joueur
var pivotPos = pivot.getPosition();
var start = pivotPos.clone().add(new pc.Vec3(0, this.height, 0));
// Direction derrière le joueur
var backDir = pivot.forward.clone().scale(-1).normalize();
// Position souhaitée sans collision
var desiredPos = start.clone().add(backDir.clone().scale(this.distance));
var rbSystem = this.app.systems.rigidbody;
if (!rbSystem) {
// sécurité : si pas de physique, on rend la position brute
return desiredPos;
}
// Raycast tête -> caméra
var hit = rbSystem.raycastFirst(start, desiredPos);
if (!hit) {
// pas de mur entre tête et caméra → position normale
return desiredPos;
}
// Distance de la tête au point d'impact
var distHit = start.distance(hit.point);
// rayon virtuel de la caméra
var radius = this.cameraRadius;
var minDist = 0.5; // distance mini tête ↔ caméra
// centre de la caméra doit rester "radius" AVANT le mur
var allowedDist = distHit - radius;
// clamp
allowedDist = Math.max(minDist, allowedDist);
allowedDist = Math.min(allowedDist, this.distance);
// Direction réelle tête -> caméra
var dir = desiredPos.clone().sub(start);
var len = dir.length();
if (len === 0) return start;
dir.normalize();
// Centre de la caméra = start + dir * allowedDist
return start.clone().add(dir.scale(allowedDist));
};
Old scritp :
var CameraMovement = pc.createScript(‘cameraMovement’);
// ---------- Paramètres exposés ----------
CameraMovement.attributes.add(‘mouseSpeed’, {
type: ‘number’,
default: 1.4,
description: ‘Sensibilité souris’
});
CameraMovement.attributes.add(‘distance’, {
type: ‘number’,
default: 4,
description: ‘Recul derrière le joueur (m)’
});
CameraMovement.attributes.add(‘height’, {
type: ‘number’,
default: 1.6,
description: ‘Hauteur au-dessus du pivot (m)’
});
/* ---------- Initialisation ---------- */
CameraMovement.prototype.initialize = function () {
this.eulers = new pc.Vec3(); // (x = yaw, y = pitch)
this.touchCoords = new pc.Vec2();
const app = this.app;
app.mouse.on('mousemove', this.onMouseMove, this);
app.mouse.on('mousewheel', this.onMouseWheel, this);
this.camera = this.entity.camera;
this.initialFov = this.camera.fov;
this.minFov = this.initialFov - 20;
this.maxFov = this.initialFov + 20;
this.on('destroy', () => {
app.mouse.off('mousemove', this.onMouseMove, this);
app.mouse.off('mousewheel', this.onMouseWheel, this);
});
this.rayEnd = app.root.findByName('RaycastEndPoint');
};
/* ---------- Variation FOV avec molette ---------- */
CameraMovement.prototype.onMouseWheel = function (event) {
if (!this.camera) return;
// Sensibilité augmentée : plus rapide à la molette
this.camera.fov = pc.math.clamp(
this.camera.fov - event.wheel * 1, // ↑ sensibilité ajustée ici
this.minFov,
this.maxFov
);
};
/* ---------- Mise à jour après simulation ---------- */
CameraMovement.prototype.postUpdate = function (dt) {
const pivot = this.entity.parent;
const targetY = this.eulers.x + 180;
const targetX = this.eulers.y;
pivot.setEulerAngles(-targetX, targetY, 0);
this.entity.setPosition(this.getWorldPoint());
this.entity.lookAt(
pivot.getPosition().x,
pivot.getPosition().y + this.height,
pivot.getPosition().z
);
};
/* ---------- Déplacement souris ---------- */
CameraMovement.prototype.onMouseMove = function (e) {
if (pc.Mouse.isPointerLocked()) {
this.eulers.x -= (this.mouseSpeed * e.dx) / 60;
this.eulers.y += (this.mouseSpeed * e.dy) / 60;
this.eulers.x = (this.eulers.x + 360) % 360;
this.eulers.y = pc.math.clamp(this.eulers.y, -60, 45);
}
};
/* ---------- Calcule la position finale de la caméra ---------- */
CameraMovement.prototype.getWorldPoint = function () {
const pivotPos = this.entity.parent.getPosition();
const upVec = new pc.Vec3(0, this.height, 0);
const backOffset = this.entity.parent.forward.clone().scale(-this.distance);
const desiredPos = pivotPos.clone().add(backOffset).add(upVec);
const start = pivotPos.clone().add(upVec);
const hit = this.app.systems.rigidbody.raycastFirst(start, desiredPos);
return hit ? hit.point : desiredPos;
};
Thanks you for reading this…
