Tutankhamun: Builders of the Eternal

โ† Back

Building an Intelligent City Builder: Smart Placement Systems in Unity

Cover Image for Building an Intelligent City Builder: Smart Placement Systems in Unity

Building an Intelligent City Builder: Smart Placement Systems in Unity

How I created an AI-driven building placement system for an isometric city builder that thinks like a real urban planner

Tutankhamun: Builders of the Eternal

Have you ever wondered how city building games like SimCity or Cities: Skylines make building placement feel so intuitive? After months of development on my Unity-based city builder Tutankhamun: Builders of the Eternal: Builders of the Eternal, I've built a system that automatically handles the complex logic of urban planning - and I'm excited to share how it works!

Table of Contents


The Vision

When I started building Tutankhamun: Builders of the Eternal, I wanted to create something more than just another city builder. I envisioned a system where players could focus on the creative aspects of urban planning while the game intelligently handled the technical details.

The result? HouseManager and RoadManager - two Unity components that work together to create an intelligent building placement system for isometric city building. These aren't just simple grid-based builders; they're AI-driven systems that understand urban planning principles.

โœจ What Makes This Special?

  • ๐Ÿง  Smart placement: Houses automatically align with roads and follow proper urban planning rules
  • ๐ŸŽฎ Isometric perfection: Designed specifically for isometric camera perspectives
  • โšก Real-time feedback: Visual preview system shows players exactly what they're building
  • ๐Ÿ—บ๏ธ Intelligent pathfinding: Roads automatically route around obstacles
  • ๐Ÿงช Rock-solid reliability: Over 45 unit tests ensure everything works perfectly

"The best game systems are invisible to the player - they just work." - This became my guiding principle throughout development.


Smart House Placement: Teaching the Game Urban Planning

The HouseManager was my first major breakthrough. I wanted houses to behave like they would in real urban planning - always connected to roads, properly oriented, and sensibly sized.

The Challenge: Making Buildings Think

One of the biggest challenges in city builders is making building placement feel natural. Players shouldn't have to manually figure out optimal orientations or worry about complex placement rules. Here's what I implemented:

๐Ÿ  Houses That Know Where They Belong

  • Road-Adjacent Requirement: No more floating houses! Every building must connect to the road network
  • Smart Orientation: The system analyzes nearby roads and automatically orients buildings correctly
  • Intelligent Sizing: Drag any rectangle - the system converts it to valid building dimensions
  • Urban Planning Rules: Maximum 2 cells perpendicular to roads (just like real subdivisions!)

๐ŸŽฏ The Magic Behind the Scenes

Here's a glimpse of how the orientation detection works:

// Example: For a horizontal road, houses extend perpendicular (north-south)
if (roadOrientation == RoadOrientation.Horizontal)
{
    targetWidth = originalWidth;  // Follow road length
    targetHeight = Math.Min(originalHeight, 2);  // Max 2 deep - realistic urban planning!
}

๐Ÿ–ฑ๏ธ Player Experience First

  • Drag to Build: Simple click-and-drag interface that anyone can understand
  • Live Preview: Semi-transparent preview shows exactly where buildings will go
  • Right-Click Cancel: Intuitive controls that feel natural
  • Smart Feedback: Invalid areas simply don't show up - no confusing error messages

Under the Hood: How Smart Placement Works

The real magic happens in the analysis algorithms. When a player drags to place buildings, the system:

๐Ÿงญ Road Orientation Detection

The AI analyzes the nearby road network to understand the urban layout:

  • Horizontal Roads: Buildings align along the X-axis, extending north-south
  • Vertical Roads: Buildings follow the Z-axis, extending east-west
  • Complex Intersections: Smart 2ร—2 maximum sizing for T-junctions and roundabouts

โœ… Multi-Layer Validation

Every building placement goes through rigorous checking:

  • โœ… Must be adjacent to existing roads (no isolated buildings!)
  • โœ… Must be on buildable terrain (land/sand, not water/mountains)
  • โœ… Cannot overlap existing structures
  • โœ… Must follow realistic dimensional constraints

๐Ÿ”„ Coordinate Magic

One of the trickiest parts was handling Unity's coordinate system for isometric views:

