Performance Optimization¶
LITIENGINE is designed to be efficient, but game performance depends on how you use it. This guide covers common optimization techniques.
Measuring Performance¶
Enable Debug Metrics¶
Or in config.properties:
Custom Metrics¶
long startTime = System.nanoTime();
// Code to measure
long elapsed = System.nanoTime() - startTime;
System.out.println("Operation took: " + (elapsed / 1_000_000.0) + "ms");
Rendering Optimization¶
Reduce Draw Calls¶
Every render call has overhead. Batch similar operations:
// BAD: Multiple state changes
for (Entity e : entities) {
g.setColor(e.getColor());
g.fill(e.getBoundingBox());
}
// GOOD: Batch by color
Map<Color, List<Entity>> byColor = groupByColor(entities);
for (Map.Entry<Color, List<Entity>> entry : byColor.entrySet()) {
g.setColor(entry.getKey());
for (Entity e : entry.getValue()) {
g.fill(e.getBoundingBox());
}
}
Limit Visible Entities¶
Only render what's on screen:
@Override
public void render(Graphics2D g) {
Rectangle2D viewport = Game.world().camera().getViewport();
for (IEntity entity : entities) {
if (viewport.intersects(entity.getBoundingBox())) {
// Only render visible entities
entity.render(g);
}
}
}
Use Static Lighting¶
Pre-bake shadows instead of calculating dynamically:
See Static Lighting for details.
Entity Management¶
Suspend Inactive Entities¶
Disable updates for off-screen entities:
// In custom entity
@Override
public void update() {
if (!isOnScreen()) {
return; // Skip update
}
// Normal update logic
}
Object Pooling¶
Reuse objects instead of creating new ones:
public class BulletPool {
private static final List<Bullet> pool = new ArrayList<>();
public static Bullet get() {
if (pool.isEmpty()) {
return new Bullet();
}
return pool.remove(pool.size() - 1);
}
public static void release(Bullet bullet) {
bullet.reset();
pool.add(bullet);
}
}
Limit Entity Count¶
// Remove entities when not needed
if (Game.world().environment().getEntities().size() > MAX_ENTITIES) {
// Remove oldest/distant entities
}
Particle System¶
Limit Particle Count¶
Suspend Off-Screen Emitters¶
Use Simple Particles¶
Shapes are faster than animated sprites:
Memory Management¶
Unload Unused Resources¶
// Clear unused spritesheets
Resources.spritesheets().clear();
// Remove disposed entities
entity.dispose();
Avoid Memory Leaks¶
// Remove listeners when disposing
public void dispose() {
Game.loop().detach(this);
Input.keyboard().removeKeyListener(listener);
}
Use Primitive Collections¶
For large collections, use primitive arrays:
Physics Optimization¶
Limit Collision Checks¶
// Use appropriate collision types
@CollisionInfo(collisionType = Collision.STATIC) // For walls
@CollisionInfo(collisionType = Collision.DYNAMIC) // For moving entities
Use Simple Collision Shapes¶
Rectangles are faster than complex polygons:
// Prefer simple collision boxes
collisionEntity.setCollisionBoxWidth(32);
collisionEntity.setCollisionBoxHeight(32);
Configuration¶
Frame Rate Settings¶
Resolution Scaling¶
Profiling Tips¶
- Profile before optimizing: Measure to find actual bottlenecks
- Test on target hardware: Low-end systems show issues first
- Check GC logs: Frequent garbage collection indicates memory issues
- Use Java VisualVM: Connect to running game for detailed profiling
Common Performance Issues¶
| Issue | Symptom | Solution |
|---|---|---|
| Too many entities | Low FPS | Limit count, use pooling |
| Large spritesheets | Memory spike | Reduce image sizes |
| Excessive particles | Frame drops | Lower particle count |
| Complex collision | Stuttering | Simplify shapes |
| Memory leaks | Gradual slowdown | Dispose resources properly |
See Also¶
- Particle System - Particle optimization
- Entity Framework - Entity management
- Render Engine - Rendering system
Zero-Allocation Game Loops
Avoid creating new Point2D.Double(...) or temporary objects inside your update loops. Reuse pre-allocated instance vectors to eliminate Garbage Collection (GC) latency spikes.