This commit introduces several performance optimizations: - Implement chunk-based dirty rendering - Add adaptive physics update rates - Return modification status from element update functions - Reduce unnecessary rendering and physics calculations - Track world movement for efficient re-rendering The key changes include: 1. Adding `dirtyChunks` and `worldMoved` tracking 2. Modifying element update functions to return modification status 3. Implementing adaptive physics update rates based on FPS 4. Skipping rendering for unchanged chunks 5. Reducing computational overhead in physics and rendering loops These optimizations should significantly improve the simulation's performance, especially with large numbers of elements.
163 lines
7.7 KiB
JavaScript
163 lines
7.7 KiB
JavaScript
// Rendering functions
|
|
function render() {
|
|
// Clear the canvas
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
// Get visible chunks
|
|
const visibleChunks = getVisibleChunks();
|
|
|
|
// Render each visible chunk
|
|
for (const { chunkX, chunkY, isVisible } of visibleChunks) {
|
|
// Skip rendering for chunks that are not visible
|
|
if (!isVisible) continue;
|
|
|
|
const key = getChunkKey(chunkX, chunkY);
|
|
|
|
// Skip rendering if the chunk hasn't changed and isn't marked as dirty
|
|
if (!dirtyChunks.has(key) && !worldMoved) {
|
|
continue;
|
|
}
|
|
|
|
if (!chunks.has(key)) continue;
|
|
|
|
const chunk = chunks.get(key);
|
|
|
|
// Calculate screen position of chunk
|
|
const screenX = (chunkX * CHUNK_SIZE - worldOffsetX) * PIXEL_SIZE;
|
|
const screenY = (chunkY * CHUNK_SIZE - worldOffsetY) * PIXEL_SIZE;
|
|
|
|
// Draw chunk border in debug mode
|
|
if (debugMode) {
|
|
ctx.strokeStyle = '#ff0000';
|
|
ctx.lineWidth = 1;
|
|
ctx.strokeRect(
|
|
screenX,
|
|
screenY,
|
|
CHUNK_SIZE * PIXEL_SIZE,
|
|
CHUNK_SIZE * PIXEL_SIZE
|
|
);
|
|
|
|
// Draw chunk coordinates
|
|
ctx.fillStyle = '#ffffff';
|
|
ctx.font = '12px Arial';
|
|
ctx.fillText(`${chunkX},${chunkY}`, screenX + 5, screenY + 15);
|
|
}
|
|
|
|
// Render each pixel in the chunk
|
|
for (let y = 0; y < CHUNK_SIZE; y++) {
|
|
for (let x = 0; x < CHUNK_SIZE; x++) {
|
|
const index = y * CHUNK_SIZE + x;
|
|
const type = chunk[index];
|
|
|
|
if (type === EMPTY) continue;
|
|
|
|
// Set color based on type
|
|
if (type === SAND) {
|
|
ctx.fillStyle = SAND_COLOR;
|
|
} else if (type === WATER) {
|
|
// Get water color from metadata with variation
|
|
const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y);
|
|
const colorIndex = metadata && metadata.colorIndex !== undefined ? metadata.colorIndex : 0;
|
|
ctx.fillStyle = WATER_COLORS[colorIndex];
|
|
} else if (type === WALL) {
|
|
ctx.fillStyle = WALL_COLOR;
|
|
} else if (type === DIRT) {
|
|
// Get dirt color from metadata with variation
|
|
const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y);
|
|
const colorIndex = metadata && metadata.colorIndex !== undefined ? metadata.colorIndex : 0;
|
|
ctx.fillStyle = DIRT_COLORS[colorIndex];
|
|
} else if (type === STONE) {
|
|
// Get stone color from metadata with variation
|
|
const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y);
|
|
const colorIndex = metadata && metadata.colorIndex !== undefined ? metadata.colorIndex : 0;
|
|
ctx.fillStyle = STONE_COLORS[colorIndex];
|
|
} else if (type === GRASS) {
|
|
// Get grass color from metadata with variation
|
|
const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y);
|
|
const colorIndex = metadata && metadata.colorIndex !== undefined ? metadata.colorIndex : 0;
|
|
ctx.fillStyle = GRASS_COLORS[colorIndex];
|
|
} else if (type === WOOD) {
|
|
// Get wood color from metadata with variation
|
|
const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y);
|
|
const colorIndex = metadata && metadata.colorIndex !== undefined ? metadata.colorIndex : 0;
|
|
ctx.fillStyle = WOOD_COLORS[colorIndex];
|
|
} else if (type === SEED) {
|
|
ctx.fillStyle = SEED_COLOR;
|
|
} else if (type === GRASS_BLADE) {
|
|
// Use the same color variation as grass
|
|
const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y);
|
|
const colorIndex = metadata && metadata.colorIndex !== undefined ? metadata.colorIndex : 0;
|
|
ctx.fillStyle = GRASS_COLORS[colorIndex];
|
|
} else if (type === FLOWER) {
|
|
// Get flower color from metadata or use a default
|
|
const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y);
|
|
ctx.fillStyle = metadata && metadata.color ? metadata.color : FLOWER_COLORS[0];
|
|
} else if (type === TREE_SEED) {
|
|
ctx.fillStyle = SEED_COLOR;
|
|
} else if (type === LEAF) {
|
|
// Get leaf color from metadata with variation
|
|
const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y);
|
|
const colorIndex = metadata && metadata.colorIndex !== undefined ? metadata.colorIndex : 0;
|
|
ctx.fillStyle = LEAF_COLORS[colorIndex];
|
|
} else if (type === FIRE) {
|
|
// Get fire color from metadata
|
|
const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y);
|
|
const colorIndex = metadata ? metadata.colorIndex : 0;
|
|
ctx.fillStyle = FIRE_COLORS[colorIndex];
|
|
} else if (type === LAVA) {
|
|
// Get lava color from metadata
|
|
const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y);
|
|
const colorIndex = metadata ? metadata.colorIndex : 0;
|
|
ctx.fillStyle = LAVA_COLORS[colorIndex];
|
|
}
|
|
|
|
// Draw the pixel
|
|
ctx.fillRect(
|
|
screenX + x * PIXEL_SIZE,
|
|
screenY + y * PIXEL_SIZE,
|
|
PIXEL_SIZE,
|
|
PIXEL_SIZE
|
|
);
|
|
}
|
|
}
|
|
|
|
// Remove this chunk from the dirty list after rendering
|
|
dirtyChunks.delete(key);
|
|
}
|
|
|
|
// Reset world moved flag after rendering
|
|
worldMoved = false;
|
|
|
|
// Draw cursor position and update debug info
|
|
if (currentMouseX !== undefined && currentMouseY !== undefined) {
|
|
const worldX = Math.floor(currentMouseX / PIXEL_SIZE) + worldOffsetX;
|
|
const worldY = Math.floor(currentMouseY / PIXEL_SIZE) + worldOffsetY;
|
|
|
|
// Update coordinates display in debug mode
|
|
if (debugMode) {
|
|
document.getElementById('coords').textContent =
|
|
`Chunk: ${Math.floor(worldOffsetX / CHUNK_SIZE)},${Math.floor(worldOffsetY / CHUNK_SIZE)} | ` +
|
|
`Mouse: ${worldX},${worldY} | Offset: ${Math.floor(worldOffsetX)},${Math.floor(worldOffsetY)}`;
|
|
|
|
// Draw cursor outline
|
|
const cursorScreenX = (worldX - worldOffsetX) * PIXEL_SIZE;
|
|
const cursorScreenY = (worldY - worldOffsetY) * PIXEL_SIZE;
|
|
|
|
ctx.strokeStyle = '#00ff00';
|
|
ctx.lineWidth = 2;
|
|
ctx.strokeRect(
|
|
cursorScreenX - PIXEL_SIZE,
|
|
cursorScreenY - PIXEL_SIZE,
|
|
PIXEL_SIZE * 3,
|
|
PIXEL_SIZE * 3
|
|
);
|
|
|
|
// Draw a dot at the exact mouse position
|
|
ctx.fillStyle = '#ff0000';
|
|
ctx.beginPath();
|
|
ctx.arc(currentMouseX, currentMouseY, 3, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
}
|
|
}
|
|
}
|