diff --git a/node/server/public/app.js b/node/server/public/app.js
index 6721e3c..5cbf01d 100644
--- a/node/server/public/app.js
+++ b/node/server/public/app.js
@@ -307,6 +307,7 @@ class Spirit {
this.spiritMeshes.push(mesh);
}
});
+ this._setupPicking();
this.scene.add(this.grp);
}
@@ -357,6 +358,15 @@ class Spirit {
}
});
}
+
+ _setupPicking() {
+ // Hier ein einfacher Ansatz: Mesh mit Info-Objekt merken!
+ this.gltf.traverse(mesh => {
+ if (mesh.isMesh) {
+ mesh.userData._spiritInfo = this.info;
+ }
+ });
+ }
}
// ---- Szene initialisieren ----
@@ -408,33 +418,89 @@ async function spawnSpirit(spiritData) {
const { scene: gltfScene } = await gltfLoader.loadAsync(modelUrl);
const spirit = new Spirit(scene, gltfScene, spiritData, spawnPos);
activeSpirits.push(spirit);
- updateSpiritOverlay(spiritData);
+ //updateSpiritOverlay(spiritData);
}
-// ---- Overlay für Spirit-Infos ----
-function updateSpiritOverlay(spirit) {
+// ---- Overlay-Logik ----
+let lastOverlaySpiritData = null;
+
+// Overlay zentriert in der Mitte mit Schließen-X
+function showSpiritOverlay(spirit) {
let el = document.getElementById('spirit-info');
if (!el) {
el = document.createElement('div');
el.id = 'spirit-info';
el.style = `
- position:absolute; right:40px; bottom:40px; color:white;
- background:rgba(0,0,0,0.6); padding:10px 18px; border-radius:10px;
- font-family: sans-serif; z-index:10; max-width: 360px;
+ position: fixed;
+ left: 50%; top: 50%;
+ transform: translate(-50%,-50%);
+ color: white;
+ background: rgba(0,0,0,0.87);
+ padding: 24px 32px 20px 32px;
+ border-radius: 16px;
+ font-family: 'Segoe UI', sans-serif;
+ z-index: 9999;
+ max-width: 540px;
+ min-width: 300px;
+ box-shadow: 0 6px 48px #000a;
+ text-align: left;
`;
document.body.appendChild(el);
}
el.innerHTML = `
-
${spirit.Name || 'Spirit'}
- ${spirit.Kategorie || ''}
- Mythos: ${spirit["Mythos/Legende"] || ''}
- Rolle: ${spirit["Funktion/Rolle"] || ''}
- Charakter: ${spirit.Charakter || ''}
- ${spirit.Herkunft ? ' ' + spirit.Herkunft : ''}
-
+
+ ${spirit.Name || 'Spirit'}
+ ${spirit.Kategorie || ''}
+ Mythos: ${spirit["Mythos/Legende"] || ''}
+ Rolle: ${spirit["Funktion/Rolle"] || ''}
+ Charakter: ${spirit.Charakter || ''}
+ ${spirit.Herkunft ? '' + spirit.Herkunft + '' : ''}
`;
+ el.style.display = "block";
+ lastOverlaySpiritData = spirit;
+
+ // Close-Button Event
+ el.querySelector("#spirit-overlay-close").onclick = () => {
+ el.style.display = "none";
+ };
}
+// Mouse-Picking (zentral)
+const raycaster = new THREE.Raycaster();
+const mouse = new THREE.Vector2();
+
+function onClick(e) {
+ // Normierte Koordinaten im WebGL-Fenster:
+ const rect = renderer.domElement.getBoundingClientRect();
+ mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
+ mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
+
+ raycaster.setFromCamera(mouse, camera);
+ // Sammle alle Meshes aus allen aktiven Spirits
+ let allMeshes = [];
+ for (const spirit of activeSpirits) {
+ spirit.gltf.traverse(mesh => {
+ if (mesh.isMesh) allMeshes.push(mesh);
+ });
+ }
+ const intersects = raycaster.intersectObjects(allMeshes, false);
+ if (intersects.length > 0) {
+ const mesh = intersects[0].object;
+ if (mesh.userData._spiritInfo) {
+ showSpiritOverlay(mesh.userData._spiritInfo);
+ }
+ }
+}
+
+// Fügt das Event hinzu:
+renderer.domElement.addEventListener('pointerdown', onClick);
+
+// Kein automatisches Update mehr! Nicht von spawnSpirit aufrufen!
+// (aber Option: „verstecke Overlay“ falls Spirit verschwindet, kann man so machen...)
+
// ---- Render-Loop ----
function animate() {
const dt = clock.getDelta(), t = clock.getElapsedTime();
@@ -450,4 +516,11 @@ function animate() {
composer.render(scene, camera);
requestAnimationFrame(animate);
-}
\ No newline at end of file
+}
+
+document.addEventListener('keydown', (e) => {
+ if (e.key === 'Escape') {
+ let el = document.getElementById('spirit-info');
+ if (el) el.style.display = "none";
+ }
+});
\ No newline at end of file