Код: Выделить всё
public class GridManager : MonoBehaviour
{
public int width = 20;
public int height = 20;
public float cellSize = 1f;
private bool[,] occupied;
void Awake()
{
occupied = new bool[width, height];
}
public Vector2Int WorldToGrid(Vector3 pos)
{
int x = Mathf.FloorToInt(pos.x / cellSize);
int y = Mathf.FloorToInt(pos.z / cellSize);
return new Vector2Int(x, y);
}
public Vector3 GridToWorld(int x, int y)
{
// Sol-alt köşe
return new Vector3(x * cellSize, 0, y * cellSize);
}
public bool IsInBounds(int x, int y)
{
return x >= 0 && x < width && y >= 0 && y < height;
}
public bool CanPlaceObject(int x, int y)
{
return IsInBounds(x, y) && !occupied[x, y];
}
public void OccupyCell(int x, int y)
{
if (IsInBounds(x, y))
occupied[x, y] = true;
}
#if UNITY_EDITOR
private void OnDrawGizmos()
{
Gizmos.color = Color.gray;
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
{
Vector3 pos = GridToWorld(x, y);
Gizmos.DrawWireCube(pos + new Vector3(cellSize / 2, 0, cellSize / 2), new Vector3(cellSize, 0.02f, cellSize));
}
}
#endif
}
Код: Выделить всё
public class PrefabGridData : MonoBehaviour
{
[Tooltip("Griddeki işgal maskesi. Her Vector2Int = modelin sol-alt köşesinden kaç kare ötede.")]
public List occupiedCells = new List() { new Vector2Int(0,0) };
public Vector3 gridOffset;
public int maskWidth = 3;
public int maskHeight = 3;
#if UNITY_EDITOR
private void OnDrawGizmosSelected()
{
float cellSize = 1f;
Vector3 basePos = transform.position + gridOffset;
Gizmos.color = new Color(0.2f, 0.8f, 1f, 0.2f);
foreach (var cell in occupiedCells)
{
Vector3 cellCenter = basePos +
new Vector3((cell.x + 0.5f) * cellSize, 0.1f, (cell.y + 0.5f) * cellSize);
Gizmos.DrawCube(cellCenter, new Vector3(cellSize, 0.1f, cellSize));
Gizmos.color = Color.blue;
Gizmos.DrawWireCube(cellCenter, new Vector3(cellSize, 0.1f, cellSize));
Gizmos.color = new Color(0.2f, 0.8f, 1f, 0.2f);
}
}
[CustomEditor(typeof(PrefabGridData))]
public class MaskEditor : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
PrefabGridData data = (PrefabGridData)target;
EditorGUILayout.Space();
EditorGUILayout.LabelField("Visual Grid Mask Editor", EditorStyles.boldLabel);
// Temp mask buffer
bool[,] tempMask = new bool[data.maskWidth, data.maskHeight];
foreach (var cell in data.occupiedCells)
if (cell.x >= 0 && cell.x < data.maskWidth && cell.y >= 0 && cell.y < data.maskHeight)
tempMask[cell.x, cell.y] = true;
bool changed = false;
for (int y = data.maskHeight - 1; y >= 0; y--)
{
EditorGUILayout.BeginHorizontal();
for (int x = 0; x < data.maskWidth; x++)
{
bool occupied = tempMask[x, y];
GUIStyle style = new GUIStyle(GUI.skin.button);
style.normal.textColor = occupied ? Color.green : Color.red;
style.fontStyle = occupied ? FontStyle.Bold : FontStyle.Normal;
string label = occupied ? "■" : "□";
if (GUILayout.Button(label, style, GUILayout.Width(30), GUILayout.Height(30)))
{
Undo.RecordObject(data, "Grid Mask Change");
tempMask[x, y] = !tempMask[x, y];
changed = true;
}
}
EditorGUILayout.EndHorizontal();
}
// CANLI güncelleme: Her tıklamada maskeyi direkt güncelle
if (changed)
{
data.occupiedCells.Clear();
for (int x = 0; x < data.maskWidth; x++)
for (int y = 0; y < data.maskHeight; y++)
if (tempMask[x, y])
data.occupiedCells.Add(new Vector2Int(x, y));
EditorUtility.SetDirty(data);
}
}
}
#endif
}
Код: Выделить всё
using UnityEngine;
using UnityEngine.InputSystem;
using System.Collections.Generic;
///
/// BuildingSystem handles previewing and placing prefabs on a grid with arbitrary masks,
/// rotating both the prefab mesh and highlights for 0°, 90°, 180°, 270° steps,
/// with correct grid-aligned placement without hard-coded offset hacks.
///
public class BuildingSystem : MonoBehaviour
{
public GridManager grid;
public GameObject buildingPrefab;
public Material highlightValidMat;
public Material highlightInvalidMat;
private GameObject previewContainer;
private GameObject previewMesh;
private PrefabGridData gridData;
private List highlightQuads = new List();
private int rotation = 0; // 0..3 => *90°
void Update()
{
// Rotate input
if (Keyboard.current.rKey.wasPressedThisFrame) rotation = (rotation + 1) % 4;
if (Keyboard.current.qKey.wasPressedThisFrame) rotation = (rotation + 3) % 4;
// Raycast to grid
Vector2 mp = Mouse.current.position.ReadValue();
Ray ray = Camera.main.ScreenPointToRay(mp);
if (!Physics.Raycast(ray, out RaycastHit hit))
{
if (previewContainer != null) previewContainer.SetActive(false);
HideHighlights();
return;
}
// Get target cell
Vector2Int gridPos = grid.WorldToGrid(hit.point);
Vector3 basePos = grid.GridToWorld(gridPos.x, gridPos.y);
float cs = grid.cellSize;
// Create preview if needed
if (previewContainer == null)
{
previewContainer = new GameObject("PreviewPivot");
previewMesh = Instantiate(buildingPrefab, previewContainer.transform);
gridData = previewMesh.GetComponent
();
// Move mesh so its gridOffset aligns with pivot
previewMesh.transform.localPosition = -gridData.gridOffset;
SetAlpha(previewMesh, 0.5f);
}
previewContainer.SetActive(true);
// Set container position (no rotation here)
previewContainer.transform.position = basePos;
previewContainer.transform.rotation = Quaternion.identity;
// Apply mesh rotation about its gridOffset pivot
Quaternion rotQuat = Quaternion.Euler(0, rotation * 90f, 0);
previewMesh.transform.localRotation = rotQuat;
// Compute rotated mask cells
var rotatedCells = GetRotatedCells(
gridData.occupiedCells,
rotation,
gridData.maskWidth,
gridData.maskHeight
);
// Show highlights on each rotated cell
ShowHighlights(rotatedCells, cs);
// Check if building can be placed
bool canBuild = true;
foreach (var c in rotatedCells)
{
Vector3 localPos = new Vector3(
c.x * cs + cs / 2f,
0,
c.y * cs + cs / 2f
);
Vector3 worldPos = previewContainer.transform.TransformPoint(localPos);
Vector2Int gp = grid.WorldToGrid(worldPos);
if (!grid.CanPlaceObject(gp.x, gp.y)) { canBuild = false; break; }
}
// Place on click
if (Mouse.current.leftButton.wasPressedThisFrame && canBuild)
{
// Instantiate at mesh pivot world position
Vector3 placePos = previewMesh.transform.position;
Instantiate(buildingPrefab, placePos, rotQuat);
// Mark occupied cells
foreach (var c in rotatedCells)
{
Vector3 localPos = new Vector3(
c.x * cs + cs / 2f,
0,
c.y * cs + cs / 2f
);
Vector3 worldPos = previewContainer.transform.TransformPoint(localPos);
Vector2Int gp = grid.WorldToGrid(worldPos);
grid.OccupyCell(gp.x, gp.y);
}
}
}
///
/// Rotates mask cell coordinates clockwise by the given rotation step (0..3).
///
List GetRotatedCells(
List cells,
int rot,
int width,
int height
)
{
var result = new List(cells.Count);
foreach (var cell in cells)
{
int x = cell.x;
int y = cell.y;
Vector2Int rc;
switch (rot)
{
case 1: // 90°
rc = new Vector2Int(height - 1 - y, x);
break;
case 2: // 180°
rc = new Vector2Int(width - 1 - x, height - 1 - y);
break;
case 3: // 270°
rc = new Vector2Int(y, width - 1 - x);
break;
default: // 0°
rc = new Vector2Int(x, y);
break;
}
result.Add(rc);
}
return result;
}
void ShowHighlights(List mask, float cellSize)
{
while (highlightQuads.Count < mask.Count)
{
var quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
quad.transform.parent = previewContainer.transform;
quad.transform.localRotation = Quaternion.Euler(90, 0, 0);
quad.transform.localScale = new Vector3(cellSize, cellSize, 1);
quad.GetComponent().enabled = false;
highlightQuads.Add(quad);
}
for (int i = 0; i < highlightQuads.Count; i++)
{
var quad = highlightQuads[i];
if (i < mask.Count)
{
quad.SetActive(true);
var c = mask[i];
Vector3 localPos = new Vector3(
c.x * cellSize + cellSize / 2f,
0.02f,
c.y * cellSize + cellSize / 2f
);
quad.transform.localPosition = localPos;
Vector3 worldPos = previewContainer.transform.TransformPoint(localPos);
Vector2Int gp = grid.WorldToGrid(worldPos);
bool valid = grid.CanPlaceObject(gp.x, gp.y);
quad.GetComponent().material = valid
? highlightValidMat
: highlightInvalidMat;
}
else quad.SetActive(false);
}
}
void HideHighlights()
{
foreach (var q in highlightQuads) q.SetActive(false);
}
void SetAlpha(GameObject obj, float alpha)
{
foreach (var r in obj.GetComponentsInChildren())
{
var mat = r.material;
Color c = mat.color; c.a = alpha; mat.color = c;
mat.SetFloat("_Mode", 3);
mat.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
mat.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
mat.SetInt("_ZWrite", 0);
mat.DisableKeyword("_ALPHATEST_ON");
mat.EnableKeyword("_ALPHABLEND_ON");
mat.DisableKeyword("_ALPHAPREMULTIPLY_ON");
mat.renderQueue = 3000;
}
}
}
Подробнее здесь: https://stackoverflow.com/questions/797 ... ing-system