Files
MVP1/Assets/Scripts/Modules/Settlement/BuildGhost.cs
T

116 lines
5.0 KiB
C#

using UnityEngine;
// МОДУЛЬ ПОСЕЛЕНИЯ. Документ: Docs/Modules/Settlement.md
// Призрак здания в режиме стройки: полупрозрачная копия картинки здания и пятно его клеток под ней.
// Пятно зелёное -- сюда можно ставить, красное -- клетки заняты или за границей.
// Только картинка: скриптов и коллайдеров префаба у призрака нет, экономика его не видит.
public class BuildGhost : MonoBehaviour
{
public BuildingData Data { get; private set; }
public BuildFootprint Footprint { get; private set; }
public Vector2Int Cell { get; private set; } // левая нижняя клетка на сетке
public bool CellsFree { get; private set; }
public static readonly Color FreeColor = new Color(0.25f, 1f, 0.35f, 0.45f);
public static readonly Color BlockedColor = new Color(1f, 0.2f, 0.2f, 0.5f);
SpriteRenderer pad;
static Sprite white;
public static BuildGhost Create(BuildingData data)
{
var go = new GameObject("BuildGhost_" + data.buildingName);
var g = go.AddComponent<BuildGhost>();
g.Data = data;
g.Footprint = BuildFootprint.Of(data);
g.Build();
return g;
}
void Build()
{
// пятно клеток: белый квадрат 1×1, растянутый на размер здания
var padGo = new GameObject("Cells");
padGo.transform.SetParent(transform, false);
pad = padGo.AddComponent<SpriteRenderer>();
pad.sprite = WhiteSprite();
pad.sortingOrder = 3;
padGo.transform.localScale = new Vector3(Footprint.size.x, Footprint.size.y, 1f);
// копия картинки здания: позиция центра пятна = позиция префаба + anchorOffset
var art = new GameObject("Art").transform;
art.SetParent(transform, false);
art.localPosition = -(Vector3)Footprint.anchorOffset;
var prefab = Data.prefab;
if (prefab == null) return;
Vector3 root = prefab.transform.position;
int minOrder = int.MaxValue;
var renderers = prefab.GetComponentsInChildren<SpriteRenderer>(true);
foreach (var sr in renderers) if (sr.sprite != null) minOrder = Mathf.Min(minOrder, sr.sortingOrder);
foreach (var sr in renderers)
{
if (sr.sprite == null) continue;
var copy = new GameObject(sr.name).transform;
copy.SetParent(art, false);
copy.localPosition = sr.transform.position - root;
copy.localRotation = sr.transform.rotation;
copy.localScale = sr.transform.lossyScale;
var r = copy.gameObject.AddComponent<SpriteRenderer>();
r.sprite = sr.sprite;
r.flipX = sr.flipX;
r.flipY = sr.flipY;
r.drawMode = sr.drawMode;
if (sr.drawMode != SpriteDrawMode.Simple) r.size = sr.size;
var c = sr.color; c.a *= 0.65f;
r.color = c;
r.sortingOrder = 4 + (sr.sortingOrder - minOrder);
}
}
static Sprite WhiteSprite()
{
if (white != null) return white;
var tex = new Texture2D(4, 4, TextureFormat.RGBA32, false);
var px = new Color32[16];
for (int i = 0; i < px.Length; i++) px[i] = new Color32(255, 255, 255, 255);
tex.SetPixels32(px);
tex.Apply();
white = Sprite.Create(tex, new Rect(0, 0, 4, 4), new Vector2(0.5f, 0.5f), 4);
return white;
}
// Поставить призрак на клетку (левая нижняя) и проверить, свободно ли
public void MoveToCell(Vector2Int cell)
{
var grid = BuildGrid.Instance;
Cell = cell;
Vector2 center = grid.RectCenter(cell, Footprint.size);
transform.position = new Vector3(center.x, center.y, 0f);
Recheck();
}
// Подвинуть так, чтобы центр пятна был ближе всего к точке мира
public void MoveNear(Vector2 worldCenter)
{
MoveToCell(BuildGrid.Instance.SnapCell(worldCenter, Footprint.size));
}
public void Recheck()
{
CellsFree = BuildGrid.Instance.CanPlace(Cell, Footprint.size);
if (pad != null) pad.color = CellsFree ? FreeColor : BlockedColor;
}
// Точка мира попадает в пятно призрака (с запасом под палец)
public bool Contains(Vector2 world, float margin = 0.4f)
{
Vector2 c = transform.position;
Vector2 half = (Vector2)Footprint.size * 0.5f + Vector2.one * margin;
return Mathf.Abs(world.x - c.x) <= half.x && Mathf.Abs(world.y - c.y) <= half.y;
}
// Куда ставить префаб, чтобы здание встало ровно на клетки призрака
public Vector3 PrefabPosition => transform.position - (Vector3)Footprint.anchorOffset;
}