Files
Northbound/BUILDING_SYSTEM_SETUP.md

324 lines
11 KiB
Markdown

# 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`:
1. **ToggleBuildMode** (Button) → Keyboard B
2. **Rotate** (Value/Axis) → Keyboard R
3. **Build** (Button) → Mouse Left Button
After adding, save the asset to regenerate the code.
### Step 1: Create BuildingData Assets
1. Right-click in Project window → Create → Northbound → Building Data
2. 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:
1. Add `NetworkObject` component
2. Add `Building` component
3. Ensure it has a `Renderer` for preview
4. Add colliders for gameplay (player collision, physics)
5. **Important:** Colliders are NOT used for placement validation - only BuildingData grid size matters
6. Set the prefab's layer to anything EXCEPT the Ground layer (buildings will be raycast-ignored during placement)
### Step 3: Setup BuildingManager
1. Create empty GameObject in your scene named "BuildingManager"
2. Add `BuildingManager` component
3. Add `NetworkObject` component
4. 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
1. Open your Player prefab
2. Add `BuildingPlacement` component
3. 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
1. Create a layer named "Ground" (or your preferred name)
2. Assign this layer to terrain/ground objects
3. **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 `.performed` events in `OnNetworkSpawn`
- Unsubscribe in `OnNetworkDespawn`
- No hardcoded keys - all controls defined in Input Actions asset
- Easy to rebind without code changes
**Pattern:**
```csharp
_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)
```csharp
// 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
```csharp
[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
1. Player presses **B** → enters build mode
2. Preview appears at mouse cursor position
3. 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
4. System checks placement validity:
- Is there ground below?
- Does grid bounds overlap with existing buildings?
5. Preview shows **green** if valid, **red** if invalid
6. Player rotates with **R** if desired
7. Player clicks to confirm placement
8. **ServerRpc** sent to server to spawn building
9. 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=2` occupies 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:
```csharp
public enum BuildingCategory { Housing, Production, Defense }
public BuildingCategory category;
```
### Add Resource Costs
```csharp
[Header("Cost")]
public int woodCost;
public int stoneCost;
```
Then check resources in `BuildingManager.PlaceBuildingServerRpc()`
### Add Build Range Limit
In `BuildingPlacement.UpdatePreviewPosition()`:
```csharp
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)