Aperçu de la boîte de collision dans l'éditeur + blocage physique au jeu
Deux retours utilisateur sur la boîte de collision (ajoutée précédemment) : - Visible et réglable directement dans l'éditeur de scène : pour l'objet sélectionné, un contour pointillé (rectangle/cercle) se superpose sur le canevas — glisser son corps ajuste le décalage, glisser sa poignée (coin bas-droit) ajuste la taille, les deux se répercutent dans le panneau "🧱 Collision" et inversement (édition des champs -> aperçu à jour). static/js/scenes/scene-editor.js::onCollisionBoxMouseDown/ onCollisionBoxResizeMouseDown, mirror des poignées de position/taille déjà existantes pour l'objet lui-même. - Bloque désormais RÉELLEMENT le déplacement au clavier : avant chaque pas, personnage-controller.js teste si la position candidate chevaucherait la boîte de collision d'un autre objet solide (jamais un "fond", jamais un objet à collision désactivée) et annule ce pas — par AXE séparément, pour permettre de glisser le long d'un mur en diagonale plutôt qu'un blocage total au moindre contact. Réutilise forgeShapesOverlap (conditions.js, factorisé depuis elementsOverlap pour ne jamais dupliquer la règle "qu'est-ce qui se touche"). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
bc3feb5080
commit
a231bcf2fa
@@ -97,6 +97,51 @@ function forgeStopPersonnageControllers() {
|
||||
}
|
||||
}
|
||||
|
||||
// Boîte de collision d'un objet à une position DONNÉE (pas forcément sa
|
||||
// position actuelle — voir forgeWouldCollide ci-dessous, qui teste une
|
||||
// position candidate AVANT de l'appliquer) — réutilise forgeCollisionRectFromBox
|
||||
// (conditions.js, déjà chargé avant ce fichier dans templates/play.html),
|
||||
// la MÊME formule que la condition de collision de flow (elementsOverlap).
|
||||
function forgeObjectCollisionRectAt(left, top, width, height, collision) {
|
||||
return forgeCollisionRectFromBox({ left: left, top: top, width: width, height: height }, collision);
|
||||
}
|
||||
|
||||
// Tout objet de scène potentiellement "solide" pour le déplacement au
|
||||
// clavier — jamais un "fond" (image de décor, voir add_scene_object.py :
|
||||
// pointer-events déjà désactivés au jeu, jamais un obstacle), ni un
|
||||
// objet dont la collision est explicitement désactivée, ni l'objet qui
|
||||
// se déplace lui-même.
|
||||
function forgeSolidObstacles(screenData, movingId) {
|
||||
return (screenData.elements || []).filter(function (o) {
|
||||
return o.id !== movingId && o.kind !== 'fond' && !(o.collision && o.collision.enabled === false);
|
||||
});
|
||||
}
|
||||
|
||||
// Est-ce que le personnage, À la position candidate (newLeft/newTop),
|
||||
// chevaucherait la boîte de collision d'un AUTRE objet solide ? Bloque
|
||||
// alors le déplacement (voir forgeRunPersonnageControllerTick) — demandé
|
||||
// par l'utilisateur : "elle doit empêcher d'avancer en cas de collision".
|
||||
// Lit la position ACTUELLE des autres objets sur le DOM (pas
|
||||
// screenData.elements, jamais mis à jour après un premier déplacement —
|
||||
// voir actions.js, qui ne touche que le style, jamais les données) pour
|
||||
// rester correct même si un autre objet a déjà bougé.
|
||||
function forgeWouldCollide(screenData, movingObj, newLeft, newTop, width, height) {
|
||||
const movingRect = forgeObjectCollisionRectAt(newLeft, newTop, width, height, movingObj.collision);
|
||||
const obstacles = forgeSolidObstacles(screenData, movingObj.id);
|
||||
for (let i = 0; i < obstacles.length; i++) {
|
||||
const other = obstacles[i];
|
||||
const otherEl = document.querySelector('[data-object-id="' + other.id + '"]');
|
||||
if (!otherEl) continue;
|
||||
const otherLeft = parseFloat(otherEl.style.left) || 0;
|
||||
const otherTop = parseFloat(otherEl.style.top) || 0;
|
||||
const otherWidth = parseFloat(otherEl.style.width) || 0;
|
||||
const otherHeight = parseFloat(otherEl.style.height) || 0;
|
||||
const otherRect = forgeObjectCollisionRectAt(otherLeft, otherTop, otherWidth, otherHeight, other.collision);
|
||||
if (forgeShapesOverlap(movingRect, otherRect)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 50ms (20 fois/seconde) — même cadence que runScreenHeldKeyTriggers
|
||||
// (triggers.js), pour un déplacement perçu comme continu.
|
||||
function forgeRunPersonnageControllerTick(screenData) {
|
||||
@@ -110,16 +155,37 @@ function forgeRunPersonnageControllerTick(screenData) {
|
||||
var targetEl = document.querySelector('[data-object-id="' + obj.id + '"]');
|
||||
if (!targetEl) return;
|
||||
|
||||
var dx = 0, dy = 0;
|
||||
var rawDx = 0, rawDy = 0;
|
||||
if (cmd.axe !== 'vertical') {
|
||||
if (heldKeys.has(cmd.gauche)) dx -= cmd.vitesse;
|
||||
if (heldKeys.has(cmd.droite)) dx += cmd.vitesse;
|
||||
if (heldKeys.has(cmd.gauche)) rawDx -= cmd.vitesse;
|
||||
if (heldKeys.has(cmd.droite)) rawDx += cmd.vitesse;
|
||||
}
|
||||
if (cmd.axe !== 'horizontal') {
|
||||
if (heldKeys.has(cmd.haut)) dy -= cmd.vitesse;
|
||||
if (heldKeys.has(cmd.bas)) dy += cmd.vitesse;
|
||||
if (heldKeys.has(cmd.haut)) rawDy -= cmd.vitesse;
|
||||
if (heldKeys.has(cmd.bas)) rawDy += cmd.vitesse;
|
||||
}
|
||||
|
||||
// Bloque le déplacement axe par axe (pas les deux à la fois dès qu'un
|
||||
// seul est gêné) — glisser le long d'un mur en diagonale reste
|
||||
// possible, plus naturel qu'un blocage total au moindre contact.
|
||||
var curLeft = parseFloat(targetEl.style.left) || 0;
|
||||
var curTop = parseFloat(targetEl.style.top) || 0;
|
||||
var boxWidth = parseFloat(targetEl.style.width) || 0;
|
||||
var boxHeight = parseFloat(targetEl.style.height) || 0;
|
||||
var dx = rawDx, dy = rawDy;
|
||||
if (dx !== 0) {
|
||||
var candidateLeft = clampSceneObjectPosition(targetEl, 'left', curLeft + dx);
|
||||
if (forgeWouldCollide(screenData, obj, candidateLeft, curTop, boxWidth, boxHeight)) dx = 0;
|
||||
}
|
||||
if (dy !== 0) {
|
||||
var candidateTop = clampSceneObjectPosition(targetEl, 'top', curTop + dy);
|
||||
if (forgeWouldCollide(screenData, obj, curLeft, candidateTop, boxWidth, boxHeight)) dy = 0;
|
||||
}
|
||||
// Se tourne vers la direction TENTÉE même si le déplacement est
|
||||
// finalement bloqué (fait face au mur/obstacle, plus naturel qu'un
|
||||
// personnage qui reste dos tourné contre ce qui le bloque).
|
||||
if (rawDx !== 0) applyObjectProperty(targetEl, 'orientation', rawDx < 0 ? 'gauche' : 'droite');
|
||||
|
||||
// Même fps pour idle et walk (bug signalé par l'utilisateur : idle
|
||||
// tournait à 4 i/s contre 8 pour walk, perçue comme "les autres
|
||||
// animations sont lentes à côté de la marche" — aucune raison réelle
|
||||
@@ -129,10 +195,7 @@ function forgeRunPersonnageControllerTick(screenData) {
|
||||
if (idleAnim) runSpriteAnimation(targetEl, resolveSpriteFrames(obj.id, { animation: idleAnim, fps: FORGE_PERSONNAGE_FPS, loop: true }));
|
||||
return;
|
||||
}
|
||||
if (dx !== 0) {
|
||||
applyObjectProperty(targetEl, 'orientation', dx < 0 ? 'gauche' : 'droite');
|
||||
applyObjectProperty(targetEl, 'pos_x_relatif', dx);
|
||||
}
|
||||
if (dx !== 0) applyObjectProperty(targetEl, 'pos_x_relatif', dx);
|
||||
if (dy !== 0) applyObjectProperty(targetEl, 'pos_y_relatif', dy);
|
||||
var moveAnim = forgePersonnageMovementAnim(obj.id);
|
||||
if (moveAnim) runSpriteAnimation(targetEl, resolveSpriteFrames(obj.id, { animation: moveAnim, fps: FORGE_PERSONNAGE_FPS, loop: true }));
|
||||
@@ -195,5 +258,12 @@ if (typeof module !== 'undefined' && module.exports) {
|
||||
forgePersonnageAnimExists, forgePersonnageMovementAnim, forgePersonnageIdleAnim, forgePersonnageActionAnim,
|
||||
forgeRunPersonnageControllerTick, forgeStartPersonnageControllers, forgeStopPersonnageControllers,
|
||||
forgeStartPersonnageInteractListener, forgeUpdateSceneWorldSize, forgeUpdateSceneCamera,
|
||||
forgeObjectCollisionRectAt, forgeSolidObstacles, forgeWouldCollide,
|
||||
};
|
||||
// node:test charge ce fichier isolément (voir __tests__/) — jamais un
|
||||
// <script> séparé en navigateur, où conditions.js est déjà chargé avant
|
||||
// celui-ci (voir templates/play.html).
|
||||
const conditionsMod = require('./conditions.js');
|
||||
global.forgeCollisionRectFromBox = global.forgeCollisionRectFromBox || conditionsMod.forgeCollisionRectFromBox;
|
||||
global.forgeShapesOverlap = global.forgeShapesOverlap || conditionsMod.forgeShapesOverlap;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user