Le entità sono occluse dal terreno su cui poggiano: profondità dello sprite presa al piede anziché alla testa #275

Open
opened 2026-08-06 12:56:56 +02:00 by panda · 0 comments
Collaborator

Un'entità in piedi sul terreno 3D viene coperta dal terreno stesso su un versante rivolto verso la camera. Segnalato dal proprietario: "ti segnalo un problema sull'altro versante il personaggio finisce sotto il mesh", riprodotto a (7516,4225), quota 227 — del corpo restano due pixel.

Precede il lavoro sulla copertura del terreno: i file che governano la profondità (WorldRenderer.cs, IsoCamera.cs, TerrainMesh3D.cs, ground_blend.fx) sono invariati rispetto allo stato pre-#274.

Causa

La base della camera dà toward = (0.3233, 0.3233, 0.8892), quindi Depth = −(0.3233·(x+y) + 0.8892·z): la quota pesa 2,75 volte la posizione planare. Il terreno che appare sopra l'entità sullo schermo (x+y minore, quindi più lontano in pianta) può perciò essere più vicino in profondità, se è abbastanza alto.

Lo sprite perde perché è un cartellone a profondità costante. Un personaggio in piedi è una colonna verticale e in questa base la testa è più vicina alla camera dei piedi; disegnando l'intera carta alla profondità del piede, la testa viene testata come se fosse a terra.

Correzione

Calcolare il layerDepth al vertice della carta anziché al piede: z + altezzaPixel / ZScale, con l'altezza presa dallo sprite stesso (AnchorY per i mobili, texture.Height per le statiche, che hanno origine (w/2, h)). La grandezza è derivata, non tarata — si adatta da sola a un ratto e a una sequoia.

Costo accettato: le gambe vengono testate alla quota della testa, quindi un dislivello che dovrebbe nasconderle non le nasconde. Sbagliare in questa direzione è molto meno visibile di un personaggio che sparisce dentro la collina.

Un epsilon tarato è stato scartato: non ha un valore derivabile — troppo piccolo lascia il bug su pendenze più ripide, troppo grande spegne l'occlusione delle rupi.

Invariants Check

Camminata sul ## Design checklist, nessun .

  • Scope ✓ solo la profondità degli sprite · Server-authoritative N/A nessun intent; DisplayZAt resta display-only e la walkability resta sul raw GroundZAt · GM authorization N/A nessun comando · Identity model N/A · Protocol versioned N/A nessuna forma wire toccata, ProtocolVersion.Current invariata · String catalog N/A nessun testo · Single-threaded sim N/A client · World.cs HARD GATE N/A server-side · Screen HARD GATE ✓ il calcolo sta in IsoCamera e nel call-site di WorldRenderer; GameScreen non riceve nulla · Client engine-independence ✓ la funzione va in Client.Core/IsoCamera.cs, testabile senza MonoGame · Gameplay/Networking N/A · Act on the instance ✓ l'altezza viene dallo sprite, non da una tabella per id · Extend by type ✓ un solo call-site, nessuno switch su tipo · Typed content-def N/A · Server-paced actions N/A · Persistence (GameServer) N/A derivato ogni frame · Persistence (Auth) N/A · Process separation N/A client-only · Typed options ✓ nessun tunable: l'altezza è misurata, non configurata · Broadcasts / AoI N/A · Multi-platform ✓ sola aritmetica float · Assets required ✓ nessun placeholder; rende visibile arte del pack che oggi sparisce · Asset naming N/A · ModernUO ✓ non ha equivalente — il suo client 2-D ordina per foot-Y senza depth buffer, quindi non può occludere col terreno; divergiamo perché il nostro terreno è geometria 3D reale · Docs & DoDdocs/architecture.md aggiornata nella stessa modifica.

Piano di verifica

  • Unit in Client.Core, senza GPU: la profondità di testa è sempre ≤ quella di piede; una carta alta H guadagna esattamente H·0.8892/(2·DepthWorldExtent); a H = 0 coincide col comportamento attuale.
  • Un caso oracolo che riproduce la configurazione segnalata (piede a z 227, versante a x+y minore con z 253) e asserisce che con la profondità di testa lo sprite vince e con quella di piede perde — il test che fallisce se qualcuno semplifica via il termine.
  • Screenshot da database fresco a /tp 7516 4225, prima/dopo, più un punto dove una rupe deve ancora occludere.
  • Limite dichiarato: "la rupe occlude ancora" resta giudizio visivo, non ho un test automatico.

Definition of Done

Oltre alla DoD base (test verdi, soluzione compilante, zero warning) e al requisito multi-piattaforma:

  1. A (7516,4225) l'entità è interamente visibile, dimostrato da screenshot prima/dopo dallo stesso punto.
  2. Le statiche su pendenza ricevono la stessa correzione, dallo stesso call-site.
  3. Un test fallisce se la profondità torna a essere calcolata al piede.
  4. Un punto con rupe genuinamente interposta continua a mostrare l'occlusione, con screenshot.

Follow-up

La risposta fisicamente esatta è una rampa di profondità lungo la carta (piede lontano → testa vicina), che SpriteBatch.layerDepth non può esprimere: richiede uno shader per il passaggio sprite. Da aprire a parte.

