2.5 update

This commit is contained in:
2026-09-28 11:07:17 +09:00
parent abd2da2057
commit 7978d82196
976 changed files with 111965 additions and 313 deletions
@@ -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: