# 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(); // 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(); 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)