BeeEngine JAVASCRIPT 2D ENGINE
Documentazione – BeeEngine
v2.5.0

BeeGrid — Documentazione Tecnica

Griglia logica a celle: ostacoli, coordinate e algoritmi di posizionamento

BeeEngine Mascot

📁 Panoramica

BeeGrid è la griglia logica di BeeEngine: divide lo spazio di gioco in celle quadrate e tiene uno stato per ogni cella (vuoto, ostacolo, tipo di tile, costo di pathfinding, e così via). Non è una tilemap grafica: non carica texture e non sa come si disegna una cella. Tiene solo i numeri.

La classe è intenzionalmente piccola. Il costruttore alloca la matrice, setCell / getCell la leggono e la scrivono con controlli di bound, e draw delega il rendering a una funzione che passi tu. Se ti serve una mappa visiva con sprite, quella è BeeTilemap. Qui stai lavorando sulla geometria dello spazio.

export class BeeGrid { constructor(cols, rows, cellSize) { this.cols = cols; this.rows = rows; this.cellSize = cellSize; this.data = Array(rows).fill().map(() => Array(cols).fill(0)); } }

📁 Ordine degli assi: colonna, poi riga

Questo è il punto dove è più facile sbagliare. La matrice interna è data[riga][colonna], ma i metodi pubblici accettano prima la colonna e poi la riga: setCell(c, r, val) e getCell(c, r). È la convenzione canvas (x, y): la colonna cresce verso destra, la riga verso il basso.

Se inverti gli argomenti, su una griglia non quadrata (per esempio 20×12) non prendi un errore: scrivi in una cella sbagliata e il gioco “funziona” in modo storto. Controlla sempre l’ordine prima di incolpare il pathfinding.

const griglia = new BeeGrid(20, 12, 32); // setCell(colonna, riga, valore) — NON (riga, colonna) griglia.setCell(3, 5, 1); griglia.getCell(3, 5); // → 1

📁 Bound sicuri su setCell e getCell

Entrambe le funzioni controllano che la riga esista (this.data[r]) e che la colonna stia in [0, cols). Fuori dai bordi setCell non fa nulla e getCell ritorna null — non 0. Zero è una cella valida (vuota di default); null significa “questa coordinata non esiste”.

Non usare getCell come se tornasse sempre un numero. Se stai facendo pathfinding o collisioni, distingue i due casi, altrimenti tratti una cella fuori mappa come ostacolo o come spazio libero a seconda di come interpreti null.

setCell(c, r, val) { if (this.data[r] && c >= 0 && c < this.cols) { this.data[r][c] = val; } } getCell(c, r) { return (this.data[r] && c >= 0 && c < this.cols) ? this.data[r][c] : null; }

📁 Disegno: tu passi la funzione, BeeGrid scorre le celle

draw(ctx, drawFunction) non dipinge da sola. Scorre tutte le celle e per ognuna chiama drawFunction(ctx, x, y, valore), dove x e y sono già in pixel: c * cellSize e r * cellSize. Il valore è quello salvato in data[r][c].

Due conseguenze pratiche. La prima: se la griglia è 40×30 stai facendo 1.200 callback ogni frame. Per una griglia di debug va bene; per un livello grande conviene disegnare solo le celle visibili (culling) fuori da questa classe, oppure non chiamare draw ogni frame. La seconda: drawFunction deve esistere. Se passi undefined, il ciclo parte e va in errore alla prima cella.

griglia.draw(ctx, (ctx, x, y, val) => { if (val === 0) return; ctx.fillStyle = val === 1 ? ‘#334155’ : ‘#f59e0b’; ctx.fillRect(x, y, griglia.cellSize, griglia.cellSize); });

📁 Da pixel a cella (e ritorno)

BeeGrid non ha un metodo worldToCell. La conversione la fai tu con la divisione intera:

const c = Math.floor(entita.worldX / griglia.cellSize); const r = Math.floor(entita.worldY / griglia.cellSize); const cella = griglia.getCell(c, r); // numero oppure null

Il contrario è quello che fa già draw: x = c * cellSize, y = r * cellSize. L’origine della griglia è in alto a sinistra dello spazio mondo, allineata a (0, 0). Se la mappa è spostata (camera, offset di livello), devi sottrarre quell’offset prima di convertire, altrimenti le celle “camminano” rispetto agli sprite.

📁 Esempio pratico

import { BeeGrid } from ‘./src/core/BeeGrid.js’; const CELL = 32; const mappa = new BeeGrid(25, 15, CELL); // 1 = muro, 2 = miele / spawn for (let c = 0; c < mappa.cols; c++) { mappa.setCell(c, 0, 1); mappa.setCell(c, mappa.rows 1, 1); } mappa.setCell(12, 7, 2); function disegnaCella(ctx, x, y, val) { if (val === 0) return; ctx.fillStyle = val === 1 ? ‘#1e293b’ : ‘#f59e0b’; ctx.fillRect(x, y, CELL, CELL); } // Nel game loop, dopo aver pulito il canvas: mappa.draw(ctx, disegnaCella);

📁 Perché è scritto così — note per chi integra

Zero è il default, non “mancante”. Ogni cella nasce a 0. Se 0 per te è “camminabile”, va bene. Se 0 per te è “non inizializzata”, non hai modo di distinguerla da una cella vuota voluta: scegli un altro sentinella (per esempio -1) e usalo tu.

Nessuna copia difensiva di data. griglia.data è l’array vivo. Puoi leggerlo per un algoritmo A*, ma se lo rimpiazzi o gli cambi la forma, setCell e draw smettono di essere coerenti con cols / rows.

Non ridimensiona a caldo. Cambiare cols o rows dopo il costruttore non rialloca la matrice. Per una mappa nuova crea un altro BeeGrid.

Non è BeeTilemap. Qui non ci sono tileset, culling, layer o collisioni automatiche. È un contenitore 2D + un loop di disegno. Tienila così: se ci metti dentro il rendering delle texture, duplichi BeeTilemap e perdi la chiarezza.

📁 Riepilogo API

Membro Ritorna Scopo
constructor(cols, rows, cellSize) BeeGrid Alloca una matrice rows × cols riempita di 0.
cols / rows / cellSize number Dimensioni della griglia e lato in pixel di una cella.
data number[][] Matrice viva data[r][c]. Non è una copia.
setCell(c, r, val) void Scrive il valore se la cella esiste. Fuori bound: no-op.
getCell(c, r) any | null Legge la cella. Fuori bound ritorna null, non 0.
draw(ctx, drawFunction) void Per ogni cella chiama drawFunction(ctx, x, y, val) in pixel.