Questo articolo riguarda il lato ingegneristico dei puzzle scorrevoli con immagine: come una foto arbitraria diventa un puzzle giocabile N×N. È la parte che i giocatori non vedono mai e su cui gli sviluppatori spendono più tempo di quanto si aspettino.
La pipeline
Tre fasi, in ordine:
1. Ritaglio quadrato
La maggior parte delle foto non è quadrata. I file HEIC dell’iPhone sono 4:3; le foto iPhone scattate in ritratto sono 3:4. Una griglia di puzzle scorrevole è 1:1. Il primo passo è scegliere quale regione quadrata della foto giocare.
Due approcci comuni:
Ritaglio al centro. Prendi il quadrato centrato più grande che entra. Veloce, prevedibile, occasionalmente sbagliato (soggetto non al centro).
Ritaglio interattivo. Mostra all’utente un quadrato trascinabile sovrapposto. Lascialo scegliere. Più lento, ma l’utente ottiene sempre ciò che vuole.
Slide Puzzle usa il ritaglio interattivo, con il centro come predefinito. Il ritaglio è non distruttivo — il file originale nella libreria foto dell’utente non viene mai modificato.
2. Ridimensionamento a risoluzione di lavoro
Una volta ritagliata, l’immagine è ancora tipicamente 3000×3000 o più (a seconda della fotocamera). Per il gioco è eccessivo. Una griglia 6×6 visualizzata a 320pt su uno schermo iPhone 3× renderizza ogni tessera a circa 53pt = 160 pixel del dispositivo. Una risoluzione sorgente di 1024×1024 dà una risoluzione per tessera di circa 170 pixel — più nitida di quanto il display possa mostrare.
Ridimensionare a 1024 (o talvolta 2048) è lo standard. Sorgenti più grandi sprecano memoria e rallentano il caricamento senza migliorare il risultato visibile.
3. Rendering delle tessere su richiesta
Questa è la parte che la maggior parte delle persone sbaglia al primo tentativo. L’immagine non viene effettivamente tagliata in 16 file separati. Sarebbe lento, dispendioso e inutile.
Invece, l’app renderizza ogni tessera disegnando la stessa immagine sorgente in un rettangolo della dimensione della tessera, con il rettangolo sorgente spostato per la posizione della tessera nell’immagine obiettivo. CSS lo chiama trucco "background-position"; iOS lo chiama rettangolo di clip CGImage; SwiftUI usa Rectangle().clipped() su un Image.
In pseudocodice, disegnare la tessera nella posizione obiettivo (row, col) di una griglia N×N con immagine di lato S:
draw image at (-col * S/N, -row * S/N)
within a clip rectangle of (S/N × S/N)
Tutto qui. Sedici tessere per un 4×4 significano 16 chiamate per disegnare la stessa immagine sorgente con 16 offset diversi. L’app non ha mai bisogno di tagliare il file.
È anche per questo che tagliare una griglia 6×6 è istantaneo: sono 36 chiamate allo stesso renderer, non 36 operazioni su file.
E l’animazione
Far scorrere una tessera è una trasformazione di traslazione sulla tessera renderizzata. Il contenuto dell’immagine all’interno della tessera non cambia — solo la posizione della cornice del rettangolo di clip sullo schermo. Questo significa che l’animazione di scorrimento è triviale per la GPU e gira a 120 Hz su display ProMotion.
Tre dettagli di implementazione che contano:
- Il clip e il contenuto sono fratelli, non genitore/figlio. Se sono annidati, il contenuto si muove con il clip e lo scorrimento si rompe.
- Usa transform, non layout. Animare x/y via CSS transform o SwiftUI offset è accelerato dalla GPU; animare left/top no.
- Pre-rasterizza alla prima apparizione. Il primo frame del rendering di una tessera può essere lento perché l’immagine sorgente deve essere decodificata. Renderizza tutte le tessere una volta all’inizio del gioco per riscaldarle.
Archiviazione
Dove vive effettivamente l’immagine importata?
In un’app che rispetta la privacy, nel sandbox dell’app, crittografata a riposo da iOS. La libreria foto contiene ancora l’originale; l’app archivia una copia di lavoro a 1024×1024 dentro la propria cartella Documents. Quando l’utente elimina l’app, la copia di lavoro viene eliminata con essa.
In un’app cloud-based, la copia di lavoro vive su un server da qualche parte. Le implicazioni sono molto diverse — per la privacy, per il gioco offline e per ciò che succede quando il server scompare. Slide Puzzle è del tipo sandbox.
Budget di memoria
Un tipico puzzle scorrevole con immagine 4×4:
- Immagine sorgente: ~3 MB JPEG.
- Immagine decodificata in memoria: 1024 × 1024 × 4 byte = 4 MB.
- Una copia per gioco attivo.
Va bene. A 6×6, l’immagine sorgente e il buffer decodificato hanno la stessa dimensione. La griglia non ha bisogno di più memoria.
Dove i budget di memoria diventano stretti è per le librerie di copertine — 300 copertine × 4 MB = 1,2 GB se tutte decodificate insieme. Le app lo evitano decodificando su richiesta (solo quando una copertina è mostrata) e rilasciando al passaggio in background (solo l’immagine del gioco attivo è mantenuta in memoria).
Casi limite
Tre cose vanno storte in pratica:
Foto con tag EXIF di orientamento. Una foto scattata in ritratto ma archiviata in landscape con un tag di rotazione si visualizzerà correttamente nella libreria foto e in modo sbagliato nel puzzle se l’app dimentica di applicare la rotazione EXIF. La prima versione di ogni app di puzzle fotografico ha questo bug.
Immagini sorgente molto grandi. Alcuni file HEIC sono 6000×8000 pixel. Caricarli in memoria a piena risoluzione farà crashare l’app su iPhone più piccoli. La correzione è una decodifica in streaming a una dimensione target ridotta — ImageIO di Apple lo supporta. Decodifica a 2048×2048 direttamente dal disco; non decodificare mai a dimensione piena.
Rendering sub-pixel. Dimensioni di tessere che non si dividono in modo uniforme nella griglia di pixel dello schermo producono una colonna di pixel frazionari su un lato di ogni tessera. Con il clipping puro, questo appare come uno spazio sottile. Risolvi o agganciando le dimensioni delle tessere a pixel interi (jitter visibile a dimensioni non standard della griglia) o lasciando che le tessere si sovrappongano di 0,5 pixel (nessuno spazio, nessun jitter).
Questi non sono problemi profondi ma sono uniformemente dimenticati dagli implementatori alle prime armi.
Sintesi
La pipeline è: ritaglia → ridimensiona → clip-e-traduci per renderizzare le tessere → trasformazioni di traduzione per animare gli scorrimenti. L’immagine non lascia mai il dispositivo in un’app che rispetta la privacy. L’intera pipeline sta in circa 200 righe di codice, più altre 50 per la gestione della rotazione EXIF che tutti dimenticano la prima volta.
I puzzle scorrevoli con immagine sono più semplici di quanto sembrino dall’esterno. L’immagine resta una sola immagine; le tessere sono solo viste inquadrate di essa.