// Converts Unity's runtime coordinates to internal map format
Vector3Int mapCoordinates = GameManager.Instance.RuntimeToMapCoordinates(runtimeCoords);
// Map format: (x, 0, z_depth) where z represents depth in isometric view

Dev Tip: Coordinate conversion was one of those "seems simple but isn't" problems. I spent weeks getting this right!


Intelligent Road Building: AI-Powered Infrastructure

If smart house placement was the breakthrough, intelligent road building was the technical masterpiece. I wanted roads that could think - automatically finding the best path between points while avoiding obstacles.

The Pathfinding Brain

Creating roads isn't just about connecting two points. It's about finding the smart path that respects terrain, avoids buildings, and feels natural to players.

๐Ÿ›ฃ๏ธ Three-Tier Intelligence System

My road AI uses a sophisticated decision-making process:

// Three-tier pathfinding approach:
1. ๐ŸŽฏ Try straight vertical line (constant X, varying Z)
2. โžก๏ธ Try straight horizontal line (constant Z, varying X)
3. ๐Ÿง  Fall back to BFS (Breadth-First Search) for complex routing

This approach means roads prefer simple, clean paths (like real urban planning) but can handle complex situations when needed.

๐ŸŽฎ Seamless Player Experience

  • Real-time Preview: See your road before you build it
  • Visual Feedback: Semi-transparent segments show the planned route
  • Smart Terrain: Automatically avoids water and mountains
  • Efficient Routing: Always finds the shortest viable path

๐Ÿง  The Algorithm in Action

The pathfinding system is where computer science meets game design:

1. ๐ŸŽฏ Straight Line Attempts (The Efficient Choice)

  • Vertical paths: Keep the same X coordinate, vary Z
  • Horizontal paths: Keep the same Z coordinate, vary X
  • Why this matters: Real cities prefer straight roads when possible!

2. ๐Ÿง  BFS Fallback (The Problem Solver)

  • Explores all possible routes when straight paths fail
  • Finds the shortest valid path around obstacles
  • Respects terrain and building constraints
  • The magic: Complex pathfinding that feels effortless to players

๐ŸŒ Terrain Intelligence

The road system understands the world around it:

private bool CanPathRoadThrough(Vector3Int mapCell)
{
    // Cannot path through houses - respect existing buildings
    if (GameManager.Instance.IsHouseCell(mapCell)) return false;

    // Must be on land (not water/mountains) - realistic constraints
    return !GameManager.Instance.IsNonLandCell(mapCell);
}

โœ… Construction Validation

Before placing any road, the system validates:

  • ๐Ÿž๏ธ Terrain suitability: Land vs water checks
  • ๐Ÿ  Existing structures: Houses block new roads
  • ๐Ÿ›ฃ๏ธ Road overlap: Smart handling of road intersections

Technical Note: The pathfinding system processes thousands of potential paths in milliseconds, but players just see smooth, intelligent road placement!


The Brain: GameManager Integration

Behind every great system is great architecture. The GameManager serves as the central nervous system, coordinating between all the different building managers and maintaining the game's state.

Why Centralized State Management?

When I first started building this system, I had each manager tracking its own state. The result? Chaos. Houses didn't know about roads, roads didn't know about houses, and bugs were everywhere. The solution was a central brain:

public class GameManager : MonoBehaviour
{
    // ๐ŸŒ Global state tracking - the single source of truth
    public HashSet<Vector3Int> roadCells;      // Every road in the city
    public HashSet<Vector3Int> houseCells;     // Every building
    public HashSet<Vector3Int> nonLandCells;   // Water, mountains, etc.

    // ๐Ÿ”„ Coordinate conversion magic
    public Vector3Int RuntimeToMapCoordinates(Vector3Int runtimeCoord);

    // โœ… Smart validation methods
    public bool IsBuildableCell(Vector3Int cell);
    public bool IsRoadCell(Vector3Int cell);
    public bool IsHouseCell(Vector3Int cell);
}

The Power of Centralization

