11 KiB
Building System Setup Guide
Overview
The building system allows players to place buildings with:
- Grid-based snapping for aligned placement
- Overlap detection to prevent buildings from colliding
- Ground validation to ensure buildings are placed on terrain
- Ghost preview with green (valid) / red (invalid) visual feedback
- Network synchronization across all players
Components
1. BuildingData (ScriptableObject)
Defines properties for each building type:
- Building name and prefab reference
- Grid size (width, length, height)
- Placement offset and rotation settings
2. Building (MonoBehaviour)
Component attached to placed building instances. Tracks:
- Grid position
- Rotation (0-3, representing 0°, 90°, 180°, 270°)
- Reference to BuildingData
3. BuildingManager (NetworkBehaviour - Singleton)
Central manager that:
- Maintains list of all placed buildings
- Validates placement (ground check + overlap detection)
- Handles grid snapping
- Spawns buildings on network via ServerRpc
4. BuildingPlacement (NetworkBehaviour)
Player-side building interface:
- Uses new Input System (
PlayerInputActions,Keyboard.current,Mouse.current) - Handles input (B key to toggle, Q/E to rotate, click to place)
- Shows ghost preview at mouse position
- Updates preview material (green/red) based on validity
- Proper input lifecycle management (enable/disable/dispose)
Setup Instructions
Step 0: Setup Input Actions (Required First!)
Before using the building system, you MUST add the required input actions.
See INPUT_ACTIONS_SETUP.md for detailed instructions.
Quick summary - Add these to your Player action map in InputSystem_Actions.inputactions:
- ToggleBuildMode (Button) → Keyboard B
- Rotate (Value/Axis) → Keyboard R
- Build (Button) → Mouse Left Button
After adding, save the asset to regenerate the code.
Step 1: Create BuildingData Assets
- Right-click in Project window → Create → Northbound → Building Data
- Configure your building:
- Assign the building prefab
- Set grid size (width, length, height)
- Adjust placement offset if needed
- Enable/disable rotation
Step 2: Setup Building Prefabs
For each building prefab:
- Add
NetworkObjectcomponent - Add
Buildingcomponent - Ensure it has a
Rendererfor preview - Add colliders for gameplay (player collision, physics)
- Important: Colliders are NOT used for placement validation - only BuildingData grid size matters
- Set the prefab's layer to anything EXCEPT the Ground layer (buildings will be raycast-ignored during placement)
Step 3: Setup BuildingManager
- Create empty GameObject in your scene named "BuildingManager"
- Add
BuildingManagercomponent - Add
NetworkObjectcomponent - Configure settings:
- Grid Size: Size of one grid unit (default: 1)
- Ground Layer: Layer mask for valid ground (e.g., "Ground")
- Available Buildings: Add your BuildingData assets
Step 4: Add BuildingPlacement to Player
- Open your Player prefab
- Add
BuildingPlacementcomponent - Configure:
- Ground Layer: Same as BuildingManager
- Max Placement Distance: How far player can place (default: 100)
- Valid Material: Green transparent material (auto-created if null)
- Invalid Material: Red transparent material (auto-created if null)
Step 5: Setup Layers
- Create a layer named "Ground" (or your preferred name)
- Assign this layer to terrain/ground objects
- Important: Make sure building prefabs are on a different layer (not Ground)
- This allows raycasting through buildings to hit the ground
- You can place buildings even when cursor is over existing buildings
Controls
Default bindings (customizable in Input Actions):
- B: Toggle build mode on/off
- R: Rotate building (90° increments)
- Left Mouse Button: Place building (if valid position)
- Mouse Movement: Move preview to placement location
Input System
The building system uses Unity's new Input System with action callbacks:
- Actions:
ToggleBuildMode,Rotate,Build - Subscribe to
.performedevents inOnNetworkSpawn - Unsubscribe in
OnNetworkDespawn - No hardcoded keys - all controls defined in Input Actions asset
- Easy to rebind without code changes
Pattern:
_inputActions.Player.ToggleBuildMode.performed += OnToggleBuildMode;
_inputActions.Player.Rotate.performed += OnRotate;
_inputActions.Player.Build.performed += OnBuild;
This uses callback-based input, similar to the pattern shown in InputSystem_Actions.cs examples.
Technical Implementation
Modern Input System (Callback Pattern)
// Initialization in OnNetworkSpawn
_inputActions = new PlayerInputActions();
_inputActions.Player.ToggleBuildMode.performed += OnToggleBuildMode;
_inputActions.Player.Rotate.performed += OnRotate;
_inputActions.Player.Build.performed += OnBuild;
_inputActions.Enable();
// Callback handlers
private void OnToggleBuildMode(InputAction.CallbackContext context)
{
ToggleBuildMode();
}
private void OnRotate(InputAction.CallbackContext context)
{
float rotateValue = context.ReadValue<float>();
// Positive = rotate right, Negative = rotate left
}
private void OnBuild(InputAction.CallbackContext context)
{
TryPlaceBuilding();
}
// Cleanup in OnNetworkDespawn
_inputActions.Player.ToggleBuildMode.performed -= OnToggleBuildMode;
_inputActions.Player.Rotate.performed -= OnRotate;
_inputActions.Player.Build.performed -= OnBuild;
_inputActions.Disable();
_inputActions.Dispose();
Modern Netcode RPC Pattern
[ServerRpc(RequireOwnership = false)]
public void PlaceBuildingServerRpc(int buildingIndex, Vector3 position, int rotation, ServerRpcParams serverRpcParams = default)
{
// Validation
if (!IsValidPlacement(...)) return;
// Server-side spawn
GameObject obj = Instantiate(...);
NetworkObject netObj = obj.GetComponent<NetworkObject>();
netObj.Spawn(); // Automatically syncs to all clients
}
How It Works
Build Mode Flow
- Player presses B → enters build mode
- Preview appears at mouse cursor position
- System raycasts to find ground (two-stage):
- Method 1: Direct raycast to ground, ignoring building colliders
- Method 2: If ground not visible (buildings blocking), raycast to anything, then cast downward from that point to find ground below
- This allows placement even in completely surrounded cells
- System checks placement validity:
- Is there ground below?
- Does grid bounds overlap with existing buildings?
- Preview shows green if valid, red if invalid
- Player rotates with R if desired
- Player clicks to confirm placement
- ServerRpc sent to server to spawn building
- Building synchronized to all clients
Validation System (Grid-Based)
IsValidPlacement() checks:
├── Ground Check: Raycast down to find ground
└── Overlap Check: Compare GRID BOUNDS (not colliders!)
├── Uses BuildingData width/length/height
├── NOT the actual collider size
└── Rotates grid size automatically (90°/180°/270°)
Important: Collision detection uses the grid size from BuildingData, not the physical colliders on the building prefab. This means:
- A building with
width=2, length=3, height=2occupies a 2x3 grid area - Even if the visual model is smaller/larger, the grid size determines overlap
- This allows consistent, predictable building placement
Grid Snapping
- World position → rounded to nearest grid size
- Example: gridSize=2, position (3.7, 0, 5.2) → snaps to (4, 0, 6)
Rotation System
- Rotation values: 0, 1, 2, 3 → 0°, 90°, 180°, 270°
- Building size auto-swaps width/length when rotated 90° or 270°
Example Building Setup
Small House (2x2)
Name: Small House
Prefab: SmallHousePrefab
Width: 2
Length: 2
Height: 3
Allow Rotation: true
Wall Segment (1x3)
Name: Wall
Prefab: WallPrefab
Width: 1
Length: 3
Height: 2
Allow Rotation: true
Debug Visualization
The system includes visual debugging to help you understand grid placement:
In Scene View (when build mode is active):
- Green/Red Wire Cube: Shows the grid bounds being checked
- Green = valid placement
- Red = invalid (overlapping or no ground)
- Yellow Grid Cells: Shows individual grid cells occupied
- Yellow Sphere: Grid origin point (snapped position)
On Placed Buildings:
- Cyan Wire Cube: Shows the grid bounds of placed buildings
- Yellow Wire Cube (when selected): Highlights the selected building's grid
- Magenta Sphere: Grid position origin
Enable/disable with:
- BuildingPlacement → Show Grid Bounds
- Building → Show Grid Bounds
Troubleshooting
Preview doesn't appear:
- Check if BuildingManager.Instance exists
- Verify availableBuildings list has BuildingData
- Ensure Ground layer is set correctly
Can't place buildings:
- Check ground layer mask matches terrain
- Verify building prefabs have NetworkObject
- Ensure BuildingManager has NetworkObject and is spawned
Buildings overlap despite visual gap:
- Remember: Collision uses grid size from BuildingData, not visual model size
- Check the grid bounds in Scene view (cyan wire cubes)
- Adjust width/length/height in BuildingData to match desired grid footprint
Can't place buildings when cursor is near existing buildings:
- FIXED: System now uses RaycastAll and ignores building colliders
- Raycasts pass through buildings to hit the ground
- You can place buildings even when cursor is hovering over old ones
- Just make sure buildings are NOT on the Ground layer
Can't place in center cell when surrounded by buildings:
- FIXED: Two-stage raycast system
- If direct ground raycast fails, system raycasts to any object then down to find ground
- Works even when all adjacent cells are built and blocking the ground view
- You can fill in gaps surrounded by buildings
Network issues:
- BuildingManager must be spawned on network
- Ensure player has BuildingPlacement component with IsOwner
Extending the System
Add Building Categories
Edit BuildingData.cs and add:
public enum BuildingCategory { Housing, Production, Defense }
public BuildingCategory category;
Add Resource Costs
[Header("Cost")]
public int woodCost;
public int stoneCost;
Then check resources in BuildingManager.PlaceBuildingServerRpc()
Add Build Range Limit
In BuildingPlacement.UpdatePreviewPosition():
float distanceToPlayer = Vector3.Distance(transform.position, snappedPosition);
if (distanceToPlayer > maxBuildRange)
isValid = false;
Multiple Building Selection
Add UI buttons that change selectedBuildingIndex in BuildingPlacement
Notes
- Buildings are spawned on the server via ServerRpc
- Preview is local-only (each player sees their own preview)
- Ground layer should include all walkable/buildable surfaces
- Grid size should match your game's scale (larger buildings = larger grid)