quot;{i:00}_{row.beatName}"); room.transform.SetParent(root.transform, false); room.transform.position = new Vector3(0f, 0f, z + len * 0.5f); var floor = ShapeGenerator.GenerateCube(PivotLocation.Center, new Vector3(w, 0.2f, len)); floor.name = "Floor"; floor.transform.SetParent(room.transform, false); floor.transform.position = room.transform.position + Vector3.up * 0.1f; TintAndCommit(floor, ColorFor(row.kind)); var wallL = ShapeGenerator.GenerateCube(PivotLocation.Center, new Vector3(0.3f, h, len)); wallL.name = "Wall_L"; wallL.transform.SetParent(room.transform, false); wallL.transform.position = room.transform.position + new Vector3(-w * 0.5f, h * 0.5f, 0f); TintAndCommit(wallL, new Color(0.2f, 0.2f, 0.2f)); var wallR = ShapeGenerator.GenerateCube(PivotLocation.Center, new Vector3(0.3f, h, len)); wallR.name = "Wall_R"; wallR.transform.SetParent(room.transform, false); wallR.transform.position = room.transform.position + new Vector3(w * 0.5f, h * 0.5f, 0f); TintAndCommit(wallR, new Color(0.2f, 0.2f, 0.2f)); if (row.kind == BeatRoomKind.Landmark) { float weenieHeight = Mathf.Max(8f, h * 3f); var weenie = ShapeGenerator.GenerateCone(PivotLocation.Center, 1.2f, weenieHeight, 12); weenie.name = "Weenie"; weenie.transform.SetParent(room.transform, false); weenie.transform.position = room.transform.position + Vector3.up * (weenieHeight * 0.5f); TintAndCommit(weenie, new Color(1f, 0.85f, 0.2f)); } z += len; } Selection.activeGameObject = root; } static Color ColorFor(BeatRoomKind kind) => kind switch { BeatRoomKind.Approach => new Color(0.55f, 0.55f, 0.55f), BeatRoomKind.Arena => new Color(0.45f, 0.45f, 0.5f), BeatRoomKind.Puzzle => new Color(0.35f, 0.55f, 0.65f), BeatRoomKind.RewardFinger => new Color(0.35f, 0.6f, 0.35f), BeatRoomKind.Calm => new Color(0.5f, 0.6f, 0.5f), BeatRoomKind.Landmark => new Color(0.55f, 0.5f, 0.35f), _ => Color.gray }; static void TintAndCommit(ProBuilderMesh pb, Color color) { // Official 6.1.4 API: colors (IList<Color>, length must match positions) and vertexCount. if (pb.colors == null || pb.colors.Count != pb.vertexCount) { var colors = new Color[pb.vertexCount]; for (int i = 0; i < colors.Length; i++) colors[i] = color; pb.colors = colors; } else { var colors = pb.colors; for (int i = 0; i < colors.Count; i++) colors[i] = color; pb.colors = colors; } pb.ToMesh(); pb.Refresh(); EditorMeshUtility.Optimize(pb, true); } } #endif ``` `ProBuilderMesh.colors` and `vertexCount` are official 6.1.4 APIs (`colors` is an `IList<Color>` whose length must match the positions array). After assigning colors, `ToMesh()` + `Refresh()` applies them; the URP sample shaders must be imported or the tint may not show. `GetColors()` also exists (returns white if the mesh has no colors). You can instead paint with the Vertex Color tool after a build. `Undo.DestroyImmediate` on the previous root is an Editor-only rebuild. It is not a runtime API. Never hook this builder to Play Mode. `GameObject.Find` is acceptable in a one-off Editor menu; do not copy it into gameplay. ## 4.3 After the stamp The generated spine is a *starting* corridor: two walls and a floor per beat, plus a cone on Landmark rows. You then: - Cut doorways between beats (or leave gaps). - Pull a RewardFinger off to the side and put a green prop at the end. - Place a `LandmarkSightline` at beat 0 if `showWeenieFromHere` is set. - Add a spotlight on the cone (part 3). - Play it. If beat 3 "feels long," it *is* long - shorten `length` and rebuild, or put a reward mid-span. ## 4.4 Exercise Write a five-row chart: Approach (weenie visible), Calm, Arena, RewardFinger, Landmark. Build it, play it, and change one length because it bored you. That is the whole loop. ## Sources - `ShapeGenerator` (6.1.4): https://docs.unity3d.com/Packages/[email protected]/api/UnityEngine.ProBuilder.ShapeGenerator.html - API overview (colors stored on `ProBuilderMesh`): https://docs.unity3d.com/Packages/[email protected]/manual/api.html - [[Level Up! (Review)]] Levels 4 and 9 - [[Custom Inspectors and Editor Tooling]]