This architecture unlocked several powerful benefits:

  • ๐ŸŽฏ Consistent State: One source of truth eliminates conflicting information
  • ๐Ÿ’ฌ Cross-Manager Communication: Houses know about roads, roads know about houses
  • ๐Ÿ“ Coordinate Standardization: Unified coordinate system prevents bugs
  • ๐Ÿ’พ Save/Load Support: Central state makes serialization trivial

Architecture Lesson: Sometimes the best solution is the simplest one. A central state manager eliminated 90% of my coordination bugs!


Coordinate System

The system uses a sophisticated coordinate mapping to handle Unity's isometric grid requirements.

Coordinate Formats

Runtime Coordinates (from Unity Grid)

  • Format: (grid_x, grid_y_depth, 0)
  • Source: grid.WorldToCell() calls
  • Usage: Raw Unity grid coordinates

Map Coordinates (internal logic)

  • Format: (map_x, 0, map_z_depth)
  • Usage: All pathfinding, validation, and storage
  • Conversion: RuntimeToMapCoordinates()

Coordinate Conversion

public Vector3Int RuntimeToMapCoordinates(Vector3Int runtimeCoord)
{
    // Convert Unity's (x, y_depth, z_slice) to logical (x, 0, z_depth)
    return new Vector3Int(runtimeCoord.x, 0, runtimeCoord.y);
}

Grid Reference Management

Both managers automatically synchronize with the TerrainManager's grid:

private void EnsureGridReference()
{
    if (grid == null && GameManager.Instance?.TerrainManager?.terrainTilemap?.layoutGrid != null)
    {
        grid = GameManager.Instance.TerrainManager.terrainTilemap.layoutGrid;
        Debug.Log("Auto-assigned grid reference for coordinate alignment");
    }
}

Testing: Beyond Unit Tests - Building Confidence in Complex Systems

Building complex game systems without a robust testing strategy is like navigating without a compass. That's why I developed a multi-layered testing philosophy that goes far beyond traditional unit testing to ensure rock-solid reliability in a system with over 45 verification points.

The Game Development Testing Challenge

Game systems present unique testing challenges that web developers rarely face:

  • Stateful interactions: Buildings affect roads, roads affect villagers, villagers affect economy
  • Frame-based execution: Logic that depends on Unity's update cycles
  • Coordinate complexity: Multiple coordinate systems that must stay synchronized
  • Emergent behavior: Simple rules creating complex, unpredictable outcomes

๐Ÿ—๏ธ Architectural Testing: Designing for Verification

The key insight was designing the system to be testable rather than retrofitting tests later:

Dependency Injection for Game Logic

public class TestableHouseManager : IHouseManager
{
    private readonly ICoordinateConverter coordinateConverter;
    private readonly ITerrainValidator terrainValidator;
    
    // Constructor injection allows mock dependencies in tests
    public TestableHouseManager(ICoordinateConverter converter, ITerrainValidator validator)
    {
        this.coordinateConverter = converter;
        this.terrainValidator = validator;
    }
}

State Snapshot Testing

Rather than testing individual methods, I test complete system states:

[Test]
public void CompletePlayerWorkflow_BuildsProperNeighborhood()
{
    // Arrange: Set up a realistic game state
    var gameState = CreateTestGameState();
    gameState.AddRoad(new Vector3Int(0, 0, 0), new Vector3Int(5, 0, 0));
    
    // Act: Simulate complete player interaction
    var result = houseManager.BuildHouseBlock(area, dragDirection);
    
    // Assert: Verify the entire resulting state
    Assert.That(gameState.GetAllBuildings(), Has.Count.EqualTo(expectedCount));
    Assert.That(AllBuildingsConnectedToRoads(gameState), Is.True);
    Assert.That(UrbanPlanningRulesFollowed(gameState), Is.True);
}

๐ŸŽฏ Property-Based Testing for Game Logic

Traditional example-based tests miss edge cases. Property-based testing generates hundreds of random scenarios:

[Test]
public void RoadPathfinding_AlwaysFindsShortestValidPath()
{
    // Generate 1000 random terrain layouts
    var testCases = GenerateRandomTerrainLayouts(1000);
    
    foreach(var terrain in testCases)
    {
        var path = roadManager.FindPath(start, end, terrain);
        
        // Properties that must ALWAYS be true:
        Assert.That(path.IsValid(), Is.True, "Path must be valid");
        Assert.That(path.Length, Is.LessThanOrEqualTo(MaxPossibleLength()));
        Assert.That(path.RespectsTerrainConstraints(), Is.True);
    }
}

