In Prison Architect, every entity — prisoner, guard, item, tree — is ultimately tied to a tile. The grid is the world, and the engine assumes that all entities can be reduced to a tile index.

That works fine for a flat, strictly tile-based game, but it comes with trade-offs:

  • Anything that doesn’t really “fit” into a single tile (like multi-tile objects, diagonal movement, or smooth animation) ends up bolted on awkwardly.
    • Remember how in Prison Architect you tried to place a 1-tile chair at a 2-tiles table ending up with one or another side but not the middle?
  • Precise positioning isn’t possible — everything snaps to the grid whether it looks right or not.
    • Yes, in Prison Architect entities are moving seemingly smoothly, but it’s achieved with rendering quirks such as decoupled render-specific attributes in the entity and floating-point additions/subtractions which are fundamentally prone to errors and are significantly slower than these operations done on integer values.
    • In Prison Architect, the source of truth is still tiles, and every movement apparently requires interpolation of the rendered pixel coordinate to the tile index. For every step of move. In every draw iteration.
  • Modders quickly hit limits when trying to represent entities that should move more naturally.
    • Want to add a helicopter? In Prison Architect, it (I assume) still participates in collision detection as a tile-bound entity.
    • A small bin under an office desk? Impossible in Prison Architect.
    • A small bin next to a small coat hanger? In Prison Architect, they take as much space as two fridges.

I decided to take a different approach.

In my engine, entities aren’t bound to tiles at all. Each entity has integer pixel coordinates (X, Y) and an integer Floor. That’s it. When the engine needs to know “which tile is this entity on?”, it can calculate the tile index from the coordinates. But entities themselves don’t care.

Why does this matter?

  • Smooth movement — Characters, vehicles, or projectiles can move freely, pixel by pixel, without snapping or jitter. And internally, it’s done by naturally simple integer arithmetics, not the floating-point error-prone, CPU-heavy inefficient stuff.
  • Flexible rendering — Sprites don’t need to align perfectly with a tile grid. Overlapping objects, partial placement, or animation offsets all become natural. (Walls and wall-mounted entities like doors are unfortunate exceptions.)
  • Modding freedom — A modder can add entities that don’t even live on the grid in a traditional sense — drones, floating effects, diagonal furniture — and the engine won’t fight back.
  • Easier multi-floor support — Since an entity’s Floor is just a separate field, nothing stops you from placing the same coordinates at different heights. No tile gymnastics required.

🚫 Consideration: Z instead of Floor

It might seem inconsistent at first glance. If entities in my engine aren’t tied to a grid and can move freely in pixel space across X and Y, why not let them have an arbitrary Z position too? Why not go full 3D?

Short answer: because gameplay clarity and simulation design benefit from having a discrete Floor index.

Here’s why I deliberately kept Floor as an integer:

  • Gameplay clarity
    • In a tile-based builder, clarity matters. The player thinks in floors, not arbitrary heights.
    • You don’t want prisoners halfway between floors, or stairs that ascend smoothly across 1.3 floors.
    • A clean “you’re either on floor 2 or floor 3” model makes rendering, interaction, and UX consistent and predictable. (Just imagine how counterintuitive it would be for a player to switch the camera to “show me floor 3” and to stop seeing a floor 2 of another building where floors were built with high ceilings.)
  • Simplicity for modders
    • With discrete floors, modders can define logic like “this job must occur on Floor 1” or “teleport to Floor -2” without worrying about z-ranges, thresholds, or floating-point weirdness.
    • Floor-specific rendering, culling, or effects are trivial: if (entity.Floor == camera.Floor).
  • Avoiding scope creep
    • Supporting full Z movement implies gravity, fall arcs, collision volumes, sloped terrain, and a 3D pathfinding layer. That’s a different game.
    • My goal is a vertical 2.5D builder, not a physics sandbox. Floors are levels in a structure, not floating platforms in space.

But what about flying drones?

  • Nothing stops you from layering a visual zOffset or animation on top of Floor. (Hey, perhaps I would add this component to the core outright after publishing this post!)
  • The simulation stays clean and discrete, while the visuals can be as smooth as you want.

What about walls, doors and windows?

They are still bound to the tile grid. Only entities are freed.

Why? Because otherwise the pathfinding algorithm would transform to a monster.

Read also: Rethinking Walls: Between Tiles, Not Inside

Conclusion

This choice I made means some extra work: pathfinding, job assignment, and collision detection all have to translate between “pixel space” and “tile space.” But that complexity lives inside the engine, not in mods. From the outside, modders get a clean, predictable system that doesn’t box every idea into a single rigid tile.

Prison Architect showed us what’s possible with a strict grid. My project aims to show what’s possible when the grid is still there — but entities are finally free to move beyond it.