(just came across this, in case this helps folks finding this question)
The byteOffset can be useful when creating multiple views on the same byteArray. E.g. you might have one byteArray with a section of 5 Uint32s followed by 5 Uint8s, e.g.:
let buffer = new ArrayBuffer(45);
let uint32Ary = new Uint32Array(buffer, 0, 5);
let uint8Ary = new Uint8Array(buffer, 20, 5);
let uint8AryAll = new Uint8Array(buffer, 0);
// setting some values to show how the whole buffer is affected
uint32Ary[1] = 2000;
uint32Ary[2] = 2001;
uint8Ary[4] = 55;
console.log(uint32Ary);
console.log(uint8Ary);
console.log(uint8AryAll);
results in:
> Uint32Array [0, 2000, 2001, 0, 0]
> Uint8Array [0, 0, 0, 0, 55]
> Uint8Array [0, 0, 0, 0, 208, 7, 0, 0, 209, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55]
setting a single byte in a Uint32Array through the Uint8Array view:
uint8AryAll[4] = 210;
console.log(uint32Ary);
> Uint32Array [0, 2002, 2001, 0, 0]
This example is contrived, but illustrates how one could use an array buffer that contains the backing data for several byteArrays of different byteSize without needing to copy the data.
To use an example a little closer to what you are doing, you could set up an array of typed arrays representing rows for your data matrix:
const cols = 16;
const rows = 16;
const buffer = new ArrayBuffer(cols * rows);
const cells = new Uint8Array(buffer);
const rowArys = new Array(cols);
for (let i = 0; i < cols; i++) {
rowArys[i] = new Uint8Array(buffer, i * rows, rows);
}
let x = 5;
let y = 1;
rowArys[y][x] = 55;
console.log(rowArys[y][x]);
console.log(cells[y * rows + x]);
cells[y * rows + x] = 33;
console.log(rowArys[y][x]);
console.log(cells[y * rows + x]);
results in:
> 55
> 55
> 33
> 33
As you can see, the row arrays point to the same data as the cells array
As for storing booleans, you could use bitwise operators to store and retrieve 8 booleans per byte, but since JS is not very efficient when doing bit operations, it might not be worth the in-memory savings due to the poorer performance and more complex code, but could be worthwhile for data loading and storing depending on the situation.
That said, here is a code snippet illustration of using bitwise operators for storing booleans compactly in a simulated 2D array based on your game example:
const cols = 16;
const rows = 16;
class BooleanArray {
constructor(height, width) {
this.height = height;
this.width = width;
this.byteWidth = (width >> 3) + ((width & 0b111) > 0 ? 1 : 0);
this.dataArray = new Uint8Array(height * this.byteWidth);
}
getAt(x, y) {
const xBytes = x >>> 3;
const bitMask = 1 << (x & 0b111);
return (this.dataArray[y * this.byteWidth + xBytes] & bitMask) > 0;
}
setAt(x, y, value) {
const xBytes = x >>> 3;
const xBits = x & 0b111;
const i = y * this.byteWidth + xBytes;
if (value) {
this.dataArray[i] |= 1 << xBits;
} else {
this.dataArray[i] &= ~(1 << xBits);
}
}
toString() {
let result = "";
for (let y = 0; y < this.height; y++) {
result += "\n";
for (let x = 0; x < this.width; x++) {
result += this.getAt(x, y) ? "1" : "0";
}
}
return result;
}
}
bools = new BooleanArray(cols, rows);
console.log(bools.dataArray.length);
bools.setAt(13, 1, true);
bools.setAt(3, 0, true);
console.log(bools.getAt(13, 1));
console.log(bools.toString());
bools.setAt(13, 1, false);
console.log(bools.getAt(13, 1));
console.log(bools.toString());
results in:
> 32
> true
>
0001000000000000
0000000000000100
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
> false
>
0001000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000