๐Ÿ”„ Integration Testing with Mock Unity Environment

The real breakthrough was creating a lightweight Unity simulation for integration tests:

public class MockUnityEnvironment
{
    public MockGrid Grid { get; private set; }
    public MockGameManager GameManager { get; private set; }
    
    public void SimulatePlayerAction(PlayerAction action)
    {
        // Simulates Unity's event system without Unity overhead
        // Allows testing complete workflows in milliseconds
    }
}

๐Ÿง  Behavioral Testing: Does It Feel Right?

Beyond correctness, I test for player experience quality:

[Test]
public void HousePlacement_FeelsNaturalToPlayers()
{
    var placements = GenerateTypicalPlayerPlacements();
    
    foreach(var placement in placements)
    {
        var result = houseManager.PlaceBuilding(placement);
        
        // Subjective qualities made objective:
        Assert.That(result.OrientationFeelsNatural(), Is.True);
        Assert.That(result.SizeFeelsReasonable(), Is.True);
        Assert.That(result.PositionFollowsUrbanPrinciples(), Is.True);
    }
}

โšก Performance Testing Under Realistic Load

Game systems must perform well under player stress:

[Test]
public void LargeCityGeneration_MaintainsPerformance()
{
    var stopwatch = Stopwatch.StartNew();
    
    // Simulate building a 100x100 city
    var cityBuilder = new CityBuilder();
    cityBuilder.GenerateCity(size: 100, buildings: 2000, roads: 500);
    
    stopwatch.Stop();
    
    // Must complete within one frame budget (16ms for 60fps)
    Assert.That(stopwatch.ElapsedMilliseconds, Is.LessThan(16));
}

The Testing Ecosystem That Changed Everything

This comprehensive approach delivered transformative benefits:

  • ๐Ÿ›ก๏ธ Fearless Refactoring: Massive architectural changes with confidence
  • ๐Ÿ” Bug Prevention: Catches issues before they become player problems
  • ๐Ÿ“š Living Documentation: Tests explain system behavior better than comments
  • โšก Rapid Iteration: New features integrate safely with existing systems
  • ๐ŸŽฏ Quality Assurance: Objective measurement of subjective game feel

Advanced Insight: The best game testing strategies verify not just that systems work correctly, but that they create the intended player experience. Testing "does it feel good?" is as important as testing "does it work?"


Behind the Scenes: Architecture That Scales ๐Ÿ—๏ธ

One thing I've learned from shipping multiple Unity projects is that good architecture is invisible - it just makes everything else work smoothly. Let me pull back the curtain on how I structured this system for maintainability and growth.

๐Ÿ“ Project Structure That Makes Sense

When other developers join the project, they can immediately understand what does what:

Assets/Scripts/
โ”œโ”€โ”€ ๐Ÿ  HouseManager.cs           # Smart house placement logic
โ”œโ”€โ”€ ๐Ÿ›ฃ๏ธ RoadManager.cs            # AI-powered road pathfinding
โ”œโ”€โ”€ ๐Ÿง  GameManager.cs            # Central nervous system
โ””โ”€โ”€ ๐Ÿงช Tests/
    โ”œโ”€โ”€ HouseManagerTests.cs          # Core functionality validation
    โ”œโ”€โ”€ HouseManagerAdvancedTests.cs  # Complex scenario testing
    โ”œโ”€โ”€ HouseManagerRegressionTests.cs # Bug prevention armor
    โ”œโ”€โ”€ TestableHouseManager.cs       # Testing infrastructure
    โ””โ”€โ”€ HouseManagerTestRunner.cs     # One-click test execution

Dev Philosophy: If someone can't understand your project structure in 30 seconds, it's too complex.

๐ŸŽฏ Design Patterns That Actually Matter

I didn't just use design patterns because they're "best practice" - each one solved a specific problem I was facing:

Singleton Pattern โ†’ The Information Hub

  • Problem: Multiple managers needed shared game state
  • Solution: GameManager provides single source of truth
  • Benefit: No more "which system knows what?" confusion

