Why a Comprehensive Building System Matters

A detailed building system is at the heart of most simulation games. Whether you’re developing a city simulator, survival game, or sandbox environment, a feature-rich system not only enhances player creativity but also defines core gameplay mechanics. This guide walks you through creating an all-encompassing building system in Unity, with modular, reusable code that covers placement, validation, resource management, upgrades, destruction, saving/loading, and multiplayer integration.


What You’ll Learn

In this guide, we’ll implement a professional-grade building system for simulation games. Each module can work independently or integrate into a larger framework, ensuring flexibility for diverse game types.

Key Features:

  1. Object Placement: Snap objects to grids or free-placement modes.
  2. Validation: Real-time placement checks for overlaps, terrain compatibility, and resource constraints.
  3. Resource Management: Monitor and deduct resources during construction.
  4. Structure Interactions: Upgrades, demolitions, and modifications.
  5. Save/Load System: Persistent building states.
  6. Multiplayer Compatibility: Synchronization across clients in a networked game.

Core Modules with Code Examples

1. Placement System

Handles object snapping and positioning.

using UnityEngine;

public class BuildingPlacement : MonoBehaviour
{
    public GameObject previewPrefab;
    public LayerMask groundLayer;
    public float gridSize = 1f;

    private GameObject previewObject;
    private bool canPlace = false;

    void Update()
    {
        UpdatePreview();
        HandlePlacementInput();
    }

    private void UpdatePreview()
    {
        if (previewPrefab == null) return;

        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity, groundLayer))
        {
            Vector3 gridPosition = new Vector3(
                Mathf.Round(hit.point.x / gridSize) * gridSize,
                Mathf.Round(hit.point.y / gridSize) * gridSize,
                Mathf.Round(hit.point.z / gridSize) * gridSize
            );

            if (previewObject == null)
            {
                previewObject = Instantiate(previewPrefab);
                previewObject.GetComponent<Collider>().enabled = false;
                SetMaterialTransparency(previewObject, true);
            }

            previewObject.transform.position = gridPosition;
            canPlace = CheckPlacementValidity(gridPosition);
        }
    }

    private void HandlePlacementInput()
    {
        if (canPlace && Input.GetMouseButtonDown(0)) // Left-click to place
        {
            GameObject placedObject = Instantiate(previewPrefab, previewObject.transform.position, Quaternion.identity);
            placedObject.GetComponent<Collider>().enabled = true;
            Destroy(previewObject);
            previewObject = null;
        }
    }

    private bool CheckPlacementValidity(Vector3 position)
    {
        Collider[] overlaps = Physics.OverlapSphere(position, gridSize / 2);
        return overlaps.Length == 0;
    }

    private void SetMaterialTransparency(GameObject obj, bool transparent)
    {
        foreach (Renderer renderer in obj.GetComponentsInChildren<Renderer>())
        {
            foreach (Material mat in renderer.materials)
            {
                Color color = mat.color;
                mat.color = new Color(color.r, color.g, color.b, transparent ? 0.5f : 1f);
            }
        }
    }
}

2. Resource Management System

Tracks and manages player resources for building.

public class ResourceManager : MonoBehaviour
{
    public int wood = 100;
    public int stone = 50;

    public bool HasEnoughResources(int woodCost, int stoneCost)
    {
        return wood >= woodCost && stone >= stoneCost;
    }

    public void DeductResources(int woodCost, int stoneCost)
    {
        if (HasEnoughResources(woodCost, stoneCost))
        {
            wood -= woodCost;
            stone -= stoneCost;
        }
    }

    public void AddResources(int woodAmount, int stoneAmount)
    {
        wood += woodAmount;
        stone += stoneAmount;
    }
}

3. Validation System

Ensures objects don’t overlap or violate constraints.

public class PlacementValidator : MonoBehaviour
{
    public LayerMask invalidPlacementLayers;
    public float checkRadius = 0.5f;

    public bool IsValidPlacement(Vector3 position)
    {
        Collider[] overlaps = Physics.OverlapSphere(position, checkRadius, invalidPlacementLayers);
        return overlaps.Length == 0;
    }
}

5. Save and Load System

Persists building data across sessions.

using UnityEngine;
using System.Collections.Generic;

[System.Serializable]
public class BuildingData
{
    public Vector3 position;
    public Quaternion rotation;
    public string prefabName;
}

public class SaveLoadSystem : MonoBehaviour
{
    public List<BuildingData> savedBuildings = new List<BuildingData>();

    public void SaveBuildings(List<GameObject> buildings)
    {
        savedBuildings.Clear();

        foreach (var building in buildings)
        {
            BuildingData data = new BuildingData
            {
                position = building.transform.position,
                rotation = building.transform.rotation,
                prefabName = building.name
            };
            savedBuildings.Add(data);
        }
    }

    public void LoadBuildings()
    {
        foreach (var data in savedBuildings)
        {
            GameObject prefab = Resources.Load<GameObject>(data.prefabName);
            Instantiate(prefab, data.position, data.rotation);
        }
    }
}

6. Multiplayer Synchronization

Synchronizes building states across players in a networked environment. (Requires Mirror or similar networking solution.)

using Mirror;

public class NetworkBuilding : NetworkBehaviour
{
    [SyncVar] public Vector3 position;
    [SyncVar] public Quaternion rotation;

    public void Initialize(Vector3 pos, Quaternion rot)
    {
        position = pos;
        rotation = rot;
    }

    [Server]
    public static void CreateBuilding(GameObject prefab, Vector3 position, Quaternion rotation)
    {
        GameObject building = Instantiate(prefab, position, rotation);
        NetworkServer.Spawn(building);
    }
}

Advantages and Limitations

Advantages

  • Modular Design: Each system is independent, allowing for easy integration or removal.
  • Scalability: Supports future extensions like advanced AI or environmental interactions.
  • Realism: Provides features like resource management and validation for a deeper gameplay experience.

Limitations

  • Performance Overhead: Real-time validation and multiplayer synchronization can be resource-intensive.
  • Complexity: Requires careful debugging to handle interactions between modules.

Final Thoughts

A well-rounded building system is essential for creating engaging simulation games. The modular components and complete code examples provided here equip you to implement a feature-rich building system in Unity. By focusing on flexibility and scalability, this guide ensures your system is ready for diverse gameplay scenarios and future expansions.

Start implementing your building system today and unlock endless creative possibilities for your players!

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다