refactor: cleanup and reorganize Unity scene and assets

main
uc-hoba 3 weeks ago
parent 84d253b227
commit b054a25e71
  1. 12791
      Assets/Main.unity
  2. 8
      Assets/Resources.meta
  3. 39
      Assets/Resources/Output.renderTexture
  4. 8
      Assets/Resources/Output.renderTexture.meta
  5. 55
      Assets/Settings/PC_Display.asset
  6. 8
      Assets/Settings/PC_Display.asset.meta
  7. 1
      Assets/Settings/PC_RPAsset.asset
  8. 8
      Assets/Temp.meta
  9. BIN
      Assets/Temp/Street_2.usdz
  10. 16
      Assets/Temp/Street_2.usdz.meta
  11. 174
      Assets/TiltShift/TiltShiftFeature.cs
  12. 267
      Packages/com.ultracombos.giant-museum/Editor/DebugSettingsGUIEditor.cs
  13. 2
      Packages/com.ultracombos.giant-museum/Editor/DebugSettingsGUIEditor.cs.meta
  14. 2
      Packages/com.ultracombos.giant-museum/Editor/UltraCombos.GiantMuseum.Editor.asmdef
  15. 430
      Packages/com.ultracombos.giant-museum/Runtime/DebugSettingsGUI.cs
  16. 2
      Packages/com.ultracombos.giant-museum/Runtime/DebugSettingsGUI.cs.meta
  17. 2
      Packages/com.ultracombos.giant-museum/Runtime/UltraCombos.GiantMuseum.Runtime.asmdef
  18. 1
      Packages/manifest.json
  19. 28
      Packages/packages-lock.json
  20. 8
      Unity-25014-ReadySetRide.slnx

File diff suppressed because it is too large Load Diff

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a7ef01be24be39e4f80509cc98fa5621
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,39 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!84 &8400000
RenderTexture:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Output
m_ImageContentsHash:
serializedVersion: 2
Hash: 00000000000000000000000000000000
m_IsAlphaChannelOptional: 0
serializedVersion: 6
m_Width: 1080
m_Height: 1920
m_AntiAliasing: 1
m_MipCount: -1
m_DepthStencilFormat: 94
m_ColorFormat: 48
m_MipMap: 0
m_GenerateMips: 1
m_SRGB: 0
m_UseDynamicScale: 0
m_UseDynamicScaleExplicit: 0
m_BindMS: 0
m_EnableCompatibleFormat: 1
m_EnableRandomWrite: 0
m_TextureSettings:
serializedVersion: 2
m_FilterMode: 1
m_Aniso: 0
m_MipBias: 0
m_WrapU: 1
m_WrapV: 1
m_WrapW: 1
m_Dimension: 2
m_VolumeDepth: 1
m_ShadowSamplingMode: 2

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: de3934cb10406bb4daa84cabd7a9a4d8
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 8400000
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,55 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: de640fe3d0db1804a85f9fc8f5cadab6, type: 3}
m_Name: PC_Display
m_EditorClassIdentifier:
debugShaders:
debugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7, type: 3}
hdrDebugViewPS: {fileID: 4800000, guid: 573620ae32aec764abd4d728906d2587, type: 3}
probeVolumeSamplingDebugComputeShader: {fileID: 7200000, guid: 53626a513ea68ce47b59dc1299fe3959, type: 3}
probeVolumeResources:
probeVolumeDebugShader: {fileID: 4800000, guid: e5c6678ed2aaa91408dd3df699057aae, type: 3}
probeVolumeFragmentationDebugShader: {fileID: 4800000, guid: 03cfc4915c15d504a9ed85ecc404e607, type: 3}
probeVolumeOffsetDebugShader: {fileID: 4800000, guid: 53a11f4ebaebf4049b3638ef78dc9664, type: 3}
probeVolumeSamplingDebugShader: {fileID: 4800000, guid: 8f96cd657dc40064aa21efcc7e50a2e7, type: 3}
probeSamplingDebugMesh: {fileID: -3555484719484374845, guid: 57d7c4c16e2765b47a4d2069b311bffe, type: 3}
probeSamplingDebugTexture: {fileID: 2800000, guid: 24ec0e140fb444a44ab96ee80844e18e, type: 3}
probeVolumeBlendStatesCS: {fileID: 7200000, guid: b9a23f869c4fd45f19c5ada54dd82176, type: 3}
m_RendererFeatures: []
m_RendererFeatureMap:
m_UseNativeRenderPass: 1
postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2}
m_AssetVersion: 3
m_PrepassLayerMask:
serializedVersion: 2
m_Bits: 4294967295
m_OpaqueLayerMask:
serializedVersion: 2
m_Bits: 4294967295
m_TransparentLayerMask:
serializedVersion: 2
m_Bits: 4294967295
m_DefaultStencilState:
overrideStencilState: 0
stencilReference: 1
stencilCompareFunction: 3
passOperation: 2
failOperation: 0
zFailOperation: 0
m_ShadowTransparentReceive: 1
m_RenderingMode: 2
m_DepthPrimingMode: 0
m_CopyDepthMode: 0
m_DepthAttachmentFormat: 0
m_DepthTextureFormat: 0
m_AccurateGbufferNormals: 0
m_IntermediateTextureMode: 0

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b1d28fcc729e55442a8d6b5c1d284860
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

@ -17,6 +17,7 @@ MonoBehaviour:
m_RendererType: 1 m_RendererType: 1
m_RendererData: {fileID: 0} m_RendererData: {fileID: 0}
m_RendererDataList: m_RendererDataList:
- {fileID: 11400000, guid: b1d28fcc729e55442a8d6b5c1d284860, type: 2}
- {fileID: 11400000, guid: f288ae1f4751b564a96ac7587541f7a2, type: 2} - {fileID: 11400000, guid: f288ae1f4751b564a96ac7587541f7a2, type: 2}
m_DefaultRendererIndex: 0 m_DefaultRendererIndex: 0
m_RequireDepthTexture: 1 m_RequireDepthTexture: 1

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9004a800b8b48124abf23910ab4e6e31
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

