initial add
This commit is contained in:
193
tilemap.cm
Normal file
193
tilemap.cm
Normal file
@@ -0,0 +1,193 @@
|
||||
// tilemap
|
||||
|
||||
function tilemap()
|
||||
{
|
||||
this.tiles = [];
|
||||
this.offset_x = 0;
|
||||
this.offset_y = 0;
|
||||
this.layer = 0; // Default layer for scene tree sorting
|
||||
this._geometry_cache = {}; // Cache actual geometry data by texture
|
||||
this._dirty = true;
|
||||
this.scale_x = 1
|
||||
this.scale_y = 1
|
||||
return this;
|
||||
}
|
||||
|
||||
tilemap.for = function tilemap_for(map, fn) {
|
||||
for (var x = 0; x < map.tiles.length; x++) {
|
||||
if (!map.tiles[x]) continue;
|
||||
for (var y = 0; y < map.tiles[x].length; y++) {
|
||||
if (map.tiles[x][y] != null) {
|
||||
var result = fn(map.tiles[x][y], {
|
||||
x: x + map.offset_x,
|
||||
y: y + map.offset_y
|
||||
});
|
||||
if (result != null) {
|
||||
map.tiles[x][y] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tilemap.prototype =
|
||||
{
|
||||
at(pos) {
|
||||
var x = pos.x - this.offset_x;
|
||||
var y = pos.y - this.offset_y;
|
||||
if (!this.tiles[x]) return null;
|
||||
return this.tiles[x][y];
|
||||
},
|
||||
|
||||
set(pos, image) {
|
||||
// Shift arrays if negative indices
|
||||
if (pos.x < this.offset_x) {
|
||||
var shift = this.offset_x - pos.x;
|
||||
var new_tiles = [];
|
||||
for (var i = 0; i < shift; i++) new_tiles[i] = [];
|
||||
this.tiles = new_tiles.concat(this.tiles);
|
||||
this.offset_x = pos.x;
|
||||
}
|
||||
|
||||
if (pos.y < this.offset_y) {
|
||||
var shift = this.offset_y - pos.y;
|
||||
for (var i = 0; i < this.tiles.length; i++) {
|
||||
if (!this.tiles[i]) this.tiles[i] = [];
|
||||
var new_col = [];
|
||||
for (var j = 0; j < shift; j++) new_col[j] = null;
|
||||
this.tiles[i] = new_col.concat(this.tiles[i]);
|
||||
}
|
||||
this.offset_y = pos.y;
|
||||
}
|
||||
|
||||
var x = pos.x - this.offset_x;
|
||||
var y = pos.y - this.offset_y;
|
||||
|
||||
// Ensure array exists up to x
|
||||
while (this.tiles.length <= x) this.tiles.push([]);
|
||||
|
||||
// Convert string to image object if needed, or handle null to remove tile
|
||||
if (image && typeof image == 'string') {
|
||||
var graphics = use('graphics');
|
||||
image = graphics.texture(image);
|
||||
}
|
||||
// Note: if image is null, it will remove the tile
|
||||
|
||||
// Set the value (null removes the tile)
|
||||
this.tiles[x][y] = image;
|
||||
|
||||
// Mark cache as dirty when tiles change
|
||||
this._dirty = true;
|
||||
},
|
||||
|
||||
clear() {
|
||||
this.tiles = [];
|
||||
this.offset_x = 0;
|
||||
this.offset_y = 0;
|
||||
this._geometry_cache = {};
|
||||
this._dirty = true;
|
||||
},
|
||||
|
||||
// Build cached geometry grouped by texture
|
||||
_build_geometry_cache(pos = {x: 0, y: 0}) {
|
||||
var geometry = use('geometry');
|
||||
|
||||
// Group tiles by texture (using a unique key per image object)
|
||||
var textureGroups = {};
|
||||
var imageToKey = new Map(); // Map image objects to unique keys
|
||||
var keyCounter = 0;
|
||||
|
||||
// Collect all tiles and their positions
|
||||
for (var x = 0; x < this.tiles.length; x++) {
|
||||
if (!this.tiles[x]) continue;
|
||||
for (var y = 0; y < this.tiles[x].length; y++) {
|
||||
var tile = this.tiles[x][y];
|
||||
if (tile) {
|
||||
if (!imageToKey.has(tile)) {
|
||||
var key = `texture_${keyCounter++}`;
|
||||
imageToKey.set(tile, key);
|
||||
log.console(`New texture key: ${key} for tile ${tile}`)
|
||||
}
|
||||
var textureKey = imageToKey.get(tile);
|
||||
|
||||
if (!textureGroups[textureKey]) {
|
||||
textureGroups[textureKey] = {
|
||||
tiles: [],
|
||||
image: tile, // Store the image object
|
||||
offset_x: this.offset_x,
|
||||
offset_y: this.offset_y,
|
||||
size_x: this.scale_x,
|
||||
size_y: this.scale_y
|
||||
};
|
||||
}
|
||||
textureGroups[textureKey].tiles.push({
|
||||
x: x + this.offset_x,
|
||||
y: y + this.offset_y,
|
||||
image: tile
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate and cache geometry for each texture group
|
||||
this._geometry_cache = {};
|
||||
for (var textureKey in textureGroups) {
|
||||
var group = textureGroups[textureKey];
|
||||
if (group.tiles.length == 0) continue;
|
||||
|
||||
// Create a temporary tilemap for this texture group
|
||||
var tempMap = {
|
||||
tiles: [],
|
||||
offset_x: group.offset_x,
|
||||
offset_y: group.offset_y,
|
||||
size_x: group.size_x, // now in world-units
|
||||
size_y: group.size_y,
|
||||
pos_x: pos.x,
|
||||
pos_y: pos.y
|
||||
};
|
||||
|
||||
// Build sparse array for this texture's tiles
|
||||
group.tiles.forEach(({x, y, image}) => {
|
||||
var arrayX = x - group.offset_x;
|
||||
var arrayY = y - group.offset_y;
|
||||
if (!tempMap.tiles[arrayX]) tempMap.tiles[arrayX] = [];
|
||||
tempMap.tiles[arrayX][arrayY] = image;
|
||||
});
|
||||
|
||||
// Generate and cache geometry for this group
|
||||
var geom = geometry.tilemap_to_data(tempMap);
|
||||
this._geometry_cache[textureKey] = {
|
||||
geometry: geom,
|
||||
image: group.image
|
||||
};
|
||||
}
|
||||
|
||||
this._dirty = false;
|
||||
},
|
||||
|
||||
draw() {
|
||||
var pos = this.pos || {x:0,y:0}
|
||||
// Rebuild cache if dirty or position changed
|
||||
if (this._dirty || Object.keys(this._geometry_cache).length == 0 || this._last_pos?.x != pos.x || this._last_pos?.y != pos.y) {
|
||||
this._build_geometry_cache(pos);
|
||||
this._last_pos = {x: pos.x, y: pos.y};
|
||||
}
|
||||
|
||||
// Generate commands from cached geometry, pulling texture_id dynamically
|
||||
var commands = [];
|
||||
var i = 0
|
||||
for (var textureKey in this._geometry_cache) {
|
||||
var cached = this._geometry_cache[textureKey];
|
||||
commands.push({
|
||||
cmd: "geometry",
|
||||
geometry: cached.geometry,
|
||||
image: cached.image,
|
||||
texture_id: cached.image.gpu // Pull GPU ID dynamically on each draw
|
||||
});
|
||||
}
|
||||
|
||||
return commands;
|
||||
},
|
||||
}
|
||||
|
||||
return tilemap
|
||||
Reference in New Issue
Block a user