Aperçu de la boîte de collision dans l'éditeur + blocage physique au jeu
Build and deploy / test-python (push) Successful in 6m15s
Build and deploy / test-js (push) Successful in 55s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped

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:
william
2026-09-02 15:48:08 +02:00
co-authored by Claude Sonnet 5
parent bc3feb5080
commit a231bcf2fa
8 changed files with 365 additions and 27 deletions
+97 -8
View File
@@ -163,23 +163,112 @@ function saveSceneObjectRole(role) {
// static/js/play/conditions.js::elementsOverlap) : disponible pour TOUT
// objet de scène (pas seulement "personnage") — même patron AJAX que les
// fonctions ci-dessus, sur routes/scenes/scene_object_collision.py.
// Recale aussi l'aperçu (#collisionBoxOverlay, seulement présent pour
// l'objet sélectionné) pour rester en phase avec les champs, qu'ils
// viennent d'être tapés à la main ou ajustés au glisser (voir
// onCollisionBoxMouseDown/onCollisionBoxResizeMouseDown plus bas).
function saveSceneObjectCollision() {
var body = document.getElementById('collisionPanelBody');
if (!body) return;
var objectId = parseInt(body.dataset.elementId, 10);
var enabled = document.getElementById('collision-enabled').checked;
var shape = document.getElementById('collision-shape').value;
var width = parseFloat(document.getElementById('collision-width').value) || 0;
var height = parseFloat(document.getElementById('collision-height').value) || 0;
var offsetX = parseFloat(document.getElementById('collision-offset-x').value) || 0;
var offsetY = parseFloat(document.getElementById('collision-offset-y').value) || 0;
var overlay = document.getElementById('collisionBoxOverlay');
var objEl = document.getElementById('el-' + objectId);
if (overlay && objEl) {
var objWidth = parseFloat(objEl.style.width) || 0, objHeight = parseFloat(objEl.style.height) || 0;
overlay.style.width = width + 'px';
overlay.style.height = height + 'px';
overlay.style.left = ((objWidth - width) / 2 + offsetX) + 'px';
overlay.style.top = ((objHeight - height) / 2 + offsetY) + 'px';
overlay.classList.toggle('is-circle', shape === 'cercle');
overlay.classList.toggle('is-disabled', !enabled);
}
fetch('/game/' + GAME_SLUG + '/scene-objects/' + objectId + '/collision', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
enabled: document.getElementById('collision-enabled').checked,
shape: document.getElementById('collision-shape').value,
width: document.getElementById('collision-width').value,
height: document.getElementById('collision-height').value,
offset_x: document.getElementById('collision-offset-x').value,
offset_y: document.getElementById('collision-offset-y').value,
}),
body: JSON.stringify({ enabled: enabled, shape: shape, width: width, height: height, offset_x: offsetX, offset_y: offsetY }),
});
}
function updateCollisionFields(width, height, offsetX, offsetY) {
var wEl = document.getElementById('collision-width'), hEl = document.getElementById('collision-height');
var oxEl = document.getElementById('collision-offset-x'), oyEl = document.getElementById('collision-offset-y');
if (wEl) wEl.value = Math.round(width);
if (hEl) hEl.value = Math.round(height);
if (oxEl) oxEl.value = Math.round(offsetX);
if (oyEl) oyEl.value = Math.round(offsetY);
}
// Glisser le CORPS de l'aperçu ajuste son décalage (offset_x/offset_y) —
// mirror d'onSceneObjectMouseDown ci-dessus, mais sur la boîte de
// collision plutôt que l'objet lui-même (jamais le même geste : cliquer
// l'objet EN DEHORS de la boîte de collision continue de le déplacer
// normalement, la boîte est un enfant plus petit posé par-dessus).
function onCollisionBoxMouseDown(e, id) {
e.stopPropagation();
if (e.target.classList.contains('collisionResizeHandle')) return;
e.preventDefault();
var overlay = document.getElementById('collisionBoxOverlay');
var objEl = document.getElementById('el-' + id);
var objWidth = parseFloat(objEl.style.width) || 0, objHeight = parseFloat(objEl.style.height) || 0;
var startX = e.clientX, startY = e.clientY;
var startLeft = parseFloat(overlay.style.left) || 0, startTop = parseFloat(overlay.style.top) || 0;
var boxWidth = parseFloat(overlay.style.width) || 0, boxHeight = parseFloat(overlay.style.height) || 0;
function onMove(ev) {
overlay.style.left = (startLeft + (ev.clientX - startX)) + 'px';
overlay.style.top = (startTop + (ev.clientY - startY)) + 'px';
}
function onUp() {
document.removeEventListener('mousemove', onMove);
document.removeEventListener('mouseup', onUp);
var offsetX = (parseFloat(overlay.style.left) || 0) - (objWidth - boxWidth) / 2;
var offsetY = (parseFloat(overlay.style.top) || 0) - (objHeight - boxHeight) / 2;
updateCollisionFields(boxWidth, boxHeight, offsetX, offsetY);
saveSceneObjectCollision();
}
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
}
// Glisser la poignée (coin bas-droit) redimensionne la boîte de collision
// — mirror d'onSceneObjectResizeMouseDown, ancré sur son coin haut-gauche
// (le centre se déplace donc légèrement à chaque redimensionnement, sauf
// à recalculer l'offset pour le garder fixe : on choisit de garder le
// COIN fixe, plus prévisible au glisser qu'un centre qui "fuit").
function onCollisionBoxResizeMouseDown(e, id) {
e.stopPropagation();
e.preventDefault();
var overlay = document.getElementById('collisionBoxOverlay');
var objEl = document.getElementById('el-' + id);
var objWidth = parseFloat(objEl.style.width) || 0, objHeight = parseFloat(objEl.style.height) || 0;
var startX = e.clientX, startY = e.clientY;
var startWidth = parseFloat(overlay.style.width) || 0, startHeight = parseFloat(overlay.style.height) || 0;
var left = parseFloat(overlay.style.left) || 0, top = parseFloat(overlay.style.top) || 0;
function onMove(ev) {
overlay.style.width = Math.max(4, startWidth + (ev.clientX - startX)) + 'px';
overlay.style.height = Math.max(4, startHeight + (ev.clientY - startY)) + 'px';
}
function onUp() {
document.removeEventListener('mousemove', onMove);
document.removeEventListener('mouseup', onUp);
var newWidth = parseFloat(overlay.style.width), newHeight = parseFloat(overlay.style.height);
var offsetX = left - (objWidth - newWidth) / 2;
var offsetY = top - (objHeight - newHeight) / 2;
updateCollisionFields(newWidth, newHeight, offsetX, offsetY);
saveSceneObjectCollision();
}
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
}
// "Commandes" (voir static/js/play/personnage-controller.js) : touches de
// déplacement/interaction + blocage d'axe + touches supplémentaires d'un
// personnage — un seul formulaire, enregistré en AJAX à chaque