Dezvoltarea unui joc de cărți precum Șeptică pare, la prima vedere, o sarcină banală. Totuși, provocarea reală apare când vrei ca experiența de joc să fie la fel de fluidă pe un iPhone 13 mini cât și pe un iPad Pro de 13 inch. În proiectul septica, am învățat că „responsive design” în mobile gaming înseamnă mult mai mult decât simple procente de lățime.
Calculul dinamic al pozițiilor pe masă
Cea mai mare bătaie de cap a fost plasarea cărților pe „masa de joc”. Nu poți folosi un layout static când raportul de aspect se schimbă drastic între un ecran portrait și unul landscape. Am implementat un sistem bazat pe coordonate relative care recalculează vectorii de poziție în funcție de containerWidth și containerHeight.
const calculateCardPosition = (index: number, totalCards: number, containerSize: { w: number, h: number }) => {
const spread = Math.min(containerSize.w * 0.8, 600); // Max spread de 600px
const x = (index - (totalCards - 1) / 2) * (spread / totalCards);
const y = Math.abs(x) * 0.1; // Efect de arc subtil
return { x: containerSize.w / 2 + x, y: containerSize.h / 2 + y };
};
Layout Landscape: HUD și Grid pe două coloane
Când utilizatorul rotește dispozitivul, logica de UI se schimbă radical. Pe un ecran lat, nu mai avem nevoie de meniuri suprapuse (modale). Am introdus un layout pe două coloane: în stânga avem felt-ul (masa de joc), iar în dreapta un grid pentru setări și statistici.
Pentru a gestiona această tranziție fără bug-uri de randare, am forțat un remount al componentei de setări la schimbarea orientării:
const SettingsGrid = ({ orientation }) => {
const [key, setKey] = useState(0);
useEffect(() => {
setKey(prev => prev + 1);
}, [orientation]);
return (
<div key={key} className={orientation === 'landscape' ? 'grid-2-cols' : 'stack'}>
{/* Elementele de setări */}
</div>
);
};
Capcana AdMob și rotația pe iOS
Integrarea reclamelor a fost un alt punct critic. În portret, bannerul AdMob stă cuminte la baza ecranului. În landscape însă, pe iOS, rotația poate declanșa uneori modale de tip „interstitial” care, dacă nu sunt gestionate corect, blochează UI-ul jocului. Am observat că dacă bannerul nu este distrus și recreat complet la schimbarea orientării (orientation change), SDK-ul AdMob poate încerca să redimensioneze un frame inexistent, cauzând un crash silențios sau o interfață decalată.
Mărimea cărților: Controlul utilizatorului
Pentru că unii jucători preferă cărți mai mari pentru vizibilitate, am adăugat un slider în setări care scalează transform: scale() al elementelor DOM. Acest lucru ne permite să păstrăm logica de calcul a pozițiilor intactă, aplicând doar o transformare vizuală la nivel de container.
Această abordare ne-a permis să păstrăm codul curat, separând logica de poziționare (matematică) de reprezentarea vizuală (CSS). Dacă plănuiești un proiect similar, sfatul meu este să nu te bazezi niciodată pe fixed units (px). Folosește vw/vh pentru elementele de UI și coordonate relative pentru obiectele de joc.
Septica este un organism viu care se adaptează constant. Urmează să implementăm suportul pentru ecrane pliabile, unde provocarea va fi tranziția fluidă între starea pliată și cea desfăcută a ecranului. Rămâneți aproape pentru detalii tehnice despre window.matchMedia și ResizeObserver!