Strategy Pattern โ†’ The Smart Builder

  • Problem: Houses need different placement rules based on road layout
  • Solution: Different building strategies for horizontal vs vertical roads
  • Benefit: Easy to add new building types without touching existing code

Observer Pattern โ†’ The City Simulator

  • Problem: VillagerManager needs to know when new housing is available
  • Solution: Automatic notifications when houses are built
  • Benefit: Emergent city simulation without tight coupling

Template Method โ†’ The Consistency Engine

  • Problem: Coordinate conversion was scattered and error-prone
  • Solution: Standardized conversion methods across all managers
  • Benefit: Eliminated 90% of coordinate-related bugs

โšก Performance: Making It Smooth for Players

Players don't care about your clever algorithms - they care that the game feels responsive. Here's how I optimized for the player experience:

Smart Pathfinding Performance

  • The Problem: Pathfinding could freeze the game for complex routes
  • The Solution: Multi-tier approach that tries fast methods first
  • The Result: 95% of roads use instant straight-line paths

Preview System Optimization

  • The Problem: Real-time building previews were causing frame drops
  • The Solution: Only update changed preview elements, cache everything else
  • The Result: Smooth 60fps even during complex building operations

Memory Management for Long Play Sessions

  • The Problem: Preview objects were leaking memory over time
  • The Solution: Proper cleanup and object pooling
  • The Result: Players can build massive cities without performance degradation

Performance Tip: The best optimization is often not optimizing - choosing the right algorithm from the start beats micro-optimizations every time.


Seeing It In Action: The Player Experience ๐ŸŽฎ

The best way to understand this system is to see how it feels from a player's perspective. Every technical decision was made to create moments of delight and eliminate frustration.

๐Ÿ  Building Your First Neighborhood

Picture this: You're a new player who just started the game...

Step 1: The Magic Drag โœจ

  • You click somewhere near a road and start dragging
  • Instantly, a semi-transparent preview shows exactly where houses will appear
  • The AI automatically calculates the perfect size and orientation - no manual rotation needed!
// Behind the scenes: This simple drag gesture triggers sophisticated analysis
// 1. Player clicks and drags โ†’ Area defined
// 2. AI analyzes nearby roads โ†’ Optimal orientation determined
// 3. Smart sizing โ†’ Realistic urban planning rules applied
// 4. Instant preview โ†’ Player sees exactly what they're getting

Step 2: The Satisfying Snap ๐ŸŽฏ

  • Release the mouse and snap - perfectly aligned houses appear
  • They're automatically connected to the road network
  • They follow real urban planning principles (max 2 cells deep from roads)
  • It just feels right without the player knowing why

Player Feedback: "I love how the houses just know where they should go. It feels like the game is reading my mind!" - Beta tester Sarah

๐Ÿ›ฃ๏ธ Connecting Your City with Smart Roads

The Path-Finding Magic ๐Ÿง 

You want to connect two distant areas of your city...

  • Click your starting point (maybe near the town center)
  • Drag toward your destination (perhaps a new residential area)
  • Watch in real-time as the AI calculates the optimal route:
// The AI's decision process in real-time:
// ๐ŸŽฏ "Can I go straight vertically?" โ†’ Try vertical line
// โžก๏ธ "How about straight horizontally?" โ†’ Try horizontal line
// ๐Ÿง  "Need to be clever?" โ†’ Advanced pathfinding kicks in
// โœ… "Found the perfect route!" โ†’ Show preview to player

Smart Obstacle Avoidance ๐Ÿ—ป

The AI doesn't just find a path - it finds the smart path:

  • Around Buildings: Automatically routes around existing structures
  • Respecting Terrain: Knows water and mountains are no-go zones
  • Efficient Routing: Prefers straight lines like real highway planning
  • Visual Feedback: Shows you the route before you commit

๐Ÿ”ง For Fellow Developers: The Technical Magic

Coordinate Conversion Made Simple ๐Ÿ“

One of the biggest headaches in isometric games is coordinate conversion. Here's how I made it bulletproof:

