148 lines
7.0 KiB
C#
148 lines
7.0 KiB
C#
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
|
||
namespace _Project.Scripts.LevelGenerating
|
||
{
|
||
public class ShellResult
|
||
{
|
||
public Transform Root;
|
||
public List<RoomView> Views = new List<RoomView>(); // индекс совпадает с FloorData.Rooms
|
||
}
|
||
|
||
// =====================================================================
|
||
// Спавнит пол, потолок и стены из одного префаба-плоскости.
|
||
// Для каждой комнаты создаётся объект Room_N_Тип с компонентом RoomView.
|
||
// Материалы здесь НЕ назначаются: этим занимаются генераторы комнат (RoomTheme).
|
||
//
|
||
// Требования к префабу:
|
||
// - Unity Plane (3D Object -> Plane): лежит в XZ, нормаль +Y, размер 10x10 при scale 1.
|
||
// Если размер другой (например 1x1), поменяй planeBaseSize. Quad не подойдёт: у него нормаль -Z.
|
||
// - Меш и Renderer на корне префаба. Коллайдеры на префабе игнорируются и заменяются на Box.
|
||
//
|
||
// Все плоскости видны только с одной стороны (внутрь комнаты):
|
||
// стены смотрят внутрь, пол вверх, потолок вниз.
|
||
//
|
||
// НЕ ЗАБЫТЬ: сейчас сюда попадает и комната лифта. Позже у неё будет свой фиксированный префаб.
|
||
// =====================================================================
|
||
public class ShellSpawner : MonoBehaviour
|
||
{
|
||
[SerializeField] private GameObject planePrefab;
|
||
[SerializeField] private float planeBaseSize = 10f;
|
||
[SerializeField] private float wallHeight = 3f;
|
||
[SerializeField] private float colliderThickness = 0.2f;
|
||
|
||
public ShellResult Spawn(FloorData floor)
|
||
{
|
||
if (planePrefab == null)
|
||
{
|
||
Debug.LogError("ShellSpawner: не назначен planePrefab");
|
||
return null;
|
||
}
|
||
|
||
var result = new ShellResult
|
||
{
|
||
Root = new GameObject($"Floor_{floor.Index}_Shell").transform
|
||
};
|
||
|
||
// 1. Объект на каждую комнату + пол и потолок
|
||
for (int i = 0; i < floor.Rooms.Count; i++)
|
||
{
|
||
var room = floor.Rooms[i];
|
||
|
||
var roomGo = new GameObject($"Room_{i}_{room.rt}");
|
||
roomGo.transform.SetParent(result.Root, false);
|
||
|
||
var view = roomGo.AddComponent<RoomView>();
|
||
view.Data = room;
|
||
view.Index = i;
|
||
view.Height = wallHeight;
|
||
result.Views.Add(view);
|
||
|
||
var scale = new Vector3(view.SizeMeters.x / planeBaseSize, 1f, view.SizeMeters.z / planeBaseSize);
|
||
Vector3 c = view.CenterMeters;
|
||
|
||
// пол: нормаль вверх
|
||
var floorGo = Make(roomGo.transform, "Floor", new Vector3(c.x, 0f, c.z),
|
||
Quaternion.identity, scale, true);
|
||
view.FloorRenderers.AddRange(floorGo.GetComponentsInChildren<Renderer>());
|
||
|
||
// потолок: перевёрнут, нормаль вниз
|
||
var ceilingGo = Make(roomGo.transform, "Ceiling", new Vector3(c.x, wallHeight, c.z),
|
||
Quaternion.Euler(180f, 0f, 0f), scale, false);
|
||
view.CeilingRenderers.AddRange(ceilingGo.GetComponentsInChildren<Renderer>());
|
||
}
|
||
|
||
// 2. Стены раскладываются по комнатам-владельцам
|
||
foreach (var run in floor.Walls)
|
||
{
|
||
if (run.RoomIndex < 0 || run.RoomIndex >= result.Views.Count)
|
||
{
|
||
Debug.LogWarning($"Стена ссылается на несуществующую комнату {run.RoomIndex}");
|
||
continue;
|
||
}
|
||
SpawnWall(result.Views[run.RoomIndex], run);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
private void SpawnWall(RoomView view, WallRun run)
|
||
{
|
||
float len = run.Length * LevelGrid.Module;
|
||
float y = wallHeight / 2f;
|
||
float sx = run.Start.x * LevelGrid.Module;
|
||
float sz = run.Start.y * LevelGrid.Module;
|
||
|
||
Vector3 pos;
|
||
Quaternion rot;
|
||
Vector3 scale;
|
||
|
||
switch (run.Facing)
|
||
{
|
||
case WallFacing.ZP: // идёт вдоль X, нормаль +Z
|
||
pos = new Vector3(sx + len / 2f, y, sz);
|
||
rot = Quaternion.Euler(90f, 0f, 0f);
|
||
scale = new Vector3(len / planeBaseSize, 1f, wallHeight / planeBaseSize);
|
||
break;
|
||
case WallFacing.ZN: // идёт вдоль X, нормаль -Z
|
||
pos = new Vector3(sx + len / 2f, y, sz);
|
||
rot = Quaternion.Euler(-90f, 0f, 0f);
|
||
scale = new Vector3(len / planeBaseSize, 1f, wallHeight / planeBaseSize);
|
||
break;
|
||
case WallFacing.XP: // идёт вдоль Z, нормаль +X
|
||
pos = new Vector3(sx, y, sz + len / 2f);
|
||
rot = Quaternion.Euler(0f, 0f, -90f);
|
||
scale = new Vector3(wallHeight / planeBaseSize, 1f, len / planeBaseSize);
|
||
break;
|
||
default: // XN: идёт вдоль Z, нормаль -X
|
||
pos = new Vector3(sx, y, sz + len / 2f);
|
||
rot = Quaternion.Euler(0f, 0f, 90f);
|
||
scale = new Vector3(wallHeight / planeBaseSize, 1f, len / planeBaseSize);
|
||
break;
|
||
}
|
||
|
||
var go = Make(view.transform, "Wall", pos, rot, scale, true);
|
||
view.WallRenderers.AddRange(go.GetComponentsInChildren<Renderer>());
|
||
}
|
||
|
||
private GameObject Make(Transform parent, string name, Vector3 pos, Quaternion rot, Vector3 scale,
|
||
bool withCollider)
|
||
{
|
||
var go = Instantiate(planePrefab, pos, rot, parent);
|
||
go.name = name;
|
||
go.transform.localScale = scale;
|
||
|
||
// коллайдеры префаба не используем: у плоскости нет толщины
|
||
foreach (var c in go.GetComponents<Collider>()) Destroy(c);
|
||
|
||
if (withCollider)
|
||
{
|
||
// Box прячем за видимой стороной (локальный -Y), чтобы он не съедал место внутри комнаты
|
||
var box = go.AddComponent<BoxCollider>();
|
||
box.size = new Vector3(planeBaseSize, colliderThickness, planeBaseSize);
|
||
box.center = new Vector3(0f, -colliderThickness / 2f, 0f);
|
||
}
|
||
return go;
|
||
}
|
||
}
|
||
} |