건설 시스템 기초 생성 및 Kaykit Medival 애셋 추가
This commit is contained in:
323
BUILDING_SYSTEM_SETUP.md
Normal file
323
BUILDING_SYSTEM_SETUP.md
Normal file
@@ -0,0 +1,323 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user