#if UNITY_EDITOR using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; // Project namespaces using MegaKoop.Steam; // SteamManager, SteamLobbyService using MegaKoop.Game.Networking; // SteamCoopNetworkManager, SteamP2PTransport, LobbyGameSceneCoordinator public static class CharacterSceneSetupGenerator { private const string WizardPrefabPath = "Assets/Game/Hero/Wizard.prefab"; [MenuItem("Tools/MegaKoop/Setup Character Scene Objects", priority = 1000)] public static void SetupCharacterScene() { var scene = SceneManager.GetActiveScene(); if (!scene.IsValid()) { EditorUtility.DisplayDialog("Character Scene Setup", "No valid scene is open. Please open your CharacterScene and run again.", "OK"); return; } Undo.IncrementCurrentGroup(); int group = Undo.GetCurrentGroup(); // 1) Ensure SteamServices root and required components var servicesRoot = GameObject.Find("SteamServices"); if (servicesRoot == null) { servicesRoot = new GameObject("SteamServices"); Undo.RegisterCreatedObjectUndo(servicesRoot, "Create SteamServices"); } EnsureComponent(servicesRoot, "Add SteamManager"); var lobby = EnsureComponent(servicesRoot, "Add SteamLobbyService"); var coop = EnsureComponent(servicesRoot, "Add SteamCoopNetworkManager"); EnsureComponent(servicesRoot, "Add SteamP2PTransport"); var coordinator = EnsureComponent(servicesRoot, "Add LobbyGameSceneCoordinator"); // Keep servicesRoot persistent across scenes to match runtime behavior if (!Application.isPlaying) { // Editor-time: mark as DontSaveInBuild is not needed; users expect this object in scene. // At runtime, code calls DontDestroyOnLoad itself. } // 2) Ensure Wizard template exists in scene root var wizardInScene = GameObject.Find("Wizard"); if (wizardInScene == null) { var prefab = AssetDatabase.LoadAssetAtPath(WizardPrefabPath); if (prefab != null) { var instantiated = PrefabUtility.InstantiatePrefab(prefab, scene) as GameObject; if (instantiated != null) { Undo.RegisterCreatedObjectUndo(instantiated, "Instantiate Wizard Template"); instantiated.name = "Wizard"; // Ensure exact name for coordinator lookup instantiated.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity); } } else { // Fallback: create a simple placeholder var placeholder = GameObject.CreatePrimitive(PrimitiveType.Capsule); Undo.RegisterCreatedObjectUndo(placeholder, "Create Wizard Placeholder"); placeholder.name = "Wizard"; placeholder.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity); // Controller components are optional for template; coordinator will add bridges on clones. placeholder.AddComponent(); placeholder.AddComponent(); } } // 3) Optional: ensure a basic ground so CharacterController can stand if (GameObject.Find("Ground") == null) { var ground = GameObject.CreatePrimitive(PrimitiveType.Plane); Undo.RegisterCreatedObjectUndo(ground, "Create Ground"); ground.name = "Ground"; ground.transform.position = Vector3.zero; ground.transform.localScale = Vector3.one * 2f; } // 4) Optional: ensure there is at least one light if (Object.FindObjectOfType() == null) { var lightGO = new GameObject("Directional Light"); Undo.RegisterCreatedObjectUndo(lightGO, "Create Directional Light"); var light = lightGO.AddComponent(); light.type = LightType.Directional; light.intensity = 1.1f; lightGO.transform.rotation = Quaternion.Euler(50f, -30f, 0f); } // 5) Hint coordinator to use current scene name if it differs if (coordinator != null) { // If you want the coordinator to target this scene specifically, uncomment the next line: // SetPrivateField(coordinator, "characterSceneName", scene.name); } EditorSceneManager.MarkSceneDirty(scene); Undo.CollapseUndoOperations(group); EditorUtility.DisplayDialog("Character Scene Setup", "Character scene objects have been set up successfully.", "OK"); } private static T EnsureComponent(GameObject go, string undoName) where T : Component { var c = go.GetComponent(); if (c == null) { c = Undo.AddComponent(go); } return c; } // Example helper if you later want to set private serialized fields via reflection private static void SetPrivateField(object target, string fieldName, object value) { var t = target.GetType(); var f = t.GetField(fieldName, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); if (f != null) { f.SetValue(target, value); EditorUtility.SetDirty((Object)target); } } } #endif