For some time now, I've been working on a game which is superficially similar in appearance to this one. A world is filled with particles that move and change state frequently (per frame, often). Drawing is achieved by finding the locations of particles within the camera's viewport, calculating lighting for the grid locations within the viewport, and then tinting a 1x1 white texture with the appropriate color, based on the particle and the lighting present in the particle's grid space:
public void drawParticles(ParticleBoard particle_board) {
final int cam_min_x = camera.getCameraMinX();
final int cam_min_y = camera.getCameraMinY();
final int cam_max_x = camera.getCameraMaxX();
final int cam_max_y = camera.getCameraMaxY();
final float[][] light = light_manager.calculateLighting();
for (int y = cam_min_y; y < cam_max_y; y++) {
for (int x = cam_min_x; x < cam_max_x; x++) {
final float l = light[x - cam_min_x][y - cam_min_y];
if (l <= 0.05f) continue; // don't draw if it's too dark.
final Particle p = particle_board.getParticle(x, y);
if (p != null) {
final Color c = p.getProperty().getColor();
sprite_batch.setColor(new Color(c.r, c.g, c.b, l));
sprite_batch.draw(texture, x, y);
}
}
}
}
Light calculations are done within an array of floats the size of the current viewport. The approach is somewhat flood-fill-y, in that initial light values are placed at the 'top' of the array and then spread across the rest of the array, changing based on the presence of blocks:
float[][] calculateLighting() {
final int cam_min_x = camera.getCameraMinX();
final int cam_min_y = camera.getCameraMinY();
final int cam_max_x = camera.getCameraMaxX();
final int cam_max_y = camera.getCameraMaxY();
final int light_width = cam_max_x - cam_min_x + 1;
final int light_height = cam_max_y - cam_min_y + 1;
final float[][] ambient_light = new float[light_width][light_height];
for (int y = light_height - 1; y >= 0; y--) {
for (int x = 0; x < light_width; x++) {
final float max = getMaxNeighboringLight(ambient_light, x, y, light_width, light_height);
if (particle_board.getParticle(x + cam_min_x, y + cam_min_y) != null) {
ambient_light[x][y] = max * 0.75f; // TODO: add constant to particle properties for transparency -- use that here.
} else {
ambient_light[x][y] = max;
}
}
}
return ambient_light;
}
Here's how the max neighboring light level is found:
private float getMaxNeighboringLight(float[][] l, int x, int y, int light_width, int light_height) {
float left = 0f, right = 0f, top = 0f, bottom = 0f;
if (x == 0) {
left = 0f;
} else if (x == light_width - 1) {
right = 0f;
} else {
left = l[x - 1][y];
right = l[x + 1][y];
}
if (y == 0) {
bottom = 0f;
} else if (y == light_height - 1) {
top = 1f;
} else {
bottom = l[x][y - 1];
top = l[x][y + 1];
}
return Math.max(left, right, top, bottom);
}
It would be very interesting to hear suggestions as to how these approaches to rendering and lighting calculations could be improved. It seems rather inefficient to create a new lighting array each frame, but I'm not sure how I could do much better, since many of the particles shift around frequently. Similarly, it seems that drawing could be improved, but I lack the knowledge to do so. If any clarification would be useful, please let me know. The approach used to draw particles is based in part on the responses that I got from this question.