Souljack/Assets/_Project/Scripts/LevelGenerating/ShellSpawner.cs

150 lines
7.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
floorGo.layer = LayerMask.NameToLayer("Ground");
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;
}
}
}