Un'entità in piedi sul terreno 3D viene coperta dal terreno stesso su un versante rivolto verso la camera. Segnalato dal proprietario: *"ti segnalo un problema sull'altro versante il personaggio finisce sotto il mesh"*, riprodotto a (7516,4225), quota 227 — del corpo restano due pixel. **Precede il lavoro sulla copertura del terreno**: i file che governano la profondità (`WorldRenderer.cs`, `IsoCamera.cs`, `TerrainMesh3D.cs`, `ground_blend.fx`) sono invariati rispetto allo stato pre-#274. ## Causa La base della camera dà `toward = (0.3233, 0.3233, 0.8892)`, quindi `Depth = −(0.3233·(x+y) + 0.8892·z)`: **la quota pesa 2,75 volte la posizione planare**. Il terreno che appare *sopra* l'entità sullo schermo (x+y minore, quindi più lontano in pianta) può perciò essere **più vicino in profondità**, se è abbastanza alto. Lo sprite perde perché è un cartellone a **profondità costante**. Un personaggio in piedi è una colonna verticale e in questa base la testa è più vicina alla camera dei piedi; disegnando l'intera carta alla profondità del piede, la testa viene testata come se fosse a terra. ## Correzione Calcolare il `layerDepth` al **vertice della carta** anziché al piede: `z + altezzaPixel / ZScale`, con l'altezza presa dallo sprite stesso (`AnchorY` per i mobili, `texture.Height` per le statiche, che hanno origine `(w/2, h)`). La grandezza è **derivata, non tarata** — si adatta da sola a un ratto e a una sequoia. Costo accettato: le gambe vengono testate alla quota della testa, quindi un dislivello che dovrebbe nasconderle non le nasconde. Sbagliare in questa direzione è molto meno visibile di un personaggio che sparisce dentro la collina. Un epsilon tarato è stato **scartato**: non ha un valore derivabile — troppo piccolo lascia il bug su pendenze più ripide, troppo grande spegne l'occlusione delle rupi. ## Invariants Check Camminata sul `## Design checklist`, nessun `✗`. - **Scope** ✓ solo la profondità degli sprite · **Server-authoritative** N/A nessun intent; `DisplayZAt` resta display-only e la walkability resta sul raw `GroundZAt` · **GM authorization** N/A nessun comando · **Identity model** N/A · **Protocol versioned** N/A nessuna forma wire toccata, `ProtocolVersion.Current` invariata · **String catalog** N/A nessun testo · **Single-threaded sim** N/A client · **`World.cs` HARD GATE** N/A server-side · **`Screen` HARD GATE** ✓ il calcolo sta in `IsoCamera` e nel call-site di `WorldRenderer`; `GameScreen` non riceve nulla · **Client engine-independence** ✓ la funzione va in `Client.Core/IsoCamera.cs`, testabile senza MonoGame · **Gameplay/Networking** N/A · **Act on the instance** ✓ l'altezza viene dallo sprite, non da una tabella per id · **Extend by type** ✓ un solo call-site, nessuno switch su tipo · **Typed content-def** N/A · **Server-paced actions** N/A · **Persistence (GameServer)** N/A derivato ogni frame · **Persistence (Auth)** N/A · **Process separation** N/A client-only · **Typed options** ✓ nessun tunable: l'altezza è misurata, non configurata · **Broadcasts / AoI** N/A · **Multi-platform** ✓ sola aritmetica float · **Assets required** ✓ nessun placeholder; rende visibile arte del pack che oggi sparisce · **Asset naming** N/A · **ModernUO** ✓ non ha equivalente — il suo client 2-D ordina per foot-Y senza depth buffer, quindi non può occludere col terreno; divergiamo perché il nostro terreno è geometria 3D reale · **Docs & DoD** ✓ `docs/architecture.md` aggiornata nella stessa modifica. ## Piano di verifica - Unit in `Client.Core`, senza GPU: la profondità di testa è sempre ≤ quella di piede; una carta alta `H` guadagna esattamente `H·0.8892/(2·DepthWorldExtent)`; a `H = 0` coincide col comportamento attuale. - Un **caso oracolo** che riproduce la configurazione segnalata (piede a z 227, versante a x+y minore con z 253) e asserisce che con la profondità di testa lo sprite vince e con quella di piede perde — il test che fallisce se qualcuno semplifica via il termine. - Screenshot da database fresco a `/tp 7516 4225`, prima/dopo, più un punto dove una rupe **deve** ancora occludere. - Limite dichiarato: "la rupe occlude ancora" resta giudizio visivo, non ho un test automatico. ## Definition of Done Oltre alla DoD base (test verdi, soluzione compilante, zero warning) e al requisito multi-piattaforma: 1. A (7516,4225) l'entità è interamente visibile, dimostrato da screenshot prima/dopo dallo stesso punto. 2. Le statiche su pendenza ricevono la stessa correzione, dallo stesso call-site. 3. Un test fallisce se la profondità torna a essere calcolata al piede. 4. Un punto con rupe genuinamente interposta continua a mostrare l'occlusione, con screenshot. ## Follow-up La risposta fisicamente esatta è una **rampa di profondità lungo la carta** (piede lontano → testa vicina), che `SpriteBatch.layerDepth` non può esprimere: richiede uno shader per il passaggio sprite. Da aprire a parte.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
marco/IsoMmo#275
No description provided.