@ -0,0 +1,16 @@
fileFormatVersion: 2
guid: 2419b212eec718e4c8153284ca64a8a5
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 7953a3c9778e49e4fbcdfd6a826af2cf, type: 3}
graph: {fileID: 11400000, guid: 628e2af98bba41b4091287c878e2b3a5, type: 2}
importSettingOverrides: []
isUsdRoot: 0
references:
version: 2
RefIds: []

@ -1,15 +1,14 @@
using UnityEngine; using UnityEngine;
using UnityEngine.Rendering; using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.Rendering.RenderGraphModule; using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.RenderGraphModule.Util; using UnityEngine.Rendering.Universal;
/// <summary> /// <summary>
/// Tilt-shift (移軸鏡) post-process effect for URP 17 (Unity 6). /// Tilt-shift post-process effect for URP 17 (Unity 6).
/// ///
/// Two modes: /// Two modes:
/// ScreenSpace — blur based on vertical screen position (fast, camera-angle dependent). /// ScreenSpace — blur based on vertical screen position (fast, no depth buffer needed).
/// DepthBased — Scheimpflug tilt-plane CoC using the depth buffer (physically accurate). /// DepthBased — Scheimpflug tilt-plane CoC using the depth buffer (physically accurate).
/// </summary> /// </summary>
public class TiltShiftFeature : ScriptableRendererFeature public class TiltShiftFeature : ScriptableRendererFeature
{ {
@ -17,9 +16,10 @@ public class TiltShiftFeature : ScriptableRendererFeature
{ {
/// <summary>Blur based on vertical UV position. No depth buffer required.</summary> /// <summary>Blur based on vertical UV position. No depth buffer required.</summary>
ScreenSpace, ScreenSpace,
/// <summary> /// <summary>
/// Blur based on distance from a tilted focal plane in world space (Scheimpflug principle). /// Blur based on distance from a tilted focal plane in world space (Scheimpflug principle).
/// Requires the camera's Depth Texture to be enabled. /// Requires the camera Depth Texture to be enabled.
/// </summary> /// </summary>
DepthBased DepthBased
} }
@ -51,7 +51,7 @@ public class TiltShiftFeature : ScriptableRendererFeature
public float focusDistance = 10f; public float focusDistance = 10f;
[Tooltip("How much the focal plane depth shifts per screen-height unit.\n" + [Tooltip("How much the focal plane depth shifts per screen-height unit.\n" +
"Positive top of screen focuses deeper (typical for bird's-eye view).\n" + "Positive = top of screen focuses deeper (typical for bird's-eye view).\n" +
"Set to 0 for standard (flat) DoF.")] "Set to 0 for standard (flat) DoF.")]
[Range(-200f, 200f)] [Range(-200f, 200f)]
public float tiltFactor = 30f; public float tiltFactor = 30f;
@ -78,10 +78,10 @@ public class TiltShiftFeature : ScriptableRendererFeature
public Settings settings = new Settings(); public Settings settings = new Settings();
TiltShiftPass _pass; private TiltShiftPass renderPass;
Material _material; private Material blitMaterial;
// ── Lifecycle ───────────────────────────────────────────────────────────────── // ── Lifecycle ─────────────────────────────────────────────────────────────────
public override void Create() public override void Create()
{ {
@ -92,65 +92,73 @@ public class TiltShiftFeature : ScriptableRendererFeature
return; return;
} }
_material = CoreUtils.CreateEngineMaterial(shader); blitMaterial = CoreUtils.CreateEngineMaterial(shader);
_pass = new TiltShiftPass(_material, settings); renderPass = new TiltShiftPass(blitMaterial, settings);
_pass.renderPassEvent = settings.injectionPoint; renderPass.renderPassEvent = settings.injectionPoint;
} }
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{ {
if (_material == null || _pass == null) return; if (blitMaterial == null || renderPass == null)
{
return;
}
var camType = renderingData.cameraData.cameraType; var camType = renderingData.cameraData.cameraType;
if (camType == CameraType.Preview || camType == CameraType.Reflection) return; if (camType == CameraType.Preview || camType == CameraType.Reflection)
{
return;
}
_pass.UpdateSettings(settings); renderPass.UpdateSettings(settings);
_pass.renderPassEvent = settings.injectionPoint; renderPass.renderPassEvent = settings.injectionPoint;
renderer.EnqueuePass(_pass); renderer.EnqueuePass(renderPass);
} }
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
CoreUtils.Destroy(_material); CoreUtils.Destroy(blitMaterial);
} }
// ── Inner render pass ────────────────────────────────────────────────────────── // ── Inner render pass ──────────────────────────────────────────────────────────
sealed class TiltShiftPass : ScriptableRenderPass private sealed class TiltShiftPass : ScriptableRenderPass
{ {
// Shader property IDs // Shader property IDs
static readonly int s_MaxBlurRadius = Shader.PropertyToID("_TiltShift_MaxBlurRadius"); private static readonly int propMaxBlurRadius = Shader.PropertyToID("_TiltShift_MaxBlurRadius");
static readonly int s_SampleCount = Shader.PropertyToID("_TiltShift_SampleCount"); private static readonly int propSampleCount = Shader.PropertyToID("_TiltShift_SampleCount");
static readonly int s_CenterOffset = Shader.PropertyToID("_TiltShift_CenterOffset"); private static readonly int propCenterOffset = Shader.PropertyToID("_TiltShift_CenterOffset");
static readonly int s_FocusWidth = Shader.PropertyToID("_TiltShift_FocusWidth"); private static readonly int propFocusWidth = Shader.PropertyToID("_TiltShift_FocusWidth");
static readonly int s_FalloffRange = Shader.PropertyToID("_TiltShift_FalloffRange"); private static readonly int propFalloffRange = Shader.PropertyToID("_TiltShift_FalloffRange");
static readonly int s_FocusDistance = Shader.PropertyToID("_TiltShift_FocusDistance"); private static readonly int propFocusDistance = Shader.PropertyToID("_TiltShift_FocusDistance");
static readonly int s_TiltFactor = Shader.PropertyToID("_TiltShift_TiltFactor"); private static readonly int propTiltFactor = Shader.PropertyToID("_TiltShift_TiltFactor");
static readonly int s_FocusBand = Shader.PropertyToID("_TiltShift_FocusBand"); private static readonly int propFocusBand = Shader.PropertyToID("_TiltShift_FocusBand");
static readonly int s_DepthFalloff = Shader.PropertyToID("_TiltShift_DepthFalloff"); private static readonly int propDepthFalloff = Shader.PropertyToID("_TiltShift_DepthFalloff");
// Shader keyword for mode switch private const string depthModeKeyword = "DEPTH_MODE";
const string k_DepthModeKeyword = "DEPTH_MODE";
private readonly Material blitMaterial;
readonly Material _material; private Settings currentSettings;
Settings _settings;
// PassData carries what the render func needs at execution time. // PassData carries what the render func needs at execution time.
class PassData private class PassData
{ {
public TextureHandle source; public TextureHandle source;
public Material material; public Material material;
public int passIndex; public int passIndex;
} }
public TiltShiftPass(Material material, Settings settings) public TiltShiftPass(Material mat, Settings passSettings)
{ {
_material = material; blitMaterial = mat;
_settings = settings; currentSettings = passSettings;
requiresIntermediateTexture = true; requiresIntermediateTexture = true;
} }
public void UpdateSettings(Settings s) => _settings = s; public void UpdateSettings(Settings s)
{
currentSettings = s;
}
// ── RecordRenderGraph ────────────────────────────────────────────────────── // ── RecordRenderGraph ──────────────────────────────────────────────────────
@ -159,79 +167,89 @@ public class TiltShiftFeature : ScriptableRendererFeature
var resourceData = frameData.Get<UniversalResourceData>(); var resourceData = frameData.Get<UniversalResourceData>();
if (resourceData.isActiveTargetBackBuffer) if (resourceData.isActiveTargetBackBuffer)
{
return; return;
}
bool useDepth = _settings.blurMode == BlurMode.DepthBased; var useDepth = currentSettings.blurMode == BlurMode.DepthBased;
// Validate depth texture availability. // Validate depth texture availability.
if (useDepth && !resourceData.cameraDepthTexture.IsValid()) if (useDepth && !resourceData.cameraDepthTexture.IsValid())
{ {
Debug.LogWarning("[TiltShift] DepthBased mode requires the camera's Depth Texture. " + Debug.LogWarning("[TiltShift] DepthBased mode requires the camera Depth Texture. " +
"Enable it in the URP Renderer asset or Camera component."); "Enable it in the URP Renderer asset or Camera component.");
useDepth = false; useDepth = false;
} }
// ── Push settings to material ────────────────────────────────────────── // Push settings to material.
_material.SetFloat(s_MaxBlurRadius, _settings.maxBlurRadius); blitMaterial.SetFloat(propMaxBlurRadius, currentSettings.maxBlurRadius);
_material.SetInt (s_SampleCount, _settings.sampleCount); blitMaterial.SetInt(propSampleCount, currentSettings.sampleCount);
_material.SetFloat(s_CenterOffset, _settings.centerOffset); blitMaterial.SetFloat(propCenterOffset, currentSettings.centerOffset);
_material.SetFloat(s_FocusWidth, _settings.focusWidth); blitMaterial.SetFloat(propFocusWidth, currentSettings.focusWidth);
_material.SetFloat(s_FalloffRange, _settings.falloffRange); blitMaterial.SetFloat(propFalloffRange, currentSettings.falloffRange);
_material.SetFloat(s_FocusDistance, _settings.focusDistance); blitMaterial.SetFloat(propFocusDistance, currentSettings.focusDistance);
_material.SetFloat(s_TiltFactor, _settings.tiltFactor); blitMaterial.SetFloat(propTiltFactor, currentSettings.tiltFactor);
_material.SetFloat(s_FocusBand, _settings.focusBand); blitMaterial.SetFloat(propFocusBand, currentSettings.focusBand);
_material.SetFloat(s_DepthFalloff, _settings.depthFalloff); blitMaterial.SetFloat(propDepthFalloff, currentSettings.depthFalloff);
if (useDepth) _material.EnableKeyword(k_DepthModeKeyword); if (useDepth)
else _material.DisableKeyword(k_DepthModeKeyword); {
blitMaterial.EnableKeyword(depthModeKeyword);
// ── Texture handles ──────────────────────────────────────────────────── }
else
{
blitMaterial.DisableKeyword(depthModeKeyword);
}
// Texture handles
var source = resourceData.activeColorTexture; var source = resourceData.activeColorTexture;
var depth = useDepth ? resourceData.cameraDepthTexture : TextureHandle.nullHandle; var depth = useDepth ? resourceData.cameraDepthTexture : TextureHandle.nullHandle;
var desc = renderGraph.GetTextureDesc(source); var desc = renderGraph.GetTextureDesc(source);
desc.clearBuffer = false; desc.clearBuffer = false;
desc.name = "TiltShift_Temp"; desc.name = "TiltShift_Temp";
TextureHandle temp = renderGraph.CreateTexture(desc); var temp = renderGraph.CreateTexture(desc);
desc.name = "TiltShift_Final";
TextureHandle final = renderGraph.CreateTexture(desc); desc.name = "TiltShift_Final";
var final = renderGraph.CreateTexture(desc);
// ── Pass 0 — Horizontal blur (source → temp) ────────────────────────── // Pass 0 — horizontal blur (source to temp)
AddBlurPass(renderGraph, "TiltShift_H", source, depth, temp, passIndex: 0, useDepth); AddBlurPass(renderGraph, "TiltShift_H", source, depth, temp, passIndex: 0, useDepth);
// ── Pass 1 — Vertical blur (temp → final) ──────────────────────────── // Pass 1 — vertical blur (temp to final)
AddBlurPass(renderGraph, "TiltShift_V", temp, depth, final, passIndex: 1, useDepth); AddBlurPass(renderGraph, "TiltShift_V", temp, depth, final, passIndex: 1, useDepth);
// Redirect camera colour so subsequent passes use our result. // Redirect camera colour so subsequent passes use our result.
resourceData.cameraColor = final; resourceData.cameraColor = final;
} }
// ── Helper to record one blur pass ───────────────────────────────────────── // ── Helper: record one blur pass ───────────────────────────────────────────
void AddBlurPass( private void AddBlurPass(
RenderGraph renderGraph, RenderGraph renderGraph,
string passName, string passName,
TextureHandle source, TextureHandle source,
TextureHandle depth, TextureHandle depth,
TextureHandle dest, TextureHandle dest,
int passIndex, int passIndex,
bool useDepth) bool useDepth)
{ {
using var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData); using var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData);
passData.source = source; passData.source = source;
passData.material = _material; passData.material = blitMaterial;
passData.passIndex = passIndex; passData.passIndex = passIndex;
builder.UseTexture(source, AccessFlags.Read); builder.UseTexture(source, AccessFlags.Read);
// Declare the dependency on the depth texture so the render graph correctly // Declare the depth dependency so the render graph orders this pass after CopyDepthPass.
// orders this pass after URP's CopyDepthPass. We do NOT call SetGlobalTexture // We do not call SetGlobalTexture here — URP's CopyDepthPass already exposes
// here — URP's CopyDepthPass already exposes _CameraDepthTexture globally via // _CameraDepthTexture globally via SetGlobalTextureAfterPass.
// SetGlobalTextureAfterPass, so the shader can sample it without any extra work.
if (useDepth && depth.IsValid()) if (useDepth && depth.IsValid())
{
builder.UseTexture(depth, AccessFlags.Read); builder.UseTexture(depth, AccessFlags.Read);
}
builder.SetRenderAttachment(dest, 0, AccessFlags.Write); builder.SetRenderAttachment(dest, 0, AccessFlags.Write);

@ -0,0 +1,267 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace UltraCombos
{
[CustomEditor(typeof(DebugSettingsGUI))]
public class DebugSettingsGUIEditor : Editor
{
// ── Supported field types ─────────────────────────────────────────────────
private static readonly HashSet<Type> supportedTypes = new()
{
typeof(bool), typeof(int), typeof(float),
typeof(string), typeof(Vector2), typeof(Vector3), typeof(Color)
};
private static bool IsSupported(Type t) => supportedTypes.Contains(t) || t.IsEnum;
// ── Cached SerializedProperties ───────────────────────────────────────────
private SerializedProperty toggleKeyProp;
private SerializedProperty initialRectProp;
private void OnEnable()
{
toggleKeyProp = serializedObject.FindProperty("toggleKey");
initialRectProp = serializedObject.FindProperty("initialRect");
}
// ── Inspector ─────────────────────────────────────────────────────────────
public override void OnInspectorGUI()
{
var gui = (DebugSettingsGUI)target;
serializedObject.Update();
EditorGUILayout.LabelField("General", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(toggleKeyProp, new GUIContent("Toggle Key"));
EditorGUILayout.PropertyField(initialRectProp, new GUIContent("Initial Rect"));
serializedObject.ApplyModifiedProperties();
EditorGUILayout.Space(10);
EditorGUILayout.LabelField("Entries", EditorStyles.boldLabel);
for (var i = 0; i < gui.entries.Count; i++)
{
var removed = DrawEntry(gui, gui.entries[i], i);
if (removed)
{
Undo.RecordObject(gui, "Remove Entry");
gui.entries.RemoveAt(i);
EditorUtility.SetDirty(gui);
i--;
}
EditorGUILayout.Space(2);
}
EditorGUILayout.Space(4);
if (GUILayout.Button("+ Add Entry", GUILayout.Height(26)))
{
Undo.RecordObject(gui, "Add Entry");
gui.entries.Add(new DebugSettingsGUI.Entry());
EditorUtility.SetDirty(gui);
}
}
// ── Draw one Entry — returns true when the user clicked Remove ─────────────
private static bool DrawEntry(DebugSettingsGUI gui, DebugSettingsGUI.Entry entry, int index)
{
var removed = false;
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
{
// Header row
using (new EditorGUILayout.HorizontalScope())
{
var title = string.IsNullOrEmpty(entry.groupLabel)
? $"Entry {index}"
: $"Entry {index} — {entry.groupLabel}";
EditorGUILayout.LabelField(title, EditorStyles.boldLabel);
GUILayout.FlexibleSpace();
if (GUILayout.Button("✕", GUILayout.Width(22), GUILayout.Height(18)))
{
removed = true;
}
}
// Group label
EditorGUI.BeginChangeCheck();
var newLabel = EditorGUILayout.TextField("Group Label", entry.groupLabel);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(gui, "Edit Group Label");
entry.groupLabel = newLabel;
EditorUtility.SetDirty(gui);
}
// Target object.
// Drag any Component or ScriptableObject here.
// Dropping a GameObject picks its first non-Transform Component automatically.
EditorGUI.BeginChangeCheck();
var newTarget = EditorGUILayout.ObjectField(
new GUIContent("Target",
"Drag any Component or ScriptableObject here.\n" +
"Dropping a GameObject picks its first non-Transform Component."),
entry.target,
typeof(UnityEngine.Object),
allowSceneObjects: true);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(gui, "Set Target");
if (newTarget is GameObject go)
{
var comps = go.GetComponents<Component>();
newTarget = comps.FirstOrDefault(c => c is not Transform) ??
comps.FirstOrDefault() ??
(UnityEngine.Object)go;
}
entry.target = newTarget;
entry.fields.Clear();
EditorUtility.SetDirty(gui);
}
// Field selection
if (entry.target != null)
{
DrawFieldSelection(gui, entry);
}
}
return removed;
}
// ── Field checkboxes ──────────────────────────────────────────────────────
private static void DrawFieldSelection(DebugSettingsGUI gui, DebugSettingsGUI.Entry entry)
{
var type = entry.target.GetType();
var fields = CollectFields(type);
if (fields.Count == 0)
{
EditorGUILayout.HelpBox(
"No displayable fields found on this object.\n" +
"Supported types: bool · int · float · string · Vector2 · Vector3 · Color · Enum\n" +
"Fields must be public or marked [SerializeField].",
MessageType.Info);
return;
}
EditorGUILayout.Space(4);
// Select All / None
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Fields to Display", EditorStyles.boldLabel);
GUILayout.FlexibleSpace();
if (GUILayout.Button("All", GUILayout.Width(36)))
{
Undo.RecordObject(gui, "Select All Fields");
entry.fields = fields.Select(f => f.Name).ToList();
EditorUtility.SetDirty(gui);
}
if (GUILayout.Button("None", GUILayout.Width(38)))
{
Undo.RecordObject(gui, "Deselect All Fields");
entry.fields.Clear();
EditorUtility.SetDirty(gui);
}
}
// Per-field toggles
EditorGUI.indentLevel++;
foreach (var fi in fields)
{
var isSelected = entry.fields.Contains(fi.Name);
var niceName = ObjectNames.NicifyVariableName(fi.Name);
var typeName = FriendlyType(fi.FieldType);
var hasRange = fi.GetCustomAttribute<RangeAttribute>() != null;
var label = new GUIContent(
$"{niceName} ({typeName}{(hasRange ? ", Range" : string.Empty)})",
tooltip: $"Field: {fi.Name}\nType: {fi.FieldType.FullName}");
EditorGUI.BeginChangeCheck();
var next = EditorGUILayout.ToggleLeft(label, isSelected);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(gui, next ? "Add Field" : "Remove Field");
if (next)
{
entry.fields.Add(fi.Name);
}
else
{
entry.fields.Remove(fi.Name);
}
EditorUtility.SetDirty(gui);
}
}
EditorGUI.indentLevel--;
}
// ── Reflection helpers ────────────────────────────────────────────────────
private static List<FieldInfo> CollectFields(Type type)
{
const BindingFlags flags =
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
var result = new List<FieldInfo>();
for (var t = type; t != null && t != typeof(UnityEngine.Object); t = t.BaseType)
{
foreach (var fi in t.GetFields(flags | BindingFlags.DeclaredOnly))
{
var exposed = fi.IsPublic || fi.GetCustomAttribute<SerializeField>() != null;
if (!exposed)
{
continue;
}
if (fi.GetCustomAttribute<HideInInspector>() != null)
{
continue;
}
if (!IsSupported(fi.FieldType))
{
continue;
}
if (!result.Any(r => r.Name == fi.Name))
{
result.Add(fi);
}
}
}
return result;
}
private static string FriendlyType(Type t)
{
if (t == typeof(float)) { return "float"; }
if (t == typeof(int)) { return "int"; }
if (t == typeof(bool)) { return "bool"; }
if (t == typeof(string)) { return "string"; }
if (t == typeof(Vector2)) { return "Vector2"; }
if (t == typeof(Vector3)) { return "Vector3"; }
if (t == typeof(Color)) { return "Color"; }
if (t.IsEnum) { return t.Name; }
return t.Name;
}
}
}

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 374031d85af94bb4f89f5a7346ffcd4c

