Fix NPC rendering on top of path — move NPC to viewport layer, convert world-px to screen-px each tick

This commit is contained in:
justin committed 2026-05-14 10:42:10 -07:00
1 parent b81958a56d
commit 34a9bae50c
3 files changed
+24 -16

No files matched your search

+18 -11
View File
@@ -205,7 +205,7 @@ class Game {
const p = document.getElementById('map-player');
if (p) {
const centreX = viewW / 2 - 20;
const centreY = viewH * 0.60 + this.state.worldY;
const centreY = viewH * 0.75 + this.state.worldY;
p.style.left = centreX + 'px';
p.style.bottom = (viewH - centreY - 40) + 'px';
p.className = `map-player walk-${dir}`;
@@ -214,10 +214,22 @@ class Game {
this._checkNPCProximity();
}
/* Slide the world so the camera follows the player */
/* Slide the world and reposition NPC in screen-space every tick */
_applyCamera() {
const world = document.getElementById('map-inner');
if (world) world.style.transform = `translateX(${-this.state.worldX}px)`;
this._positionNPCOnScreen();
}
_positionNPCOnScreen() {
const world = document.getElementById('map-inner');
const viewH = world ? world.parentElement.offsetHeight : 300;
const npcWrap = document.getElementById('map-npc-wrap');
if (npcWrap) {
const screenX = this.state.npcWorldX - this.state.worldX;
npcWrap.style.left = (screenX - 24) + 'px';
npcWrap.style.bottom = (viewH * 0.18) + 'px';
}
}
_checkNPCProximity() {
@@ -233,7 +245,7 @@ class Game {
if (hint) {
hint.style.display = near ? 'block' : 'none';
hint.style.left = (viewW / 2 - 28) + 'px';
hint.style.bottom = (viewH * 0.46) + 'px';
hint.style.bottom = (viewH * 0.32) + 'px';
}
if (bub) bub.style.opacity = near ? '0' : '1';
this._updateDirArrow(near);
@@ -250,7 +262,7 @@ class Game {
arrow.textContent = npcScreenX >= viewW / 2 ? '▶' : '◀';
arrow.style.display = 'block';
arrow.style.left = (viewW / 2 + 30) + 'px';
arrow.style.bottom = (viewH * 0.40) + 'px';
arrow.style.bottom = (viewH * 0.28) + 'px';
}
_nearNPC() {
@@ -266,7 +278,7 @@ class Game {
this.startQuestion();
}
/* Place player at viewport centre, NPC at world-px position */
/* Place player at viewport centre, NPC via screen-space conversion */
_placeMapSprites() {
const world = document.getElementById('map-inner');
const viewW = world ? world.parentElement.offsetWidth : 420;
@@ -275,15 +287,10 @@ class Game {
const p = document.getElementById('map-player');
if (p) {
p.style.left = (viewW / 2 - 20) + 'px';
p.style.bottom = (viewH * 0.32) + 'px';
p.style.bottom = (viewH * 0.18) + 'px';
p.style.transform = 'scaleX(-1)';
p.className = 'map-player idle';
}
const npcWrap = document.getElementById('map-npc-wrap');
if (npcWrap) {
npcWrap.style.left = (this.state.npcWorldX - 24) + 'px';
npcWrap.style.bottom = (viewH * 0.32) + 'px';
}
this._applyCamera();
const hint = document.getElementById('map-talk-hint');
if (hint) hint.style.display = 'none';
+4 -4
View File
@@ -107,14 +107,14 @@
<span class="tree">🌲</span><span class="tree">🌳</span><span class="tree">🌲</span>
<span class="tree">🌳</span><span class="tree">🌲</span><span class="tree">🌳</span>
</div>
<!-- NPC with ! bubble — position driven by JS in world-px -->
</div>
<!-- Path stays fixed to the viewport — does not scroll -->
<div class="map-ground"></div>
<!-- NPC in viewport layer — JS positions it using screen-space X -->
<div class="map-npc-wrap" id="map-npc-wrap">
<div class="npc-bubble" id="npc-bubble">!</div>
<div class="map-npc" id="map-npc"></div>
</div>
</div>
<!-- Path stays fixed to the viewport — does not scroll -->
<div class="map-ground"></div>
<!-- Player stays fixed to viewport, only Y moves -->
<div class="map-player" id="map-player">🧢</div>
<!-- Hints overlay the viewport, not the world -->
+2 -1
View File
@@ -395,6 +395,7 @@ html, body {
top: 0; bottom: 0; left: 0;
width: 400%;
will-change: transform;
z-index: 5;
}
.map-trees {
position: absolute;
@@ -436,7 +437,7 @@ html, body {
filter: drop-shadow(0 0 4px rgba(248,192,48,.6));
}
/* NPC lives inside .map-inner, positioned in world-px via JS */
.map-npc-wrap { position:absolute; text-align:center; }
.map-npc-wrap { position:absolute; text-align:center; z-index: 30; }
.npc-bubble { font-size:14px; font-family:var(--font); color:var(--black); background:var(--yellow); border:2px solid var(--black); width:20px; height:20px; display:flex; align-items:center; justify-content:center; margin:0 auto 2px; animation:bounce .7s ease-in-out infinite; }
.map-npc { font-size:clamp(36px,10vw,52px); filter:drop-shadow(2px 3px 0 rgba(0,0,0,.35)); cursor:pointer; }
/* Player is fixed to the viewport centre horizontally; only Y moves */