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.
185 lines
5.6 KiB
JavaScript
185 lines
5.6 KiB
JavaScript
// Plant element behaviors (grass, seeds, trees)
|
|
function updateGrass(x, y) {
|
|
// Grass behaves like dirt for physics
|
|
if (getPixel(x, y + 1) === EMPTY) {
|
|
setPixel(x, y, EMPTY);
|
|
setPixel(x, y + 1, GRASS);
|
|
return true;
|
|
}
|
|
else if (getPixel(x - 1, y + 1) === EMPTY) {
|
|
setPixel(x, y, EMPTY);
|
|
setPixel(x - 1, y + 1, GRASS);
|
|
return true;
|
|
}
|
|
else if (getPixel(x + 1, y + 1) === EMPTY) {
|
|
setPixel(x, y, EMPTY);
|
|
setPixel(x + 1, y + 1, GRASS);
|
|
return true;
|
|
}
|
|
else if (getPixel(x, y + 1) === WATER) {
|
|
setPixel(x, y, WATER);
|
|
setPixel(x, y + 1, GRASS);
|
|
return true;
|
|
}
|
|
|
|
// Grass can spread to nearby dirt
|
|
if (Math.random() < 0.0005) {
|
|
const directions = [
|
|
{dx: -1, dy: 0}, {dx: 1, dy: 0},
|
|
{dx: 0, dy: -1}, {dx: 0, dy: 1}
|
|
];
|
|
|
|
const dir = directions[Math.floor(Math.random() * directions.length)];
|
|
if (getPixel(x + dir.dx, y + dir.dy) === DIRT) {
|
|
setPixel(x + dir.dx, y + dir.dy, GRASS);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Grass dies if covered (no air above)
|
|
if (getPixel(x, y - 1) !== EMPTY && Math.random() < 0.05) {
|
|
setPixel(x, y, DIRT);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function updateSeed(x, y) {
|
|
// Seeds fall like sand
|
|
if (getPixel(x, y + 1) === EMPTY) {
|
|
setPixel(x, y, EMPTY);
|
|
setPixel(x, y + 1, SEED);
|
|
moveMetadata(x, y, x, y + 1);
|
|
return true;
|
|
}
|
|
else if (getPixel(x - 1, y + 1) === EMPTY) {
|
|
setPixel(x, y, EMPTY);
|
|
setPixel(x - 1, y + 1, SEED);
|
|
moveMetadata(x, y, x - 1, y + 1);
|
|
return true;
|
|
}
|
|
else if (getPixel(x + 1, y + 1) === EMPTY) {
|
|
setPixel(x, y, EMPTY);
|
|
setPixel(x + 1, y + 1, SEED);
|
|
moveMetadata(x, y, x + 1, y + 1);
|
|
return true;
|
|
}
|
|
// Seeds can float on water
|
|
else if (getPixel(x, y + 1) === WATER) {
|
|
// Just float, don't do anything
|
|
return false;
|
|
}
|
|
// If seed is on dirt or grass, it can germinate
|
|
else if (getPixel(x, y + 1) === DIRT || getPixel(x, y + 1) === GRASS) {
|
|
const metadata = getMetadata(x, y);
|
|
|
|
// Check if this is a flower seed
|
|
if (metadata && metadata.type === 'flower') {
|
|
setPixel(x, y, FLOWER);
|
|
// Set a random flower color
|
|
setMetadata(x, y, {
|
|
type: 'flower',
|
|
color: FLOWER_COLORS[Math.floor(Math.random() * FLOWER_COLORS.length)],
|
|
age: 0,
|
|
height: 1
|
|
});
|
|
return true;
|
|
} else {
|
|
// Regular seed becomes a grass blade
|
|
setPixel(x, y, GRASS_BLADE);
|
|
setMetadata(x, y, { age: 0, height: 1 });
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function updateGrassBlade(x, y) {
|
|
// Grass blades are static once grown
|
|
const metadata = getMetadata(x, y);
|
|
|
|
if (!metadata) {
|
|
setMetadata(x, y, { age: 0, height: 1 });
|
|
return true;
|
|
}
|
|
|
|
// Increment age
|
|
metadata.age++;
|
|
setMetadata(x, y, metadata);
|
|
|
|
// Grass blades can grow taller up to a limit
|
|
if (metadata.age % 200 === 0 && metadata.height < 3 && getPixel(x, y - 1) === EMPTY) {
|
|
setPixel(x, y - 1, GRASS_BLADE);
|
|
setMetadata(x, y - 1, { age: 0, height: metadata.height + 1 });
|
|
metadata.isTop = false;
|
|
setMetadata(x, y, metadata);
|
|
return true;
|
|
}
|
|
|
|
// Grass blades die if covered by something other than another grass blade
|
|
if (getPixel(x, y - 1) !== EMPTY && getPixel(x, y - 1) !== GRASS_BLADE && Math.random() < 0.01) {
|
|
setPixel(x, y, EMPTY);
|
|
removeMetadata(x, y);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function updateFlower(x, y) {
|
|
// Flowers are similar to grass blades but with a colored top
|
|
const metadata = getMetadata(x, y);
|
|
|
|
if (!metadata) {
|
|
setMetadata(x, y, {
|
|
type: 'flower',
|
|
color: FLOWER_COLORS[Math.floor(Math.random() * FLOWER_COLORS.length)],
|
|
age: 0,
|
|
height: 1
|
|
});
|
|
return true;
|
|
}
|
|
|
|
// Increment age
|
|
metadata.age++;
|
|
setMetadata(x, y, metadata);
|
|
|
|
// Flowers can grow taller up to a limit (2x bigger)
|
|
if (metadata.age % 300 === 0 && metadata.height < 8 && getPixel(x, y - 1) === EMPTY) {
|
|
// If this is the top of the flower, make it a stem and put a new flower on top
|
|
setPixel(x, y - 1, FLOWER);
|
|
setMetadata(x, y - 1, {
|
|
type: 'flower',
|
|
color: metadata.color,
|
|
age: 0,
|
|
height: metadata.height + 1,
|
|
isTop: true
|
|
});
|
|
metadata.isTop = false;
|
|
setMetadata(x, y, metadata);
|
|
return true;
|
|
}
|
|
|
|
// Flowers die if covered
|
|
if (getPixel(x, y - 1) !== EMPTY && getPixel(x, y - 1) !== FLOWER && Math.random() < 0.01) {
|
|
setPixel(x, y, EMPTY);
|
|
removeMetadata(x, y);
|
|
return true;
|
|
}
|
|
|
|
// Flowers can drop seeds occasionally
|
|
if (metadata.isTop && Math.random() < 0.0001) {
|
|
const directions = [-1, 1];
|
|
const dir = directions[Math.floor(Math.random() * directions.length)];
|
|
if (getPixel(x + dir, y) === EMPTY) {
|
|
setPixel(x + dir, y, SEED);
|
|
setMetadata(x + dir, y, { type: 'flower' });
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|