@ -1,6 +1,6 @@
{ {
"name": "UltraCombos.GiantMuseum.Editor", "name": "UltraCombos.GiantMuseum.Editor",
"rootNamespace": "UltraCombos.GiantMuseum", "rootNamespace": "UltraCombos",
"references": [ "references": [
"GUID:b2e08e75d81bb8b408397e7998130fbb" "GUID:b2e08e75d81bb8b408397e7998130fbb"
], ],

@ -0,0 +1,430 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Text;
using UnityEngine;
namespace UltraCombos
{
/// <summary>
/// Runtime settings panel. Press the configured toggle key (default F1) to show or hide.
/// Drag any Component into the Entries list via the Inspector, select the fields to expose,
/// and the panel lets you tweak them live at runtime.
/// </summary>
public class DebugSettingsGUI : MonoBehaviour
{
// ── Data ──────────────────────────────────────────────────────────────────
[Serializable]
public class Entry
{
public UnityEngine.Object target;
public string groupLabel = string.Empty;
/// <summary>Field names written by the custom Editor.</summary>
public List<string> fields = new();
}
[Header("General")]
public KeyCode toggleKey = KeyCode.F1;
public Rect initialRect = new Rect(10, 10, 360, 520);
[Header("Entries")]
public List<Entry> entries = new();
// ── Runtime state ─────────────────────────────────────────────────────────
private bool isVisible;
private Rect windowRect;
private Vector2 scrollPos;
// Styles — lazy-initialised inside OnGUI so GUI.skin is available.
private bool stylesInitialized;
private GUIStyle headerStyle;
private GUIStyle subBoxStyle;
private GUIStyle labelStyle;
private GUIStyle readonlyStyle;
// ── Unity callbacks ───────────────────────────────────────────────────────
private void Awake()
{
windowRect = initialRect;
}
private void Update()
{
if (Input.GetKeyDown(toggleKey))
{
isVisible = !isVisible;
}
}
private void OnGUI()
{
if (!isVisible)
{
return;
}
EnsureStyles();
windowRect = GUILayout.Window(
GetInstanceID(), windowRect, DrawWindow,
$" Debug Settings [{toggleKey}: toggle]");
}
// ── Window ────────────────────────────────────────────────────────────────
private void DrawWindow(int id)
{
scrollPos = GUILayout.BeginScrollView(scrollPos);
var first = true;
foreach (var e in entries)
{
if (e == null || e.target == null || e.fields.Count == 0)
{
continue;
}
if (!first)
{
GUILayout.Space(6);
}
first = false;
DrawEntry(e);
}
GUILayout.EndScrollView();
GUI.DragWindow(new Rect(0, 0, 10000, 22));
}
private void DrawEntry(Entry entry)
{
if (!string.IsNullOrEmpty(entry.groupLabel))
{
GUILayout.Label(entry.groupLabel, headerStyle);
}
var type = entry.target.GetType();
const BindingFlags flags =
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
foreach (var name in entry.fields)
{
var fi = type.GetField(name, flags);
if (fi == null)
{
continue;
}
try
{
DrawField(entry.target, fi);
}
catch
{
// Skip fields that throw during reflection.
}
}
}
// ── Per-type drawing ──────────────────────────────────────────────────────
private void DrawField(object obj, FieldInfo fi)
{
var val = fi.GetValue(obj);
var ftype = fi.FieldType;
var label = NicifyName(fi.Name);
if (ftype == typeof(bool))
{
DrawBool(obj, fi, (bool)val, label);
}
else if (ftype == typeof(float))
{
DrawFloat(obj, fi, (float)val, label);
}
else if (ftype == typeof(int))
{
DrawInt(obj, fi, (int)val, label);
}
else if (ftype == typeof(string))
{
DrawString(obj, fi, (string)val, label);
}
else if (ftype == typeof(Vector2))
{
DrawVector2(obj, fi, (Vector2)val, label);
}
else if (ftype == typeof(Vector3))
{
DrawVector3(obj, fi, (Vector3)val, label);
}
else if (ftype == typeof(Color))
{
DrawColor(obj, fi, (Color)val, label);
}
else if (ftype.IsEnum)
{
DrawEnum(obj, fi, val, ftype, label);
}
else
{
// Unsupported type — read-only display.
using (new GUILayout.HorizontalScope())
{
GUILayout.Label(label, labelStyle, GUILayout.Width(150));
GUILayout.Label(val?.ToString() ?? "(null)", readonlyStyle);
}
}
}
private void DrawBool(object obj, FieldInfo fi, bool val, string label)
{
using (new GUILayout.HorizontalScope())
{
GUILayout.Label(label, labelStyle, GUILayout.Width(150));
var next = GUILayout.Toggle(val, val ? "✓" : string.Empty);
if (next != val)
{
fi.SetValue(obj, next);
}
}
}
private void DrawFloat(object obj, FieldInfo fi, float val, string label)
{
var range = fi.GetCustomAttribute<RangeAttribute>();
using (new GUILayout.HorizontalScope())
{
GUILayout.Label(label, labelStyle, GUILayout.Width(150));
float next;
if (range != null)
{
next = GUILayout.HorizontalSlider(val, range.min, range.max);
GUILayout.Label(next.ToString("F2"), GUILayout.Width(44));
}
else
{
var s = GUILayout.TextField(val.ToString("G5"));
next = float.TryParse(s, out var p) ? p : val;
}
if (!Mathf.Approximately(next, val))
{
fi.SetValue(obj, next);
}
}
}
private void DrawInt(object obj, FieldInfo fi, int val, string label)
{
var range = fi.GetCustomAttribute<RangeAttribute>();
using (new GUILayout.HorizontalScope())
{
GUILayout.Label(label, labelStyle, GUILayout.Width(150));
int next;
if (range != null)
{
next = Mathf.RoundToInt(GUILayout.HorizontalSlider(val, range.min, range.max));
GUILayout.Label(next.ToString(), GUILayout.Width(32));
}
else
{
var s = GUILayout.TextField(val.ToString());
next = int.TryParse(s, out var p) ? p : val;
}
if (next != val)
{
fi.SetValue(obj, next);
}
}
}
private void DrawString(object obj, FieldInfo fi, string val, string label)
{
using (new GUILayout.HorizontalScope())
{
GUILayout.Label(label, labelStyle, GUILayout.Width(150));
var next = GUILayout.TextField(val ?? string.Empty);
if (next != val)
{
fi.SetValue(obj, next);
}
}
}
private void DrawVector2(object obj, FieldInfo fi, Vector2 val, string label)
{
using (new GUILayout.HorizontalScope())
{
GUILayout.Label(label, labelStyle, GUILayout.Width(150));
GUILayout.Label("X", GUILayout.Width(14));
var x = ParseFloat(GUILayout.TextField(val.x.ToString("G4"), GUILayout.Width(52)), val.x);
GUILayout.Label("Y", GUILayout.Width(14));
var y = ParseFloat(GUILayout.TextField(val.y.ToString("G4"), GUILayout.Width(52)), val.y);
var next = new Vector2(x, y);
if (next != val)
{
fi.SetValue(obj, next);
}
}
}
private void DrawVector3(object obj, FieldInfo fi, Vector3 val, string label)
{
using (new GUILayout.HorizontalScope())
{
GUILayout.Label(label, labelStyle, GUILayout.Width(150));
GUILayout.Label("X", GUILayout.Width(14));
var x = ParseFloat(GUILayout.TextField(val.x.ToString("G4"), GUILayout.Width(40)), val.x);
GUILayout.Label("Y", GUILayout.Width(14));
var y = ParseFloat(GUILayout.TextField(val.y.ToString("G4"), GUILayout.Width(40)), val.y);
GUILayout.Label("Z", GUILayout.Width(14));
var z = ParseFloat(GUILayout.TextField(val.z.ToString("G4"), GUILayout.Width(40)), val.z);
var next = new Vector3(x, y, z);
if (next != val)
{
fi.SetValue(obj, next);
}
}
}
private void DrawColor(object obj, FieldInfo fi, Color val, string label)
{
GUILayout.Label(label, headerStyle);
using (new GUILayout.VerticalScope(subBoxStyle))
{
var r = SliderChannel("R", val.r);
var g = SliderChannel("G", val.g);
var b = SliderChannel("B", val.b);
var a = SliderChannel("A", val.a);
var next = new Color(r, g, b, a);
if (next != val)
{
fi.SetValue(obj, next);
}
}
}
private float SliderChannel(string ch, float val)
{
using (new GUILayout.HorizontalScope())
{
GUILayout.Label(ch, GUILayout.Width(16));
var next = GUILayout.HorizontalSlider(val, 0f, 1f);
GUILayout.Label(next.ToString("F2"), GUILayout.Width(36));
return next;
}
}
private void DrawEnum(object obj, FieldInfo fi, object val, Type type, string label)
{
var names = Enum.GetNames(type);
var values = Enum.GetValues(type);
var cur = Array.IndexOf(values, val);
using (new GUILayout.HorizontalScope())
{
GUILayout.Label(label, labelStyle, GUILayout.Width(150));
if (names.Length <= 4)
{
var next = GUILayout.SelectionGrid(cur, names, names.Length);
if (next != cur)
{
fi.SetValue(obj, values.GetValue(next));
}
}
else
{
// Cycle through with prev / next buttons for long enums.
if (GUILayout.Button("◀", GUILayout.Width(24)))
{
cur = (cur - 1 + names.Length) % names.Length;
}
GUILayout.Label(names[Mathf.Clamp(cur, 0, names.Length - 1)]);
if (GUILayout.Button("▶", GUILayout.Width(24)))
{
cur = (cur + 1) % names.Length;
}
fi.SetValue(obj, values.GetValue(Mathf.Clamp(cur, 0, values.Length - 1)));
}
}
}
// ── Helpers ───────────────────────────────────────────────────────────────
private static float ParseFloat(string s, float fallback)
=> float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var v) ? v : fallback;
/// <summary>Converts a camelCase or _prefixed field name to a readable label.</summary>
private static string NicifyName(string s)
{
// Strip leading underscores and "m_" prefix.
var start = 0;
while (start < s.Length && s[start] == '_')
{
start++;
}
if (s.Length > start + 1 && s[start] == 'm' && s[start + 1] == '_')
{
start += 2;
}
var sb = new StringBuilder();
for (var i = start; i < s.Length; i++)
{
var c = s[i];
if (i == start)
{
sb.Append(char.ToUpper(c));
continue;
}
if (char.IsUpper(c) && !char.IsUpper(s[i - 1]))
{
sb.Append(' ');
}
sb.Append(c);
}
return sb.ToString();
}
// ── Styles — OnGUI-safe lazy initialisation ────────────────────────────────
private void EnsureStyles()
{
if (stylesInitialized)
{
return;
}
stylesInitialized = true;
headerStyle = new GUIStyle(GUI.skin.label)
{
fontStyle = FontStyle.Bold,
normal = { textColor = new Color(1f, 0.85f, 0.3f) }
};
subBoxStyle = new GUIStyle(GUI.skin.box)
{
padding = new RectOffset(6, 6, 3, 3),
margin = new RectOffset(2, 2, 0, 0)
};
labelStyle = new GUIStyle(GUI.skin.label)
{
normal = { textColor = new Color(0.85f, 0.85f, 0.85f) }
};
readonlyStyle = new GUIStyle(GUI.skin.label)
{
normal = { textColor = new Color(0.55f, 0.55f, 0.55f) },
fontStyle = FontStyle.Italic
};
}
}
}

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: da5159de08551c3438978ca2a98e1a50

@ -1,6 +1,6 @@
{ {
"name": "UltraCombos.GiantMuseum.Runtime", "name": "UltraCombos.GiantMuseum.Runtime",
"rootNamespace": "UltraCombos.GiantMuseum", "rootNamespace": "UltraCombos",
"references": [ "references": [
"GUID:75469ad4d38634e559750d17036d5f7c" "GUID:75469ad4d38634e559750d17036d5f7c"
], ],

@ -11,6 +11,7 @@
"dependencies": { "dependencies": {
"com.github-glitchenzo.nugetforunity": "https://github.com/GlitchEnzo/NuGetForUnity.git?path=/src/NuGetForUnity", "com.github-glitchenzo.nugetforunity": "https://github.com/GlitchEnzo/NuGetForUnity.git?path=/src/NuGetForUnity",
"com.unity.ide.visualstudio": "2.0.26", "com.unity.ide.visualstudio": "2.0.26",
"com.unity.importer.usd": "1.0.0-pre.2",
"com.unity.inputsystem": "1.19.0", "com.unity.inputsystem": "1.19.0",
"com.unity.render-pipelines.universal": "17.3.0", "com.unity.render-pipelines.universal": "17.3.0",
"com.unity.timeline": "1.8.11", "com.unity.timeline": "1.8.11",

@ -15,7 +15,7 @@
}, },
"com.unity.burst": { "com.unity.burst": {
"version": "1.8.28", "version": "1.8.28",
"depth": 2, "depth": 1,
"source": "registry", "source": "registry",
"dependencies": { "dependencies": {
"com.unity.mathematics": "1.2.1", "com.unity.mathematics": "1.2.1",
@ -25,7 +25,7 @@
}, },
"com.unity.collections": { "com.unity.collections": {
"version": "2.6.5", "version": "2.6.5",
"depth": 2, "depth": 1,
"source": "registry", "source": "registry",
"dependencies": { "dependencies": {
"com.unity.burst": "1.8.27", "com.unity.burst": "1.8.27",
@ -51,6 +51,19 @@
}, },
"url": "https://packages.unity.com" "url": "https://packages.unity.com"
}, },
"com.unity.importer.usd": {
"version": "1.0.0-pre.2",
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.burst": "1.7.3",
"com.unity.usd.core": "1.0.0-pre.1",
"com.unity.collections": "1.3.1",
"com.unity.mathematics": "1.2.6",
"com.unity.shadergraph": "15.0.0"
},
"url": "https://packages.unity.com"
},
"com.unity.inputsystem": { "com.unity.inputsystem": {
"version": "1.19.0", "version": "1.19.0",
"depth": 0, "depth": 0,
@ -69,7 +82,7 @@
}, },
"com.unity.nuget.mono-cecil": { "com.unity.nuget.mono-cecil": {
"version": "1.11.6", "version": "1.11.6",
"depth": 3, "depth": 2,
"source": "registry", "source": "registry",
"dependencies": {}, "dependencies": {},
"url": "https://packages.unity.com" "url": "https://packages.unity.com"
@ -134,7 +147,7 @@
}, },
"com.unity.test-framework.performance": { "com.unity.test-framework.performance": {
"version": "3.2.0", "version": "3.2.0",
"depth": 3, "depth": 2,
"source": "registry", "source": "registry",
"dependencies": { "dependencies": {
"com.unity.test-framework": "1.1.33", "com.unity.test-framework": "1.1.33",
@ -163,6 +176,13 @@
"com.unity.modules.imgui": "1.0.0" "com.unity.modules.imgui": "1.0.0"
} }
}, },
"com.unity.usd.core": {
"version": "1.0.0-pre.1",
"depth": 1,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
},
"jp.keijiro.klak.lineargradient": { "jp.keijiro.klak.lineargradient": {
"version": "1.0.2", "version": "1.0.2",
"depth": 1, "depth": 1,

@ -1,18 +1,23 @@
<Solution> <Solution>
<Project Path="Unity.Timeline.csproj" /> <Project Path="Unity.Timeline.csproj" />
<Project Path="Unity.Importer.csproj" />
<Project Path="Unity.Mathematics.csproj" /> <Project Path="Unity.Mathematics.csproj" />
<Project Path="Unity.Timeline.Editor.csproj" /> <Project Path="Unity.Timeline.Editor.csproj" />
<Project Path="Unity.Collections.csproj" /> <Project Path="Unity.Collections.csproj" />
<Project Path="Metamesh.Editor.csproj" /> <Project Path="Metamesh.Editor.csproj" />
<Project Path="Unity.PerformanceTesting.Editor.csproj" /> <Project Path="Unity.PerformanceTesting.Editor.csproj" />
<Project Path="Unity.Burst.csproj" /> <Project Path="Unity.Burst.csproj" />
<Project Path="Unity.Importer.USD.csproj" />
<Project Path="Unity.VisualStudio.Editor.csproj" /> <Project Path="Unity.VisualStudio.Editor.csproj" />
<Project Path="Unity.InputSystem.csproj" /> <Project Path="Unity.InputSystem.csproj" />
<Project Path="Unity.Searcher.Editor.csproj" /> <Project Path="Unity.Searcher.Editor.csproj" />
<Project Path="USD.NET.Unity.csproj" />
<Project Path="Unity.Burst.CodeGen.csproj" /> <Project Path="Unity.Burst.CodeGen.csproj" />
<Project Path="Unity.PerformanceTesting.csproj" /> <Project Path="Unity.PerformanceTesting.csproj" />
<Project Path="Unity.Importer.Editor.csproj" />
<Project Path="Unity.Burst.Editor.csproj" /> <Project Path="Unity.Burst.Editor.csproj" />
<Project Path="OscJack.Runtime.csproj" /> <Project Path="OscJack.Runtime.csproj" />
<Project Path="Unity.Importer.USD.Editor.csproj" />
<Project Path="NuGetForUnity.csproj" /> <Project Path="NuGetForUnity.csproj" />
<Project Path="Unity.InputSystem.ForUI.csproj" /> <Project Path="Unity.InputSystem.ForUI.csproj" />
<Project Path="Unity.Collections.CodeGen.csproj" /> <Project Path="Unity.Collections.CodeGen.csproj" />
@ -21,11 +26,12 @@
<Project Path="HBAO.Universal.Runtime.csproj" /> <Project Path="HBAO.Universal.Runtime.csproj" />
<Project Path="Unity.InputSystem.TestFramework.csproj" /> <Project Path="Unity.InputSystem.TestFramework.csproj" />
<Project Path="Assembly-CSharp.csproj" /> <Project Path="Assembly-CSharp.csproj" />
<Project Path="UltraCombos.GiantMuseum.Runtime.csproj" />
<Project Path="Metatex.Editor.csproj" /> <Project Path="Metatex.Editor.csproj" />
<Project Path="Klak.Spout.Editor.csproj" /> <Project Path="Klak.Spout.Editor.csproj" />
<Project Path="Unity.USD.Core.csproj" />
<Project Path="Klak.Chromatics.LinearGradient.Runtime.csproj" /> <Project Path="Klak.Chromatics.LinearGradient.Runtime.csproj" />
<Project Path="OscJack.Editor.csproj" /> <Project Path="OscJack.Editor.csproj" />
<Project Path="UltraCombos.GiantMuseum.Runtime.csproj" />
<Project Path="Unity.InputSystem.DocCodeSamples.csproj" /> <Project Path="Unity.InputSystem.DocCodeSamples.csproj" />
<Project Path="Sirenix.OdinInspector.Modules.UnityMathematics.csproj" /> <Project Path="Sirenix.OdinInspector.Modules.UnityMathematics.csproj" />
<Project Path="UltraCombos.GiantMuseum.Editor.csproj" /> <Project Path="UltraCombos.GiantMuseum.Editor.csproj" />

Loading…
Cancel
Save