건설 시스템 기초 생성 및 Kaykit Medival 애셋 추가
This commit is contained in:
153
Assets/Scripts/BuildingManager.cs
Normal file
153
Assets/Scripts/BuildingManager.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
using System.Collections.Generic;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
public class BuildingManager : NetworkBehaviour
|
||||
{
|
||||
public static BuildingManager Instance { get; private set; }
|
||||
|
||||
[Header("Settings")]
|
||||
public float gridSize = 1f;
|
||||
public LayerMask groundLayer;
|
||||
|
||||
[Header("Building Database")]
|
||||
public List<BuildingData> availableBuildings = new List<BuildingData>();
|
||||
|
||||
private List<Building> placedBuildings = new List<Building>();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
public Vector3 SnapToGrid(Vector3 worldPosition)
|
||||
{
|
||||
return new Vector3(
|
||||
Mathf.Round(worldPosition.x / gridSize) * gridSize,
|
||||
worldPosition.y,
|
||||
Mathf.Round(worldPosition.z / gridSize) * gridSize
|
||||
);
|
||||
}
|
||||
|
||||
public Vector3Int WorldToGrid(Vector3 worldPosition)
|
||||
{
|
||||
return new Vector3Int(
|
||||
Mathf.RoundToInt(worldPosition.x / gridSize),
|
||||
Mathf.RoundToInt(worldPosition.y / gridSize),
|
||||
Mathf.RoundToInt(worldPosition.z / gridSize)
|
||||
);
|
||||
}
|
||||
|
||||
public Vector3 GridToWorld(Vector3Int gridPosition)
|
||||
{
|
||||
return new Vector3(
|
||||
gridPosition.x * gridSize,
|
||||
gridPosition.y * gridSize,
|
||||
gridPosition.z * gridSize
|
||||
);
|
||||
}
|
||||
|
||||
public bool IsValidPlacement(BuildingData data, Vector3 position, int rotation, out Vector3 groundPosition)
|
||||
{
|
||||
groundPosition = position;
|
||||
|
||||
// Ground check
|
||||
if (!CheckGround(position, out groundPosition))
|
||||
return false;
|
||||
|
||||
// IMPORTANT: Snap to grid BEFORE checking overlap!
|
||||
// Otherwise we check the raw cursor position which might overlap
|
||||
Vector3 snappedPosition = SnapToGrid(groundPosition);
|
||||
groundPosition = snappedPosition; // Update groundPosition to snapped value
|
||||
|
||||
// Overlap check using GRID SIZE from BuildingData (not actual colliders)
|
||||
Vector3 gridSize = data.GetSize(rotation);
|
||||
|
||||
// Shrink bounds slightly to allow buildings to touch without overlapping
|
||||
// This prevents Bounds.Intersects() from returning true for adjacent buildings
|
||||
Vector3 shrunkSize = gridSize - Vector3.one * 0.01f;
|
||||
Bounds checkBounds = new Bounds(snappedPosition + Vector3.up * gridSize.y * 0.5f, shrunkSize);
|
||||
|
||||
foreach (var building in placedBuildings)
|
||||
{
|
||||
if (building == null) continue;
|
||||
|
||||
// Compare grid bounds, not collider bounds
|
||||
Bounds buildingGridBounds = building.GetGridBounds();
|
||||
|
||||
if (checkBounds.Intersects(buildingGridBounds))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the grid bounds for a building at a given position
|
||||
/// Useful for preview visualization
|
||||
/// </summary>
|
||||
public Bounds GetPlacementBounds(BuildingData data, Vector3 position, int rotation)
|
||||
{
|
||||
Vector3 gridSize = data.GetSize(rotation);
|
||||
return new Bounds(position + Vector3.up * gridSize.y * 0.5f, gridSize);
|
||||
}
|
||||
|
||||
private bool CheckGround(Vector3 position, out Vector3 groundPosition)
|
||||
{
|
||||
groundPosition = position;
|
||||
|
||||
// Raycast down to find ground
|
||||
if (Physics.Raycast(position + Vector3.up * 10f, Vector3.down, out RaycastHit hit, 20f, groundLayer))
|
||||
{
|
||||
groundPosition = hit.point;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
public void PlaceBuildingServerRpc(int buildingIndex, Vector3 position, int rotation)
|
||||
{
|
||||
if (buildingIndex < 0 || buildingIndex >= availableBuildings.Count)
|
||||
return;
|
||||
|
||||
BuildingData data = availableBuildings[buildingIndex];
|
||||
|
||||
// IsValidPlacement now returns snapped position in groundPosition
|
||||
if (!IsValidPlacement(data, position, rotation, out Vector3 snappedPosition))
|
||||
return;
|
||||
|
||||
Vector3Int gridPosition = WorldToGrid(snappedPosition);
|
||||
|
||||
// Spawn building at snapped position
|
||||
GameObject buildingObj = Instantiate(data.prefab, snappedPosition + data.placementOffset, Quaternion.Euler(0, rotation * 90f, 0));
|
||||
NetworkObject netObj = buildingObj.GetComponent<NetworkObject>();
|
||||
|
||||
if (netObj != null)
|
||||
{
|
||||
netObj.Spawn();
|
||||
|
||||
Building building = buildingObj.GetComponent<Building>();
|
||||
if (building == null)
|
||||
building = buildingObj.AddComponent<Building>();
|
||||
|
||||
building.Initialize(data, gridPosition, rotation);
|
||||
placedBuildings.Add(building);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveBuilding(Building building)
|
||||
{
|
||||
if (placedBuildings.Contains(building))
|
||||
placedBuildings.Remove(building);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user