While waiting for Age of New Worlds to move through the publishing path for iOS, Steam, and Android, I tried a small experiment.
This time in Unity.
Not as a replacement for AoNW. More as a question:
What happens if I take some of the mechanics and architecture from AoNW, move them into a 3D engine, and let the concept grow in a different direction?
A few hours later, I had the first slice of Peryhelium: a space 4X prototype about colonizing the Solar System, starting with the Moon, previewing locked planets, and rendering spherical hex maps with real planetary textures.
Some of the assets came from NASA sources, including lunar data and planetary references. Some runtime textures came from other high-resolution sources where NASA’s available files were too small for close-up gameplay.
The interesting part was not that Unity could render planets.
Of course it could.
The interesting part was how little the domain architecture cared that the renderer had changed.
The Renderer Changed. The Game Shape Did Not.
AoNW is a Flutter and Flame game.
Peryhelium is a Unity game.
That sounds like a big move. And visually, it is. The old project is a 2D hex-based 4X world. The new prototype is a 3D Solar System with orbit cameras, planet textures, spherical surfaces, and mesh-based hexes.
But under the renderer, the same idea still works:
flowchart TB
subgraph AoNW["Age of New Worlds"]
Flutter["Flutter UI"]
Flame["Flame Renderer"]
Core["aonw_core"]
end
subgraph Peryhelium["Peryhelium"]
Unity["Unity Presentation"]
App["Application Layer"]
Domain["Pure C# Domain"]
end
Flutter --> Core
Flame --> Core
Unity --> App
App --> Domain
Core -. "architectural lessons" .-> DomainThe domain does not need to know whether the player clicked a Flutter widget, a Flame component, or a Unity collider.
It needs to know that the player intended something.
That intent becomes a command.
Commands Still Carry The Game
In AoNW, I already had a habit of not letting UI code mutate the world directly.
That habit transferred almost immediately.
In Peryhelium, Unity sends commands like:
new EnterSurfaceCommand(commandId, playerId, "moon");
new PreviewSurfaceCommand(commandId, playerId, "mars");
new FoundColonyCommand(commandId, playerId, "moon", cellIndex);
The reducer decides what is valid.
A locked Mars can be previewed.
A locked Mars cannot have selected hexes.
A locked Mars cannot receive a colony.
That is not a button rule.
That is a domain rule.
sequenceDiagram
participant Unity as Unity UI
participant Transport as Command Transport
participant Reducer as GameStateReducer
participant Store as Event Store
participant ReadModel as Read Model
Unity->>Transport: GameCommand
Transport->>Reducer: Reduce(state, command)
Reducer-->>Transport: GameEvent[]
Transport->>Store: Append event envelope
Transport-->>Unity: New state
Unity->>ReadModel: Project state
ReadModel-->>Unity: Renderable modelThis is the part that made the experiment feel fast.
The renderer was new.
The architecture was not.
Unity Is An Adapter
It is tempting in Unity to put gameplay logic inside MonoBehaviours.
That is often fine for small games.
But a 4X game grows teeth very quickly. There are unlock rules, research gates, surface maps, colonies, future multiplayer, AI, persistence, and replay.
So Peryhelium treats Unity as an adapter.
Unity owns:
- cameras,
- lights,
- meshes,
- materials,
- HUD,
- raycasts,
- labels,
- animation.
The domain owns:
- celestial bodies,
- unlock state,
- commands,
- events,
- surface maps,
- colony rules,
- grid frequency,
- reducer behavior.
The split looks like this:
flowchart LR
Input["Unity Input / Raycast"] --> Command["GameCommand"]
Command --> Reducer["Pure Reducer"]
Reducer --> Events["GameEvent[]"]
Events --> State["PeryheliumState"]
State --> Projection["Read Models"]
Projection --> Render["Unity Rendering"]This means the same rule can later run locally, on a server, or inside tests.
That was one of the strongest lessons from AoNW multiplayer work:
The client can feel responsive.
But it should not invent reality.
From Flat Hexes To Planet Hexes
The biggest conceptual change was the map.
AoNW uses a flat hex world.
Peryhelium needs planetary surfaces.
A sphere cannot be made entirely of perfect hexagons. You need 12 pentagons, just like a geodesic sphere.
So the prototype generates geodesic surface maps: mostly hexes, with 12 pentagons, projected onto a 3D sphere.
The number of cells scales with the real size of the body, but it is capped for performance.
The Moon stays smaller.
Mars gets more cells.
Earth and Venus get denser maps.
Jupiter and Saturn are large, but they do not create absurd prototype-breaking grids.
flowchart TB
Radius["Planet radius in km"] --> Frequency["Grid frequency"]
Frequency --> Geodesic["Geodesic sphere generator"]
Geodesic --> Cells["Surface cells"]
Cells --> ReadModel["SurfaceReadModel"]
ReadModel --> UnityMeshes["Unity cell meshes"]This is a good example of where the domain and renderer meet.
The domain decides the surface topology.
Unity decides how it looks.
Textures Changed The Feeling Quickly
The first prototype with plain colored spheres was useful.
But it did not feel like the Solar System yet.
Adding real textures changed that very quickly.
The Moon uses NASA LRO/LOLA-based assets. Mercury uses NASA/MESSENGER data. Other planets use high-resolution runtime textures where the available NASA 3D resources were too low resolution for close-up gameplay.
That matters because Peryhelium is not only a rules prototype.
It is a scale prototype.
The player should feel that the Moon is a place. Mars should feel locked but visible. Saturn should not just be a beige ball with a line around it.
The visual layer is not the domain.
But it changes how seriously the domain feels.
The First Slice Took Only A Few Hours
That is the part I keep thinking about.
A few hours was enough to get:
- a Solar System board,
- orbiting and spinning planets,
- NASA-based Moon textures,
- high-resolution planet textures,
- a textured Saturn ring,
- an unlocked Moon,
- locked planet previews,
- spherical hex maps,
- subtle low-opacity hex overlays,
- colony founding on unlocked surfaces,
- domain tests for preview and unlock rules.
Not because all of that is finished.
It is not.
But because the project did not start from chaos.
It started from a known shape.
Tests Made It Safe To Move Fast
The fastest way to make a prototype fragile is to put every rule in the scene.
The fastest way to keep it flexible is to test the rules outside the scene.
For example, the locked planet preview rule does not need a Unity screenshot test.
It can be a reducer test:
start match
preview Mars
assert Mars surface exists
try selecting a hex
expect no event
try founding a colony
expect no event
unlock Mars
expect preview mode to become normal surface mode
That test says more about the game than a screenshot would.
Rendering still needs visual checks.
But rules need rule tests.
This is another thing AoNW made natural.
What Actually Transfers Between Games
The nouns do not transfer directly.
AoNW has cities, units, workers, production queues, fog of war, terrain yields, diplomacy, and turn processing.
Peryhelium has planets, moons, orbital progression, spherical surfaces, colony sites, planetary unlocks, and resource infrastructure that does not exist yet.
But the grammar transfers.
flowchart TB
Intent["Player intent"] --> Command["Command"]
Command --> Decision["Reducer decision"]
Decision --> Event["Domain event"]
Event --> State["State transition"]
State --> Projection["Read model"]
Projection --> Renderer["Renderer"]That grammar is the reusable asset.
Not the UI.
Not the engine.
Not even the exact gameplay objects.
The reusable part is the contract between intent, rules, history, and presentation.
Why This Matters
Peryhelium is already a different game from AoNW.
It should be.
AoNW is about civilizations growing across a world.
Peryhelium is about humanity learning to occupy the Solar System, one hostile body at a time.
But it can still inherit architectural discipline.
That is what made the experiment fun.
Unity gave me scale, lighting, textures, cameras, and 3D presence.
AoNW gave me the confidence to keep the gameplay rules somewhere clean.
The result is still early.
Very early.
But it already has the thing I care about most in a strategy game codebase:
The renderer can change.
The rules stay honest.
Leave a Reply