using UnityEngine; namespace KirbyAdventures { /// /// Procedural greybox for Phase A — Level_01_Forest. /// public static class Level01ForestBuilder { static Transform _root; public static void Build() { if (GameObject.Find("Level_01_Forest") != null) return; _root = new GameObject("Level_01_Forest").transform; EnsureLighting(); BuildGroundAndPath(); BuildJumpPlatforms(); var startPos = new Vector3(0f, 1.1f, 0f); BuildCheckpoints(); BuildStoryTriggers(); BuildAbilityPedestal(); BuildVillageGate(); var player = BuildPlayer(startPos); BuildCamera(player); BuildKillPlane(player.transform); CheckpointSystem.EnsureStart(startPos, Quaternion.identity); CheckpointSystem.SetCheckpoint(startPos, Quaternion.identity, "start"); DialogueUI.ShowTemporary("Кирби очнулся среди деревьев. Тропа ведёт к деревне — и к себе.", 4f); } static void EnsureLighting() { // Keep scene light; add one if missing if (Object.FindFirstObjectByType() == null) { var lightGo = new GameObject("Directional Light"); lightGo.transform.SetParent(_root); var light = lightGo.AddComponent(); light.type = LightType.Directional; light.color = new Color(1f, 0.95f, 0.85f); light.intensity = 1.1f; lightGo.transform.rotation = Quaternion.Euler(50f, -30f, 0f); } RenderSettings.ambientMode = UnityEngine.Rendering.AmbientMode.Trilight; RenderSettings.ambientSkyColor = new Color(0.45f, 0.55f, 0.65f); RenderSettings.ambientEquatorColor = new Color(0.35f, 0.4f, 0.3f); RenderSettings.ambientGroundColor = new Color(0.15f, 0.12f, 0.1f); } static void BuildGroundAndPath() { // Start platform CreateBox("StartPlatform", new Vector3(0f, 0f, 0f), new Vector3(10f, 1f, 10f), new Color(0.35f, 0.45f, 0.28f)); // Forest path stretches CreateBox("Path_A", new Vector3(0f, 0f, 14f), new Vector3(6f, 1f, 16f), new Color(0.32f, 0.42f, 0.26f)); CreateBox("Path_B", new Vector3(0f, 0f, 32f), new Vector3(5f, 1f, 12f), new Color(0.3f, 0.4f, 0.24f)); // Side tree markers (cylinders) for (int i = 0; i < 8; i++) { float z = 4f + i * 5f; float x = (i % 2 == 0) ? -5.5f : 5.5f; var tree = CreatePrimitive(PrimitiveType.Cylinder, $"Tree_{i}", new Vector3(x, 2f, z), new Vector3(0.6f, 2f, 0.6f), new Color(0.2f, 0.28f, 0.15f)); tree.transform.SetParent(_root); } } static void BuildJumpPlatforms() { // Gap then platforms CreateBox("Pad_1", new Vector3(0f, 0.5f, 42f), new Vector3(3.5f, 1f, 3.5f), new Color(0.45f, 0.38f, 0.28f)); CreateBox("Pad_2", new Vector3(3f, 1.2f, 48f), new Vector3(3f, 1f, 3f), new Color(0.48f, 0.4f, 0.3f)); CreateBox("Pad_3", new Vector3(-2f, 2f, 54f), new Vector3(3f, 1f, 3f), new Color(0.5f, 0.42f, 0.32f)); CreateBox("Pad_4", new Vector3(0f, 2.5f, 61f), new Vector3(4f, 1f, 4f), new Color(0.4f, 0.45f, 0.3f)); // Landing after mid section CreateBox("Path_C", new Vector3(0f, 0f, 72f), new Vector3(8f, 1f, 14f), new Color(0.33f, 0.42f, 0.27f)); CreateBox("Path_D", new Vector3(0f, 0f, 90f), new Vector3(10f, 1f, 16f), new Color(0.36f, 0.44f, 0.3f)); CreateBox("VillageApproach", new Vector3(0f, 0f, 108f), new Vector3(12f, 1f, 14f), new Color(0.4f, 0.4f, 0.35f)); } static void BuildCheckpoints() { CreateCheckpoint("CP_Start", new Vector3(0f, 1.5f, 2f), new Vector3(4f, 2f, 2f), "cp_start"); CreateCheckpoint("CP_Mid", new Vector3(0f, 3.5f, 61f), new Vector3(3f, 2f, 3f), "cp_mid"); CreateCheckpoint("CP_NearGate", new Vector3(0f, 1.5f, 100f), new Vector3(5f, 2f, 3f), "cp_gate"); } static void CreateCheckpoint(string name, Vector3 pos, Vector3 size, string id) { var go = new GameObject(name); go.transform.SetParent(_root); go.transform.position = pos; var box = go.AddComponent(); box.isTrigger = true; box.size = size; var cp = go.AddComponent(); cp.CheckpointId = id; var marker = CreatePrimitive(PrimitiveType.Cube, name + "_Marker", pos + Vector3.up * 0.5f, new Vector3(0.4f, 1.2f, 0.4f), new Color(0.2f, 0.7f, 0.9f, 0.7f)); marker.transform.SetParent(go.transform); Object.Destroy(marker.GetComponent()); cp.SpawnPoint = go.transform; } static void BuildStoryTriggers() { CreateStory("Story_Fall", new Vector3(0f, 2f, 8f), new Vector3(6f, 3f, 3f), "«Падение»\nЗемля приняла его мягко, будто лес давно ждал чёрного кота.", 5f); CreateStory("Story_Road", new Vector3(0f, 2f, 28f), new Vector3(5f, 3f, 3f), "«Путь в деревню»\nТропа узкая, но шаг за шагом — и чужой шум становится тише.", 5f); CreateStory("Story_Source", new Vector3(0f, 3.5f, 74f), new Vector3(6f, 3f, 3f), "«Внутренний источник»\nСвет не нужно искать в небе — он уже греет изнутри.", 5f); } static void CreateStory(string name, Vector3 pos, Vector3 size, string quote, float secs) { var go = new GameObject(name); go.transform.SetParent(_root); go.transform.position = pos; var box = go.AddComponent(); box.isTrigger = true; box.size = size; var st = go.AddComponent(); st.Quote = quote; st.DisplaySeconds = secs; } static void BuildAbilityPedestal() { var pedestal = CreatePrimitive(PrimitiveType.Cylinder, "LightPedestal", new Vector3(0f, 1.2f, 78f), new Vector3(1.4f, 0.6f, 1.4f), new Color(0.55f, 0.5f, 0.75f)); pedestal.transform.SetParent(_root); var orb = CreatePrimitive(PrimitiveType.Sphere, "LightOrb", new Vector3(0f, 2.4f, 78f), Vector3.one * 0.7f, new Color(1f, 0.95f, 0.55f)); orb.transform.SetParent(pedestal.transform); Object.Destroy(orb.GetComponent()); var trigger = new GameObject("AbilityUnlock"); trigger.transform.SetParent(_root); trigger.transform.position = new Vector3(0f, 1.5f, 78f); var col = trigger.AddComponent(); col.isTrigger = true; col.size = new Vector3(3.5f, 3f, 3.5f); trigger.AddComponent(); } static void BuildVillageGate() { var posts = CreateBox("GatePosts", new Vector3(0f, 3f, 116f), new Vector3(8f, 6f, 1.2f), new Color(0.35f, 0.28f, 0.22f)); // Hollow-ish: leave a door slab that moves var door = CreateBox("GateDoor", new Vector3(0f, 2.5f, 116.2f), new Vector3(3.2f, 4.5f, 0.6f), new Color(0.15f, 0.12f, 0.2f)); var gate = door.AddComponent(); gate.GateVisual = door.transform; // Invisible hit proxy in front var hit = new GameObject("GateLightTarget"); hit.transform.SetParent(_root); hit.transform.position = new Vector3(0f, 2.5f, 115.5f); var hc = hit.AddComponent(); hc.size = new Vector3(3.5f, 4.5f, 1.5f); // forward hits to LightGate via child relationship hit.transform.SetParent(door.transform); // LightGate is on door; GetComponentInParent will find it } static GameObject BuildPlayer(Vector3 pos) { // Black capsule named Kirby var body = CreatePrimitive(PrimitiveType.Capsule, "Kirby", pos, new Vector3(0.9f, 1f, 0.9f), new Color(0.05f, 0.05f, 0.08f)); body.tag = "Player"; Object.Destroy(body.GetComponent()); var cc = body.AddComponent(); cc.height = 2f; cc.radius = 0.45f; cc.center = new Vector3(0f, 0f, 0f); cc.skinWidth = 0.08f; var yaw = new GameObject("YawPivot"); yaw.transform.SetParent(body.transform); yaw.transform.localPosition = Vector3.zero; var motor = body.AddComponent(); motor.YawPivot = yaw.transform; body.AddComponent(); return body; } static void BuildCamera(GameObject player) { Camera cam = Camera.main; if (cam == null) { var camGo = new GameObject("Main Camera"); cam = camGo.AddComponent(); cam.tag = "MainCamera"; camGo.AddComponent(); } var follow = cam.GetComponent(); if (follow == null) follow = cam.gameObject.AddComponent(); var motor = player.GetComponent(); Transform yaw = player.transform.Find("YawPivot"); follow.Target = player.transform; follow.YawPivot = yaw; motor.CameraTransform = cam.transform; motor.YawPivot = yaw; cam.transform.position = player.transform.position + new Vector3(0f, 2.5f, -5.5f); cam.transform.LookAt(player.transform.position + Vector3.up * 1.2f); } static void BuildKillPlane(Transform player) { var go = new GameObject("FallKillPlane"); go.transform.SetParent(_root); var kill = go.AddComponent(); kill.KillY = -10f; kill.Bind(player); } static GameObject CreateBox(string name, Vector3 pos, Vector3 scale, Color color) { var go = CreatePrimitive(PrimitiveType.Cube, name, pos, scale, color); go.transform.SetParent(_root); return go; } static GameObject CreatePrimitive(PrimitiveType type, string name, Vector3 pos, Vector3 scale, Color color) { var go = GameObject.CreatePrimitive(type); go.name = name; go.transform.position = pos; go.transform.localScale = scale; var rend = go.GetComponent(); if (rend != null) { // URP Lit may exist after package import; fallback to Standard / Unlit var shader = Shader.Find("Universal Render Pipeline/Lit"); if (shader == null) shader = Shader.Find("Standard"); if (shader == null) shader = Shader.Find("Sprites/Default"); if (shader != null) { var mat = new Material(shader); if (mat.HasProperty("_BaseColor")) mat.SetColor("_BaseColor", color); else if (mat.HasProperty("_Color")) mat.SetColor("_Color", color); else mat.color = color; rend.material = mat; } else { rend.material.color = color; } } return go; } } }