// From messy Unity grid coordinates to clean game logic:
Vector3Int messyUnityCoords = grid.WorldToCell(mousePosition);
Vector3Int cleanGameCoords = GameManager.Instance.RuntimeToMapCoordinates(messyUnityCoords);

// Now use clean coordinates for all game logic
bool canPlayerBuildHere = GameManager.Instance.IsBuildableCell(cleanGameCoords);

This simple conversion eliminated hundreds of potential bugs and made the entire system predictable.

The Power of Immediate Feedback โšก

Everything in this system is designed for immediate feedback:

  • Building previews update in real-time as you drag
  • Road paths are calculated and shown instantly
  • Invalid areas simply don't highlight - no confusing error messages
  • Success feedback is immediate and satisfying

Dev Insight: The difference between a good building system and a great one is the preview system. Players need to see their intentions reflected immediately.


Lessons Learned: What I'd Do Differently ๐Ÿค”

Lessons Learned

Building this system taught me as much about game development as it did about urban planning. Here are the biggest lessons that other developers can learn from:

๐Ÿง  Start with the Player Experience, Not the Code

My Original Approach: I started by building a generic "grid placement system" and tried to make it work for buildings.

What I Learned: This backwards approach led to months of frustration. Buildings felt mechanical and lifeless.

The Better Way: I started over by playing Cities: Skylines for hours, taking notes on what felt good, then building systems to recreate those feelings.

Lesson: Good game systems serve player emotions first, technical requirements second.

๐Ÿงช Test-Driven Architecture for Game Systems

My Resistance: "I'll add tests later when the features are done."

Reality Check: One small coordinate bug took me 3 days to track down across multiple managers because I had no systematic way to isolate the problem.

The Breakthrough: I discovered that testing game systems requires a fundamentally different approach than web development. You need to design for testability from day one.

The Game-Changer: Creating testable abstractions that decouple Unity dependencies from core logic. This meant I could test pathfinding algorithms, coordinate conversions, and placement rules without starting Unity at all.

Lesson: In game development, your testing strategy must account for state management, frame-based updates, and complex object interactions that don't exist in typical software.

๐ŸŽฏ Simplicity Scales, Complexity Doesn't

My Over-Engineering Phase: I tried to make the system handle every possible edge case from day one.

The Problem: The code became so complex that simple changes broke everything.

The Solution: I started with the simplest possible version that worked, then added complexity only when needed.

Example: The road pathfinding started as just straight lines. I added BFS only when players actually needed complex routing.

Lesson: Build the minimum viable system first, then evolve it based on real usage.

๐Ÿ”„ Architecture Decisions Compound Over Time

Early Mistake: Each manager tracked its own state independently.

The Pain: Houses didn't know about roads, roads didn't know about houses, chaos ensued.

The Fix: Centralized state management through GameManager.

The Payoff: This single architectural decision eliminated 90% of coordination bugs and made new features trivial to add.

Lesson: Good architecture is an investment that pays dividends for years.


Want to Build Your Own? ๐Ÿ› ๏ธ

If this post inspired you to build your own intelligent placement systems, here are my top recommendations:

Start Small, Think Big

  1. Begin with a simple grid-based placement system
  2. Add one intelligent feature at a time (road alignment, terrain awareness, etc.)
  3. Test each feature thoroughly before moving to the next
  4. Always prioritize player experience over technical complexity

Essential Resources

  • "Game Programming Patterns" by Robert Nystrom - Architecture guidance that actually works
  • Unity Test Runner Documentation - For building bulletproof systems
  • "The Art of Game Design" by Jesse Schell - Understanding what makes placement feel good

Final Thoughts: Building Games That Feel Alive ๐Ÿ’ซ

Final Screenshot

After two weeks of intensive development, watching players effortlessly create beautiful cities with this system brings me more joy than any technical achievement. The best game systems are invisible - they amplify player creativity without getting in the way.

The HouseManager and RoadManager aren't just code - they're the foundation of countless player stories. Every perfectly placed house, every efficiently routed road, every thriving neighborhood represents a moment where technology served human creativity.

Whether you're building your first Unity project or your fiftieth, remember that great game systems solve human problems, not technical ones. Focus on the moments of delight you want to create, then build the technology to support them.

Happy building, and may your cities be forever prosperous! ๐Ÿ†