2.5 update
This commit is contained in:
@@ -0,0 +1,266 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BumpCombat.Constants;
|
||||
using BumpCombat.Defense.Comparison;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace BumpCombat.Editor
|
||||
{
|
||||
public static class Defense25DRunSetup
|
||||
{
|
||||
public const string WaveConstantsPath = "Assets/_Project/Constants/Defense25DComparison/LongDefenseWaveDirector.asset";
|
||||
public const string RunDefinitionPath = "Assets/_Project/Constants/Defense25DComparison/LongDefenseRun.asset";
|
||||
public const string MonsterPoolPath = "Assets/_Project/Constants/Defense25DComparison/MonsterPools.asset";
|
||||
public const string ArtifactDropConstantsPath = "Assets/_Project/Constants/Defense25DComparison/ArtifactDropConstants.asset";
|
||||
public const string ComparisonSettingsPath = "Assets/_Project/Constants/Defense25DComparison/ComparisonSettings.asset";
|
||||
public const string HudConstantsPath = "Assets/_Project/Constants/Defense25DComparison/RunHudConstants.asset";
|
||||
public const string ScenePath = "Assets/_Project/Scenes/Defense25DRun.unity";
|
||||
|
||||
[MenuItem("BumpCombat/Defense 2.5D/Create Long Defense Profile and Scene")]
|
||||
public static void CreateLongDefenseAssets()
|
||||
{
|
||||
EnsureFolder("Assets/_Project/Constants/Defense25DComparison");
|
||||
EnsureFolder("Assets/_Project/Scenes");
|
||||
|
||||
Defense25DWaveDirectorConstants waveConstants = AssetDatabase.LoadAssetAtPath<Defense25DWaveDirectorConstants>(WaveConstantsPath);
|
||||
if (waveConstants == null)
|
||||
{
|
||||
waveConstants = ScriptableObject.CreateInstance<Defense25DWaveDirectorConstants>();
|
||||
AssetDatabase.CreateAsset(waveConstants, WaveConstantsPath);
|
||||
}
|
||||
|
||||
Defense25DRunDefinition runDefinition = AssetDatabase.LoadAssetAtPath<Defense25DRunDefinition>(RunDefinitionPath);
|
||||
Defense25DMonsterPoolDefinition monsterPool = AssetDatabase.LoadAssetAtPath<Defense25DMonsterPoolDefinition>(MonsterPoolPath);
|
||||
Defense25DArtifactDropConstants artifactDropConstants =
|
||||
AssetDatabase.LoadAssetAtPath<Defense25DArtifactDropConstants>(ArtifactDropConstantsPath);
|
||||
Defense25DComparisonSettings comparisonSettings =
|
||||
AssetDatabase.LoadAssetAtPath<Defense25DComparisonSettings>(ComparisonSettingsPath);
|
||||
Defense25DRunHudConstants hudConstants = AssetDatabase.LoadAssetAtPath<Defense25DRunHudConstants>(HudConstantsPath);
|
||||
if (monsterPool == null || artifactDropConstants == null)
|
||||
throw new InvalidOperationException("The verified monster pool and artifact-drop settings assets are required before creating the long-defense profile.");
|
||||
if (comparisonSettings == null || hudConstants == null)
|
||||
throw new InvalidOperationException("Comparison and Run HUD settings assets are required before creating the playable scene.");
|
||||
|
||||
if (runDefinition == null)
|
||||
{
|
||||
runDefinition = ScriptableObject.CreateInstance<Defense25DRunDefinition>();
|
||||
runDefinition.ProfileId = "LongDefense-v2";
|
||||
runDefinition.PreparationSeconds = 20f;
|
||||
runDefinition.FinalBossAtSeconds = 720f;
|
||||
runDefinition.BattlefieldDefinitions = new[] { Defense25DBattlefieldDefinition.CreateDefault() };
|
||||
runDefinition.PortalDefinitions = Defense25DRunDefinition.CreateDefaultPortals();
|
||||
runDefinition.WaveDirectorConstants = waveConstants;
|
||||
runDefinition.MonsterPoolDefinition = monsterPool;
|
||||
runDefinition.ArtifactDropConstants = artifactDropConstants;
|
||||
AssetDatabase.CreateAsset(runDefinition, RunDefinitionPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool changed = false;
|
||||
if (runDefinition.WaveDirectorConstants == null)
|
||||
{
|
||||
runDefinition.WaveDirectorConstants = waveConstants;
|
||||
changed = true;
|
||||
}
|
||||
if (runDefinition.MonsterPoolDefinition == null)
|
||||
{
|
||||
runDefinition.MonsterPoolDefinition = monsterPool;
|
||||
changed = true;
|
||||
}
|
||||
if (runDefinition.ArtifactDropConstants == null)
|
||||
{
|
||||
runDefinition.ArtifactDropConstants = artifactDropConstants;
|
||||
changed = true;
|
||||
}
|
||||
if (changed) EditorUtility.SetDirty(runDefinition);
|
||||
}
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.ImportAsset(WaveConstantsPath, ImportAssetOptions.ForceUpdate);
|
||||
AssetDatabase.ImportAsset(RunDefinitionPath, ImportAssetOptions.ForceUpdate);
|
||||
waveConstants = AssetDatabase.LoadAssetAtPath<Defense25DWaveDirectorConstants>(WaveConstantsPath);
|
||||
runDefinition = AssetDatabase.LoadAssetAtPath<Defense25DRunDefinition>(RunDefinitionPath);
|
||||
if (waveConstants == null || runDefinition == null || runDefinition.WaveDirectorConstants != waveConstants)
|
||||
throw new InvalidOperationException("Long-defense assets did not reload with their Wave Director reference.");
|
||||
if (!runDefinition.Validate(out string validationError))
|
||||
throw new InvalidOperationException($"Long-defense profile validation failed: {validationError}");
|
||||
|
||||
EnsurePlayableScene(runDefinition, comparisonSettings, hudConstants);
|
||||
EnsureBuildScene();
|
||||
AssetDatabase.SaveAssets();
|
||||
Debug.Log($"DEFENSE25D_LONG_RUN_READY profile={runDefinition.ProfileId} scene={ScenePath} " +
|
||||
$"preparation={runDefinition.PreparationSeconds:0.###}s finalBoss={runDefinition.FinalBossAtSeconds:0.###}s");
|
||||
}
|
||||
|
||||
private static void EnsurePlayableScene(Defense25DRunDefinition runDefinition,
|
||||
Defense25DComparisonSettings comparisonSettings, Defense25DRunHudConstants hudConstants)
|
||||
{
|
||||
Scene previousActiveScene = SceneManager.GetActiveScene();
|
||||
Scene scene = SceneManager.GetSceneByPath(ScenePath);
|
||||
bool openedHere = false;
|
||||
if (!scene.IsValid() || !scene.isLoaded)
|
||||
{
|
||||
scene = File.Exists(ScenePath)
|
||||
? EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Additive)
|
||||
: EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Additive);
|
||||
openedHere = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Defense25DRunSceneProfile profile = FindInScene<Defense25DRunSceneProfile>(scene);
|
||||
GameObject root = profile != null ? profile.gameObject : new GameObject("Defense25DRunProfile");
|
||||
if (profile == null)
|
||||
{
|
||||
SceneManager.MoveGameObjectToScene(root, scene);
|
||||
profile = root.AddComponent<Defense25DRunSceneProfile>();
|
||||
}
|
||||
profile.Bind(runDefinition);
|
||||
EditorUtility.SetDirty(profile);
|
||||
|
||||
Defense25DComparison comparison = root.GetComponent<Defense25DComparison>();
|
||||
if (comparison == null)
|
||||
comparison = root.AddComponent<Defense25DComparison>();
|
||||
|
||||
Defense25DView view = FindInScene<Defense25DView>(scene);
|
||||
if (view == null)
|
||||
{
|
||||
GameObject cameraObject = new("Defense25DWorldCamera", typeof(Camera), typeof(Defense25DView));
|
||||
SceneManager.MoveGameObjectToScene(cameraObject, scene);
|
||||
cameraObject.transform.SetParent(root.transform, false);
|
||||
view = cameraObject.GetComponent<Defense25DView>();
|
||||
}
|
||||
Camera worldCamera = view.GetComponent<Camera>();
|
||||
worldCamera.orthographic = true;
|
||||
worldCamera.orthographicSize = comparisonSettings.CameraSize;
|
||||
worldCamera.nearClipPlane = .1f;
|
||||
worldCamera.farClipPlane = 200f;
|
||||
view.transform.SetPositionAndRotation(
|
||||
new Vector3(0f, 28f, -26f), Quaternion.Euler(comparisonSettings.CameraPitch, 0f, 0f));
|
||||
SetReference(comparison, "settings", comparisonSettings);
|
||||
SetReference(comparison, "view", view);
|
||||
SetReference(comparison, "runSceneProfile", profile);
|
||||
SetInt(comparison, "longRunLayoutSeed", 1);
|
||||
EditorUtility.SetDirty(comparison);
|
||||
|
||||
Defense25DRunHud hud = root.GetComponent<Defense25DRunHud>();
|
||||
if (hud == null)
|
||||
hud = root.AddComponent<Defense25DRunHud>();
|
||||
SetReference(hud, "constants", hudConstants);
|
||||
EditorUtility.SetDirty(hud);
|
||||
SetReference(comparison, "runHud", hud);
|
||||
EditorUtility.SetDirty(comparison);
|
||||
|
||||
EnsureGround(scene, comparisonSettings, runDefinition.BattlefieldDefinitions[0].Bounds);
|
||||
EnsureDirectionalLight(scene);
|
||||
|
||||
if (!EditorSceneManager.SaveScene(scene, ScenePath))
|
||||
throw new InvalidOperationException($"Could not save playable run scene at {ScenePath}.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (openedHere && scene.IsValid() && scene.isLoaded)
|
||||
EditorSceneManager.CloseScene(scene, true);
|
||||
if (previousActiveScene.IsValid() && previousActiveScene.isLoaded)
|
||||
SceneManager.SetActiveScene(previousActiveScene);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetReference(UnityEngine.Object target, string propertyName, UnityEngine.Object value)
|
||||
{
|
||||
SerializedObject serialized = new(target);
|
||||
SerializedProperty property = serialized.FindProperty(propertyName);
|
||||
if (property == null)
|
||||
throw new InvalidOperationException($"Serialized property '{propertyName}' was not found on {target.GetType().Name}.");
|
||||
property.objectReferenceValue = value;
|
||||
serialized.ApplyModifiedPropertiesWithoutUndo();
|
||||
}
|
||||
|
||||
private static void SetInt(UnityEngine.Object target, string propertyName, int value)
|
||||
{
|
||||
SerializedObject serialized = new(target);
|
||||
SerializedProperty property = serialized.FindProperty(propertyName);
|
||||
if (property == null)
|
||||
throw new InvalidOperationException($"Serialized property '{propertyName}' was not found on {target.GetType().Name}.");
|
||||
property.intValue = value;
|
||||
serialized.ApplyModifiedPropertiesWithoutUndo();
|
||||
}
|
||||
|
||||
private static T FindInScene<T>(Scene scene) where T : Component
|
||||
{
|
||||
foreach (GameObject sceneRoot in scene.GetRootGameObjects())
|
||||
{
|
||||
T component = sceneRoot.GetComponentInChildren<T>(true);
|
||||
if (component != null) return component;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void EnsureGround(Scene scene, Defense25DComparisonSettings settings, Rect bounds)
|
||||
{
|
||||
GameObject ground = FindGameObjectInScene(scene, "Defense25DRun Ground");
|
||||
if (ground == null)
|
||||
{
|
||||
ground = GameObject.CreatePrimitive(PrimitiveType.Plane);
|
||||
ground.name = "Defense25DRun Ground";
|
||||
SceneManager.MoveGameObjectToScene(ground, scene);
|
||||
}
|
||||
ground.layer = LayerMask.NameToLayer("Default");
|
||||
ground.transform.position = new Vector3(bounds.center.x, settings.GroundY, bounds.center.y);
|
||||
ground.transform.localScale = new Vector3(bounds.width / 10f, 1f, bounds.height / 10f);
|
||||
Renderer renderer = ground.GetComponent<Renderer>();
|
||||
if (renderer != null && settings.GroundMaterial != null)
|
||||
renderer.sharedMaterial = settings.GroundMaterial;
|
||||
}
|
||||
|
||||
private static GameObject FindGameObjectInScene(Scene scene, string objectName)
|
||||
{
|
||||
foreach (UnityEngine.GameObject sceneRoot in scene.GetRootGameObjects())
|
||||
{
|
||||
if (sceneRoot.name == objectName) return sceneRoot;
|
||||
foreach (Transform child in sceneRoot.GetComponentsInChildren<Transform>(true))
|
||||
if (child.name == objectName) return child.gameObject;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void EnsureDirectionalLight(Scene scene)
|
||||
{
|
||||
foreach (GameObject sceneRoot in scene.GetRootGameObjects())
|
||||
foreach (Light light in sceneRoot.GetComponentsInChildren<Light>(true))
|
||||
if (light.type == LightType.Directional) return;
|
||||
|
||||
GameObject lightObject = new("Defense25DRun Directional Light", typeof(Light));
|
||||
SceneManager.MoveGameObjectToScene(lightObject, scene);
|
||||
lightObject.transform.rotation = Quaternion.Euler(50f, -30f, 0f);
|
||||
Light directionalLight = lightObject.GetComponent<Light>();
|
||||
directionalLight.type = LightType.Directional;
|
||||
directionalLight.intensity = 1f;
|
||||
}
|
||||
|
||||
private static void EnsureBuildScene()
|
||||
{
|
||||
List<EditorBuildSettingsScene> scenes = new(EditorBuildSettings.scenes);
|
||||
for (int i = 0; i < scenes.Count; i++)
|
||||
if (scenes[i].path == ScenePath)
|
||||
return;
|
||||
scenes.Add(new EditorBuildSettingsScene(ScenePath, true));
|
||||
EditorBuildSettings.scenes = scenes.ToArray();
|
||||
}
|
||||
|
||||
private static void EnsureFolder(string path)
|
||||
{
|
||||
if (AssetDatabase.IsValidFolder(path)) return;
|
||||
int separator = path.LastIndexOf('/');
|
||||
if (separator <= 0) throw new InvalidOperationException($"Invalid asset folder: {path}");
|
||||
string parent = path.Substring(0, separator);
|
||||
string folder = path.Substring(separator + 1);
|
||||
EnsureFolder(parent);
|
||||
AssetDatabase.CreateFolder(parent, folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5cf60fd8a35b48d79ce8882f1bb6044e
|
||||
MonoImporter
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user