Unity 3D C# Architecture Grid Snapping Raycast Placement

Tower Defense

A tactical Unity 3D framework featuring real-time Raycast mouse tracking, 1x1 grid snapping, and dynamic placement validation.

Placement System

Unity 3D C# Raycasting Real-time Preview

At the core of this project is the TowerPlacementManager. This system enables players to intuitively place defensive towers with real-time visual feedback reflecting placement validity.

The preview dynamically toggles material colors (green for valid, red for obstructed) based on Physics collision checks, delivering a smooth user experience.

HandlePlacementPreview.cs
C#
void HandlePlacementPreview()
{
    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

    if (Physics.Raycast(ray, out RaycastHit hit, 1000f, groundLayer))
    {
        Vector3 placementPos = hit.point;

        // Snap coordinates to 1x1 grid
        placementPos = SnapToGrid(placementPos);

        towerPreview.transform.position = placementPos;

        // Check if placement is valid
        bool isValid = IsValidPlacement(placementPos);
        UpdatePreviewColor(isValid);
    }
}

Grid Snapping Algorithm

To ensure strict architectural alignment, the system utilizes a custom snapping algorithm. This locks all tower placements onto a precise 1x1 unit grid, which is crucial for tactical maze building and enemy pathfinding.

SnapToGrid.cs
C#
Vector3 SnapToGrid(Vector3 position)
{
    float gridSize = 1f;
    float x = Mathf.Round(position.x / gridSize) * gridSize;
    float z = Mathf.Round(position.z / gridSize) * gridSize;
    return new Vector3(x, position.y, z);
}

Placement Validation

Validation is calculated via Physics.OverlapSphere, detecting whether a proposed tower location overlaps existing obstacles or defensive structures, preventing stacking bugs.

IsValidPlacement.cs
C#
bool IsValidPlacement(Vector3 position)
{
    // Check if position is blocked by other towers
    Collider[] colliders = Physics.OverlapSphere(position, 0.5f);

    foreach (var col in colliders)
    {
        if (col.CompareTag("Tower"))
            return false;
    }

    return true;
}

Full Manager Architecture

The full C# component managing preview instantiation, input polling, placement confirmation, and cancellation state transitions:

TowerPlacementManager.cs
C#
using UnityEngine;

public class TowerPlacementManager : MonoBehaviour
{
    [SerializeField] private LayerMask groundLayer;
    [SerializeField] private GameObject towerPrefab;
    [SerializeField] private int towerCost = 100;
    [SerializeField] private Material previewMaterial;
    [SerializeField] private Color validColor = new Color(0, 1, 0, 0.5f);
    [SerializeField] private Color invalidColor = new Color(1, 0, 0, 0.5f);

    private GameObject towerPreview;
    private bool isPlacementMode = false;
    private Renderer[] previewRenderers;

    void Update()
    {
        if (isPlacementMode)
        {
            HandlePlacementPreview();

            if (Input.GetMouseButtonDown(0))
            {
                TryPlaceTower();
            }

            if (Input.GetMouseButtonDown(1) || Input.GetKeyDown(KeyCode.Escape))
            {
                ExitPlacementMode();
            }
        }
    }

    public void EnterPlacementMode(GameObject prefab, int cost)
    {
        if (!GameManager.Instance.CanAfford(cost)) return;

        isPlacementMode = true;
        towerPrefab = prefab;
        towerCost = cost;

        towerPreview = Instantiate(prefab);
        SetPreviewMaterial(towerPreview);
        UpdatePreviewPosition();
    }

    private void UpdatePreviewPosition()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out RaycastHit hit, 1000f, groundLayer))
        {
            Vector3 pos = SnapToGrid(hit.point);
            towerPreview.transform.position = pos;
            UpdatePreviewColor(IsValidPlacement(pos));
        }
    }
}