116 lines
5.0 KiB
C#
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;
|
|
}
|