- [p]Note: this article was taken from our blog site. For better format & experience go there.[/p]
[p][/p][p]Welcome to the very first devlog for Ripper, our indie top-down shooter! We want to bring you along for the ride as we build this game, sharing the problems we run into and how we solve them. Today we are talking about the world itself — how it is built, how multi-floor buildings work, and how we keep it all running smoothly with multiple players in the same game.[/p][p]
![]()
[/p][h2]What We Wanted[/h2][p]From the start, we had a pretty specific vision. We wanted players to be able to raid multi-floor buildings — blowing up walls, kicking in doors, and wrecking everything in sight, all while playing with friends. We also knew from day one that we wanted to support modding, so the whole system needed to be flexible enough for the community to reshape it however they like.[/p][p]That combination of requirements — multiplayer, multiple floors, full destructibility, and moddability — pushed us toward some creative solutions. Let's walk through them.[/p][h2]Building the World from Small Pieces[/h2][p]Everything in the game world is made up of objects. Each object is just a piece of data — think of it like a blueprint — and a dedicated
factory is responsible for bringing it to life and managing its behavior. Instead of every single fridge or wall segment being its own fully loaded entity in memory, there is one factory per object type that handles all instances of that type at once. This keeps memory usage manageable, especially for larger worlds.[/p][p]There are three kinds of objects:[/p]
- [p]Tiles — the most common type, arranged on a grid across two layers (floors and walls). Think grass, wooden wall corners, that kind of thing.[/p]
- [p]Furniture — physical objects in the world. You can move some of them around, and you can blow all of them up. Fridges, trees, explosive barrels...[/p]
- [p]Utilities — mostly invisible, but they serve important purposes. Stairs, item spawners, and similar things fall into this category.[/p]
[p]
Tiles on the left, furniture in the middle, utilities on the right.[/p][p]A nice bonus of this factory-based setup: because the object data is already structured and separated from the game logic, sending world state to all players in a multiplayer session requires no extra work. It is already in a format that can be sent directly over the network.[/p][h2]The Multi-Floor Problem[/h2][p]This is where things get interesting. Getting multiple floors to work in a game that has real physics, multiplayer, and enemy AI is trickier than it might seem.[/p][p]The most obvious solution — unloading one floor and loading the next when a player uses the stairs — works well in single-player games, but falls apart for us immediately. Players can be on different floors at the same time, and enemies need to be able to follow players across floors. You cannot just swap out the whole level.[/p][p]Another idea we considered was placing each floor at a different physical location in the world and teleporting the player between them. This technically works, but it is messy — it creates arbitrary boundaries, wastes space, and most importantly, the farther you get from the origin point of the world, the more floating point errors accumulate. On the 15th floor, you could start seeing strange, unpredictable physics behaviour.[/p][p]Our actual solution uses
collision layers. Godot, the engine we use, supports 32 collision layers. We reserve the top 16 of those to represent floors — each floor gets its own dedicated layer. This still leaves us 15 layers free for everything else (projectiles, melee hit detection, triggers, etc.).[/p][p]\[VIDEOS ARE ONLY AVAILABLE ON OUR BLOG SITE
https://blog.unheard-software.com/making-of-the-world][/p][p]
Animation showing how different collision types work across floors within a single physics space.[/p][p]Every entity and physical object only exists on the collision layer of the floor it is currently on. For things like projectiles or melee weapons that need to interact with objects, they carry a second hitbox area that combines the floor layer with their own purpose layer — so a projectile on floor 2 checks both the floor 2 layer and the "projectile" layer.[/p][p]A neat side effect of this setup is that all floors physically exist at the same location in the world, just on different layers. This means lower floors are always rendered beneath upper floors, and the player can actually see down through openings like balconies. It opens up some fun vertical gameplay possibilities.[/p][h2]Getting Enemies Around the World[/h2][p]With the floors sorted, we needed enemies to actually navigate them — and to follow players between floors.[/p][p]Our first idea was to use Godot's built-in tilemap navigation system, which supports navigation layers. It seemed perfect. Unfortunately, we hit a wall: Godot does not allow multiple navigation meshes to exist above each other, even on separate layers. Since every floor in our game can have a completely different layout, we need separate navigation data per floor. So that approach was out.[/p][p]Next we looked at Godot's [c]AStarGrid2D[/c], which lets you feed in a grid and mark cells as blocked (walls) or open. This was promising, but it comes with a catch — it requires you to define a fixed grid size upfront. We do not want to cap the world at an arbitrary boundary, so that was a dead end too.[/p][p]Our final solution is a custom pathfinding grid we built from scratch. It works on the same principles — tracking which cells are walkable based on wall placement — but without any size limit, and with a few extra tweaks to handle larger entities better.[/p][p]For navigating
between floors, we tie into the stairs system. Each staircase object is linked to its counterpart on another floor via a unique ID. When a player or enemy enters a staircase, they are moved to the connected staircase on the target floor (currently as an instant teleport — a transition animation is on the
maybe do it later list). For enemy navigation, when a target is on a different floor the AI looks up the nearest staircase that connects to that floor and pathfinds toward it first, then continues on the new floor.[/p][h2]How Big Can the World Get?[/h2][p]Just for fun, here are the hard technical limits. Each floor supports up to 32,767 tiles in each direction across two layers, and there can be up to 16 floors total. That works out to a theoretical maximum of over 137
billion tiles in a single world — which, at roughly 12 bytes per tile, would be a file size of around 1,500 gigabytes. For other objects, each one gets a unique 8-byte identifier (1 byte for the floor, 7 bytes for the unique ID), giving each floor room for an almost incomprehensible number of individual objects.[/p][p]We are pretty confident nobody is hitting these limits. Realistically, the game engine would have long since given up before you got anywhere close.[/p][h2]Wrapping Up[/h2][p]This solution ticks all our boxes — multiplayer, multi-floor, full destructibility, and modding support. Is it perfect? Not yet. We already have a list of optimizations and improvements we want to make, and we will keep iterating.[/p][p]Thanks for reading! If you want to support the project, you can
wishlist Ripper on Steam or check out more on
our website.[/p][p]Next devlog — hopefully in two weeks — we will be digging into how we built the projectile system. See you there![/p][p]
Unheard Software[/p]