This commit is contained in:
SoulliesOfficial
2026-06-09 01:43:55 -04:00
parent 0fb72f5bba
commit 5fc1392747
171 changed files with 30149 additions and 22331 deletions

View File

@@ -0,0 +1,70 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: New Material
m_Shader: {fileID: 4800000, guid: 4aa0ba8da2b8f5b4ca69696b5c13144c, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AlphaCutoff: 0.5
- _Opacity: 1
- _count: 8
- _cube_radius: 0.033
- _cube_softness: 0
- _cube_width: 0.05
- _gap_softness: 0
- _gapsize: 0.5
- _ring_radius: 0.5
- _ring_rotation: 0
- _ring_softness: 0
- _ring_width: 0.02
m_Colors:
- _EmissionColor: {r: 1, g: 1, b: 1, a: 1}
- _ImageColor: {r: 1, g: 1, b: 1, a: 0}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &2669937578497038573
MonoBehaviour:
m_ObjectHideFlags: 11
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 4aa0ba8da2b8f5b4ca69696b5c13144c
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,6 @@ GameObject:
serializedVersion: 6
m_Component:
- component: {fileID: 7986888078867694945}
- component: {fileID: 1245899045477576615}
- component: {fileID: 2422713327444875072}
m_Layer: 5
m_Name: DynamicUISubcontainer
@@ -37,30 +36,6 @@ RectTransform:
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 650, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &1245899045477576615
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5517159431924024882}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8a8695521f0d02e499659fee002a26c2, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Padding:
m_Left: 15
m_Right: 15
m_Top: 15
m_Bottom: 15
m_ChildAlignment: 0
m_StartCorner: 0
m_StartAxis: 0
m_CellSize: {x: 200, y: 100}
m_Spacing: {x: 10, y: 10}
m_Constraint: 0
m_ConstraintCount: 2
--- !u!114 &2422713327444875072
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -74,6 +49,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
parentContainer: {fileID: 0}
gridLayoutGroup: {fileID: 1245899045477576615}
horizontalLayoutGroup: {fileID: 0}
rect: {fileID: 7986888078867694945}
dynamicUIElements: []
rowHeight: 100

View File

@@ -103,20 +103,23 @@ MonoBehaviour:
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_characterHorizontalScale: 1
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_enableWordWrapping: 1
m_TextWrappingMode: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
@@ -185,8 +188,8 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
title: {fileID: 4146383444293307128}
canvasGroup: {fileID: 0}
parameterName:
isAlwaysUpdated: 0
text: {fileID: 1511802765271541473}
--- !u!1 &4303436613740383453
GameObject:
@@ -291,20 +294,23 @@ MonoBehaviour:
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_characterHorizontalScale: 1
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_enableWordWrapping: 1
m_TextWrappingMode: 0
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0

View File

@@ -30,6 +30,7 @@ namespace Ichni.Editor.Commands
{
if (target == null) return;
ReflectionHelper.SetDeepValue(target, path, newValue);
target.Refresh();
}
public void Undo()
@@ -38,9 +39,8 @@ namespace Ichni.Editor.Commands
if (target == null) return;
ReflectionHelper.SetDeepValue(target, path, oldValue);
target.Refresh();
target.Refresh();
RefreshInspectorIfMatched();
}
public void Redo()
@@ -49,8 +49,7 @@ namespace Ichni.Editor.Commands
ReflectionHelper.SetDeepValue(target, path, newValue);
target.Refresh();
RefreshInspectorIfMatched();
}
public bool TryMerge(ICommand other)

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: ea823e8c44a0ca842a3b0e59390994de
guid: ef107ab630341ed4083145b5a21db705
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -0,0 +1,163 @@
using System;
using System.Collections.Generic;
using Ichni.RhythmGame;
using UnityEngine;
namespace Ichni.Editor
{
/// <summary>
/// 类型→绘制器注册表。集中管理 Prefab 引用和 Type→IPropertyDrawer 映射。
/// 替代分散在 IHaveInspection 各 Generate 方法中的 basePrefabs 直接访问。
/// 使用惰性单例,首次访问时自动注册所有内置 Drawer。
/// </summary>
public class DrawerRegistry
{
private static DrawerRegistry _instance;
/// <summary> 全局单例 </summary>
public static DrawerRegistry Instance
{
get
{
if (_instance == null)
{
_instance = new DrawerRegistry();
_instance.RegisterBuiltInDrawers();
}
return _instance;
}
}
// 按属性类型注册的 Drawer (优先级最高)
private readonly Dictionary<Type, IPropertyDrawer> _attributeDrawers = new Dictionary<Type, IPropertyDrawer>();
// 按字段类型注册的 Drawer
private readonly Dictionary<Type, IPropertyDrawer> _typeDrawers = new Dictionary<Type, IPropertyDrawer>();
// 特殊 Drawer (Button, HintText 等不绑定字段类型的)
private readonly Dictionary<string, IPropertyDrawer> _namedDrawers = new Dictionary<string, IPropertyDrawer>();
// Fallback Drawer
private IPropertyDrawer _fallbackDrawer;
/// <summary> Prefab 引用桥接到 BasePrefabsCollection </summary>
private BasePrefabsCollection Prefabs => EditorManager.instance.basePrefabs;
/// <summary> 注册字段类型→Drawer 映射 </summary>
public void RegisterDrawer(Type fieldType, IPropertyDrawer drawer)
{
_typeDrawers[fieldType] = drawer;
}
/// <summary> 注册属性类型→Drawer 映射(属性特化优先于字段类型) </summary>
public void RegisterAttributeDrawer<TAttr>(IPropertyDrawer drawer) where TAttr : Attribute
{
_attributeDrawers[typeof(TAttr)] = drawer;
}
/// <summary> 注册命名 Drawer用于 Button、HintText 等非类型绑定的控件) </summary>
public void RegisterNamedDrawer(string name, IPropertyDrawer drawer)
{
_namedDrawers[name] = drawer;
}
/// <summary> 设置 Fallback Drawer类型未注册时使用 </summary>
public void SetFallbackDrawer(IPropertyDrawer drawer)
{
_fallbackDrawer = drawer;
}
/// <summary>
/// 获取适用的 Drawer。查找优先级:
/// 1. 属性注册(如 [InspectorSlider] → SliderDrawer
/// 2. 类型注册(如 bool → ToggleDrawer
/// 3. Enum 基类型检测
/// 4. Fallback (InputFieldDrawer)
/// </summary>
public IPropertyDrawer GetDrawer(Type fieldType, Attribute[] attributes = null)
{
// 1. 属性特化优先
if (attributes != null)
{
foreach (var attr in attributes)
{
if (_attributeDrawers.TryGetValue(attr.GetType(), out var attrDrawer))
return attrDrawer;
}
}
// 2. 精确类型匹配
if (fieldType != null && _typeDrawers.TryGetValue(fieldType, out var typeDrawer))
return typeDrawer;
// 3. Enum 基类型检测
if (fieldType != null && fieldType.IsEnum && _typeDrawers.TryGetValue(typeof(Enum), out var enumDrawer))
return enumDrawer;
// 4. Fallback
return _fallbackDrawer;
}
/// <summary> 获取命名 Drawer </summary>
public IPropertyDrawer GetNamedDrawer(string name)
{
_namedDrawers.TryGetValue(name, out var drawer);
return drawer;
}
/// <summary> 获取 DynamicUI Element Prefab </summary>
public GameObject GetPrefab(string elementType)
{
return elementType switch
{
"button" => Prefabs.button,
"toggle" => Prefabs.toggle,
"inputField" => Prefabs.inputField,
"slider" => Prefabs.slider,
"vector2InputField" => Prefabs.vector2InputField,
"vector3InputField" => Prefabs.vector3InputField,
"enumDropdown" => Prefabs.enumDropdown,
"stringListDropdown" => Prefabs.stringListDropdown,
"baseColorPicker" => Prefabs.baseColorPicker,
"emissionColorPicker" => Prefabs.emissionColorPicker,
"hintText" => Prefabs.hintText,
"parameterText" => Prefabs.parameterText,
"hsvDrawer" => Prefabs.hsvDrawer,
"container" => Prefabs.dynamicUIContainer,
"subcontainer" => Prefabs.dynamicUISubcontainer,
_ => null,
};
}
/// <summary>
/// 注册所有内置 Drawer覆盖全部基本字段类型和特化属性。
/// </summary>
private void RegisterBuiltInDrawers()
{
// 基本类型 Drawer
var inputFieldDrawer = new InputFieldDrawer();
RegisterDrawer(typeof(float), inputFieldDrawer);
RegisterDrawer(typeof(int), inputFieldDrawer);
RegisterDrawer(typeof(string), inputFieldDrawer);
SetFallbackDrawer(inputFieldDrawer);
RegisterDrawer(typeof(bool), new ToggleDrawer());
RegisterDrawer(typeof(System.Enum), new EnumDropdownDrawer());
RegisterDrawer(typeof(Vector2), new Vector2FieldDrawer());
RegisterDrawer(typeof(Vector3), new Vector3FieldDrawer());
RegisterDrawer(typeof(Color), new BaseColorPickerDrawer());
// 属性特化 Drawer
RegisterAttributeDrawer<InspectorSliderAttribute>(new SliderDrawer());
RegisterAttributeDrawer<InspectorEmissionColorAttribute>(new EmissionColorPickerDrawer());
// 命名 Drawer非类型绑定的控件
RegisterNamedDrawer("button", new ButtonDrawer());
RegisterNamedDrawer("hintText", new HintTextDrawer());
RegisterNamedDrawer("parameterText", new ParameterTextDrawer());
RegisterNamedDrawer("slider", new SliderDrawer());
RegisterNamedDrawer("stringListDropdown", new StringListDropdownDrawer());
RegisterNamedDrawer("emissionColorPicker", new EmissionColorPickerDrawer());
}
}
}

View File

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

View File

@@ -0,0 +1,27 @@
namespace Ichni.Editor
{
/// <summary>
/// 延迟引用容器 —— 在 Build() 时由 InspectorBuilder 填充。
/// 用于在 Button 回调中访问其他控件(如 UnboundInputField 的值)。
///
/// 用法:
/// <code>
/// var nameRef = new ElementRef&lt;DynamicUIInputField&gt;();
/// InspectorBuilder.For(this)
/// .Section("Search")
/// .UnboundInputField("Name").WithRef(nameRef)
/// .Button("Find", () => {
/// string name = nameRef.Value?.GetValue&lt;string&gt;();
/// })
/// .Build();
/// </code>
/// </summary>
public class ElementRef<T> where T : DynamicUIElement
{
/// <summary> 构建后填充的控件实例Build() 之前为 null </summary>
public T Value { get; internal set; }
/// <summary> 隐式转换,方便直接当 T 使用 </summary>
public static implicit operator T(ElementRef<T> r) => r?.Value;
}
}

View File

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

View File

@@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using Ichni.RhythmGame;
using UnityEngine;
namespace Ichni.Editor
{
/// <summary>
/// 可插拔属性绘制器接口。每种字段类型bool, float, Color 等)
/// 对应一个 Drawer 实现,负责创建和配置对应类型的 DynamicUIElement。
/// </summary>
public interface IPropertyDrawer
{
/// <summary> 默认占据的列数 (1-3)1 = 1/3 行宽 </summary>
int DefaultSpan { get; }
/// <summary> 默认高度 (px) </summary>
float DefaultHeight { get; }
/// <summary> 创建并初始化一个 DynamicUIElement挂载到 parent 下 </summary>
DynamicUIElement Draw(DrawContext ctx);
}
/// <summary>
/// 绘制上下文,传递给 IPropertyDrawer.Draw() 的所有必要信息。
/// 由 InspectorBuilder 在 Build() 时构造。
/// </summary>
public struct DrawContext
{
/// <summary> 绑定的目标元素 </summary>
public IBaseElement Target;
/// <summary> 反射路径ReflectionHelper 使用的字段名) </summary>
public string FieldName;
/// <summary> 显示标签 </summary>
public string Label;
/// <summary> 父容器Row 的 RectTransform </summary>
public Transform Parent;
/// <summary> Drawer 注册表,用于获取 Prefab 引用 </summary>
public DrawerRegistry Registry;
/// <summary> 目标 Inspector 窗口(用于 Mark 和 MarkedElements 注册) </summary>
public IHaveInspection Inspection;
// ──────── 可选配置(由 Builder 链式方法设置)────────
/// <summary> 覆盖默认 Span </summary>
public int? OverrideSpan;
/// <summary> 覆盖默认高度 </summary>
public float? OverrideHeight;
/// <summary> 是否启用自动更新 </summary>
public bool AutoUpdate;
/// <summary> 是否只读 </summary>
public bool ReadOnly;
/// <summary> 值变更时的回调 </summary>
public Action OnChangedCallback;
/// <summary> 控件启用条件(返回 false 时禁用交互) </summary>
public Func<bool> EnabledCondition;
/// <summary> Mark 标记键 </summary>
public string MarkKey;
// ──────── Slider 专用 ────────
public float SliderMin;
public float SliderMax;
public bool SliderWholeNumbers;
// ──────── EmissionColor 专用 ────────
public string EmissionEnabledName;
public string EmissionIntensityName;
// ──────── Button 专用 ────────
public Action ButtonAction;
// ──────── HintText 专用 ────────
public string StaticText;
public Func<string> DynamicText;
// ──────── Dropdown 专用 ────────
public Type EnumType;
public List<string> StringListOptions;
// ──────── Unbound 控件 ────────
/// <summary> 无绑定控件类型 (None = 正常绑定控件) </summary>
public UnboundKind Unbound;
/// <summary> 无绑定 InputField 的默认文本 </summary>
public string DefaultText;
/// <summary> 无绑定 Slider 的默认值 </summary>
public float DefaultFloat;
/// <summary> 无绑定 Vector3Field 的默认值 </summary>
public Vector3 DefaultVector3;
}
/// <summary>
/// 无绑定控件类型枚举。
/// None 表示正常的数据绑定控件;其他值表示不绑定任何字段的独立控件。
/// </summary>
public enum UnboundKind
{
None = 0,
InputField,
Toggle,
Slider,
Vector3Field,
}
}

View File

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

View File

@@ -0,0 +1,147 @@
using System;
namespace Ichni.Editor
{
/// <summary>
/// 字段/属性标注 —— 驱动 InspectorBuilder.AutoBuild() 自动生成 UI 控件。
/// 替代旧版 DynamicUIAttribute新增 GroupOrder 和 Order 排序支持。
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class InspectorFieldAttribute : Attribute
{
/// <summary> 显示名称,为空时使用成员名 </summary>
public string Name { get; set; }
/// <summary> 所属 Section 标题 </summary>
public string Group { get; set; }
/// <summary> Section 排序权重(与 InspectorBuilder.Section 的 sectionOrder 统一排序,升序) </summary>
public int GroupOrder { get; set; }
/// <summary> Section 内元素排序权重(升序,同 order 按声明顺序 stable sort </summary>
public int Order { get; set; }
/// <summary>
/// 排版格子要求。一行最大宽度分为 3 份。
/// 填 1 代表需占 1/3。填 3 代表全宽占满整行。
/// 填 0 由内置工厂根据数据类型自动识别。
/// </summary>
public int Span { get; set; }
/// <summary>
/// 垂直占位高度 (px)。填 0 由系统按类型智能推断。
/// </summary>
public float Height { get; set; }
/// <summary> 是否自动更新(绑定 IHaveAutoUpdate </summary>
public bool AutoUpdate { get; set; }
/// <summary> 只读模式(生成 HintText 替代可编辑控件) </summary>
public bool ReadOnly { get; set; }
public InspectorFieldAttribute(
string name = "",
string group = "Default",
int groupOrder = 0,
int order = 0,
int span = 0,
float height = 0f,
bool autoUpdate = false,
bool readOnly = false)
{
Name = name;
Group = group;
GroupOrder = groupOrder;
Order = order;
Span = span;
Height = height;
AutoUpdate = autoUpdate;
ReadOnly = readOnly;
}
}
/// <summary>
/// Slider 控件标注,适用于 float/int 类型字段。
/// 替代旧版 DynamicUISliderAttribute。
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class InspectorSliderAttribute : InspectorFieldAttribute
{
public float Min { get; set; }
public float Max { get; set; }
public bool WholeNumbers { get; set; }
public InspectorSliderAttribute(
string name = "",
string group = "Default",
float min = 0f,
float max = 1f,
bool wholeNumbers = false,
int groupOrder = 0,
int order = 0,
int span = 0,
float height = 0f,
bool autoUpdate = false,
bool readOnly = false)
: base(name, group, groupOrder, order, span, height, autoUpdate, readOnly)
{
Min = min;
Max = max;
WholeNumbers = wholeNumbers;
}
}
/// <summary>
/// EmissionColor 拾色器标注,支持 HDR 及独立 Emission 强度/开关通道。
/// 替代旧版 DynamicUIEmissionColorAttribute。
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class InspectorEmissionColorAttribute : InspectorFieldAttribute
{
/// <summary> Emission 开关字段名,"NULL" 表示强制启用 </summary>
public string EmissionEnabledName { get; set; }
/// <summary> Emission 强度字段名,"NULL" 表示使用颜色 Alpha 通道 </summary>
public string EmissionIntensityName { get; set; }
public InspectorEmissionColorAttribute(
string name,
string enabledName = "NULL",
string intensityName = "NULL",
string group = "Default",
int groupOrder = 0,
int order = 0,
int span = 3,
float height = 0f)
: base(name, group, groupOrder, order, span, height)
{
EmissionEnabledName = enabledName;
EmissionIntensityName = intensityName;
}
}
/// <summary>
/// 按钮标注,挂载在无参 Method 上。
/// 替代旧版 DynamicUIButtonAttribute。
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class InspectorButtonAttribute : Attribute
{
public string Name { get; set; }
public string Group { get; set; }
public int GroupOrder { get; set; }
public int Order { get; set; }
public InspectorButtonAttribute(
string name = "",
string group = "Default",
int groupOrder = 0,
int order = 0)
{
Name = name;
Group = group;
GroupOrder = groupOrder;
Order = order;
}
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -0,0 +1,181 @@
using System.Collections.Generic;
using UnityEngine;
namespace Ichni.Editor
{
/// <summary>
/// 自动行打包算法 —— 将元素列表按 Span 和高度层级自动装入行。
/// 替代手动调用 GenerateSubcontainer(N) 手动分配列数。
/// </summary>
public static class LayoutPacker
{
/// <summary> 每行最大 Span 总量 </summary>
public const int MaxSpanPerRow = 3;
/// <summary> 行总宽度 (px),与 DynamicUI 现有 600px 布局一致 </summary>
public const float TotalRowWidth = 600f;
/// <summary> 默认行高 (px) </summary>
public const float DefaultRowHeight = 100f;
/// <summary>
/// 高度层级。同一行内只允许相同层级的元素,
/// 避免 100px 的 Toggle 和 280px 的 ColorPicker 混排导致空白浪费。
/// </summary>
public enum HeightTier
{
/// <summary> 标准高度 (&lt;= 100px): bool, float, int, string, enum, Vector2, Vector3, Button... </summary>
Standard,
/// <summary> 中等高度 (&lt;= 280px): BaseColorPicker </summary>
Tall,
/// <summary> 超高 (&gt; 280px): EmissionColorPicker 等 </summary>
ExtraTall
}
/// <summary> 待打包的元素定义 </summary>
public struct ElementDef
{
/// <summary> 占据的列数 (1-3) </summary>
public int Span;
/// <summary> 元素高度 (px) </summary>
public float Height;
/// <summary> 高度层级(由 Height 自动推断) </summary>
public HeightTier Tier;
/// <summary> 排序权重Section 内排序) </summary>
public int Order;
/// <summary> 稳定排序用的原始插入索引 </summary>
public int InsertionIndex;
/// <summary> 绘制上下文 </summary>
public DrawContext Context;
/// <summary> 对应的绘制器 </summary>
public IPropertyDrawer Drawer;
}
/// <summary> 一行的定义 </summary>
public struct RowDef
{
/// <summary> 行内元素 </summary>
public List<ElementDef> Elements;
/// <summary> 行高 = max(行内所有元素的 Height) </summary>
public float RowHeight;
}
/// <summary>
/// 将元素列表打包为行列表。
/// 约束: 同一行内 Span 总和 &lt;= MaxSpanPerRow且元素必须属于相同 HeightTier。
/// 元素先按 Order 升序 stable sort再按顺序贪心装箱。
/// </summary>
public static List<RowDef> Pack(List<ElementDef> elements)
{
var rows = new List<RowDef>();
if (elements == null || elements.Count == 0)
return rows;
// Stable sort: 先按 Order 升序,同 Order 按 InsertionIndex 升序
elements.Sort((a, b) =>
{
int cmp = a.Order.CompareTo(b.Order);
return cmp != 0 ? cmp : a.InsertionIndex.CompareTo(b.InsertionIndex);
});
var currentRow = new List<ElementDef>();
int currentSpan = 0;
HeightTier currentTier = elements[0].Tier;
float currentMaxHeight = 0f;
int currentRowSpan = -1; // 当前行元素的 Span 值(-1 表示尚未确定)
for (int i = 0; i < elements.Count; i++)
{
var elem = elements[i];
int elemSpan = Mathf.Clamp(elem.Span, 1, MaxSpanPerRow);
bool spanOverflow = currentSpan + elemSpan > MaxSpanPerRow;
bool tierMismatch = currentRow.Count > 0 && elem.Tier != currentTier;
// GridLayoutGroup 强制统一 cellSize同一行内混搭不同 Span 宽度会导致变形
bool spanMismatch = currentRow.Count > 0 && elemSpan != currentRowSpan;
if (spanOverflow || tierMismatch || spanMismatch)
{
// 当前行已满、高度层级不兼容或 Span 不一致,提交当前行
FlushRow(rows, currentRow, currentMaxHeight);
currentRow = new List<ElementDef>();
currentSpan = 0;
currentMaxHeight = 0f;
currentTier = elem.Tier;
currentRowSpan = -1;
}
currentRow.Add(elem);
currentSpan += elemSpan;
currentRowSpan = elemSpan;
if (elem.Height > currentMaxHeight)
currentMaxHeight = elem.Height;
}
// 提交最后一行
if (currentRow.Count > 0)
{
FlushRow(rows, currentRow, currentMaxHeight);
}
return rows;
}
/// <summary> 根据高度值推断 HeightTier </summary>
public static HeightTier InferTier(float height)
{
if (height <= DefaultRowHeight)
return HeightTier.Standard;
if (height <= 280f)
return HeightTier.Tall;
return HeightTier.ExtraTall;
}
/// <summary> 根据类型推断默认 Span </summary>
public static int InferSpan(System.Type fieldType)
{
if (fieldType == null) return 1;
if (fieldType == typeof(bool)) return 1;
if (fieldType == typeof(float) || fieldType == typeof(int) || fieldType == typeof(string)) return 1;
if (fieldType.IsEnum) return 1;
if (fieldType == typeof(Vector2)) return 2;
if (fieldType == typeof(Vector3) || fieldType == typeof(Color)) return 3;
return 1;
}
/// <summary> 根据类型推断默认高度 </summary>
public static float InferHeight(System.Type fieldType, System.Attribute[] attributes = null)
{
if (attributes != null)
{
foreach (var attr in attributes)
{
if (attr is InspectorEmissionColorAttribute)
return 320f;
}
}
if (fieldType == typeof(Color))
return 280f;
return DefaultRowHeight;
}
private static void FlushRow(List<RowDef> rows, List<ElementDef> rowElements, float maxHeight)
{
rows.Add(new RowDef
{
Elements = rowElements,
RowHeight = Mathf.Max(maxHeight, DefaultRowHeight)
});
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 4e8a9feff817f194da598f2ef0c357ea

View File

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

View File

@@ -0,0 +1,37 @@
using Ichni.RhythmGame;
using Lean.Pool;
using UnityEngine;
using UnityEngine.UI;
namespace Ichni.Editor
{
/// <summary>
/// 处理 Color 类型的 Drawer。
/// 创建 DynamicUIBaseColorPicker。
/// </summary>
public class BaseColorPickerDrawer : IPropertyDrawer
{
public int DefaultSpan => 3;
public float DefaultHeight => 280f;
public DynamicUIElement Draw(DrawContext ctx)
{
var go = LeanPool.Spawn(ctx.Registry.GetPrefab("baseColorPicker"), ctx.Parent);
var element = go.GetComponent<DynamicUIBaseColorPicker>();
element.Initialize(ctx.Target, ctx.Label, ctx.FieldName);
DynamicUIElement.DisableNavigation(
element.inputFieldBaseR,
element.inputFieldBaseG,
element.inputFieldBaseB,
element.inputFieldBaseA);
int span = ctx.OverrideSpan ?? DefaultSpan;
float height = ctx.OverrideHeight ?? DefaultHeight;
element.SetLayoutSize(span * LayoutPacker.TotalRowWidth / LayoutPacker.MaxSpanPerRow, height);
return element;
}
}
}

View File

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

View File

@@ -0,0 +1,40 @@
using Lean.Pool;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace Ichni.Editor
{
/// <summary>
/// 按钮 Drawer不绑定数据字段。
/// 创建 DynamicUIButton 并绑定 ButtonAction。
/// </summary>
public class ButtonDrawer : IPropertyDrawer
{
public int DefaultSpan => 1;
public float DefaultHeight => LayoutPacker.DefaultRowHeight;
public DynamicUIElement Draw(DrawContext ctx)
{
var go = LeanPool.Spawn(ctx.Registry.GetPrefab("button"), ctx.Parent);
var element = go.GetComponent<DynamicUIButton>();
element.SetText(ctx.Label);
element.Initialize(ctx.Target, ctx.Label, string.Empty);
if (ctx.ButtonAction != null)
{
UnityAction action = () => ctx.ButtonAction();
element.ApplyFunction(action);
}
DynamicUIElement.DisableNavigation(element.button);
int span = ctx.OverrideSpan ?? DefaultSpan;
float height = ctx.OverrideHeight ?? DefaultHeight;
element.SetLayoutSize(span * LayoutPacker.TotalRowWidth / LayoutPacker.MaxSpanPerRow, height);
return element;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 23d17d2a4ce34f84a84f140f213d166c

View File

@@ -0,0 +1,40 @@
using Ichni.RhythmGame;
using Lean.Pool;
using UnityEngine;
using UnityEngine.UI;
namespace Ichni.Editor
{
/// <summary>
/// 处理带 [InspectorEmissionColor] 属性的 Color 类型的 Drawer。
/// 创建 DynamicUIEmissionColorPicker支持 Emission 开关和强度控制。
/// </summary>
public class EmissionColorPickerDrawer : IPropertyDrawer
{
public int DefaultSpan => 3;
public float DefaultHeight => 320f;
public DynamicUIElement Draw(DrawContext ctx)
{
var go = LeanPool.Spawn(ctx.Registry.GetPrefab("emissionColorPicker"), ctx.Parent);
var element = go.GetComponent<DynamicUIEmissionColorPicker>();
element.Initialize(ctx.Target, ctx.Label,
ctx.EmissionEnabledName ?? "NULL",
ctx.FieldName,
ctx.EmissionIntensityName ?? "NULL");
DynamicUIElement.DisableNavigation(
element.inputFieldEmissionR,
element.inputFieldEmissionG,
element.inputFieldEmissionB,
element.inputFieldEmissionI);
int span = ctx.OverrideSpan ?? DefaultSpan;
float height = ctx.OverrideHeight ?? DefaultHeight;
element.SetLayoutSize(span * LayoutPacker.TotalRowWidth / LayoutPacker.MaxSpanPerRow, height);
return element;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 444ef9b226757004f9290d1ea11d2c60

View File

@@ -0,0 +1,49 @@
using System;
using Ichni.RhythmGame;
using Lean.Pool;
using UnityEngine;
namespace Ichni.Editor
{
/// <summary>
/// 处理所有 Enum 类型的 Drawer。
/// 通过反射获取字段的枚举类型,创建 DynamicUIEnumDropdown。
/// </summary>
public class EnumDropdownDrawer : IPropertyDrawer
{
public int DefaultSpan => 1;
public float DefaultHeight => LayoutPacker.DefaultRowHeight;
public DynamicUIElement Draw(DrawContext ctx)
{
var go = LeanPool.Spawn(ctx.Registry.GetPrefab("enumDropdown"), ctx.Parent);
var element = go.GetComponent<DynamicUIEnumDropdown>();
// 获取枚举类型:优先使用 DrawContext 中的 EnumTypefallback 到反射
Type enumType = ctx.EnumType;
if (enumType == null && ctx.Target != null && !string.IsNullOrEmpty(ctx.FieldName))
{
var field = ctx.Target.GetType().GetField(ctx.FieldName);
if (field != null) enumType = field.FieldType;
else
{
var prop = ctx.Target.GetType().GetProperty(ctx.FieldName);
if (prop != null) enumType = prop.PropertyType;
}
}
if (enumType != null)
{
element.SetUpEnum(enumType);
}
element.Initialize(ctx.Target, ctx.Label, ctx.FieldName);
int span = ctx.OverrideSpan ?? DefaultSpan;
float height = ctx.OverrideHeight ?? DefaultHeight;
element.SetLayoutSize(span * LayoutPacker.TotalRowWidth / LayoutPacker.MaxSpanPerRow, height);
return element;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 13456a684c2325341a1010f04d0dabb5

View File

@@ -0,0 +1,38 @@
using Lean.Pool;
using UnityEngine;
namespace Ichni.Editor
{
/// <summary>
/// 提示文本 Drawer支持静态文本和 Func&lt;string&gt; 动态文本。
/// 创建 DynamicUIHintText。
/// </summary>
public class HintTextDrawer : IPropertyDrawer
{
public int DefaultSpan => 1;
public float DefaultHeight => LayoutPacker.DefaultRowHeight;
public DynamicUIElement Draw(DrawContext ctx)
{
var go = LeanPool.Spawn(ctx.Registry.GetPrefab("hintText"), ctx.Parent);
var element = go.GetComponent<DynamicUIHintText>();
element.Initialize(ctx.Target, string.Empty, string.Empty);
if (ctx.DynamicText != null)
{
element.SetUpdatingContent(ctx.DynamicText);
}
else if (ctx.StaticText != null)
{
element.SetContent(ctx.StaticText);
}
int span = ctx.OverrideSpan ?? DefaultSpan;
float height = ctx.OverrideHeight ?? DefaultHeight;
element.SetLayoutSize(span * LayoutPacker.TotalRowWidth / LayoutPacker.MaxSpanPerRow, height);
return element;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6437a7c7bdd1fe748b37bb0ebda0abd1

View File

@@ -0,0 +1,42 @@
using Ichni.RhythmGame;
using Lean.Pool;
using UnityEngine;
using UnityEngine.UI;
namespace Ichni.Editor
{
/// <summary>
/// 处理 float, int, string 类型的 Drawer。
/// 创建 DynamicUIInputField 并绑定反射路径。
/// </summary>
public class InputFieldDrawer : IPropertyDrawer
{
public int DefaultSpan => 1;
public float DefaultHeight => LayoutPacker.DefaultRowHeight;
public DynamicUIElement Draw(DrawContext ctx)
{
var go = LeanPool.Spawn(ctx.Registry.GetPrefab("inputField"), ctx.Parent);
var element = go.GetComponent<DynamicUIInputField>();
element.Initialize(ctx.Target, ctx.Label, ctx.FieldName);
DynamicUIElement.DisableNavigation(element.inputField);
if (ctx.AutoUpdate && element is IHaveAutoUpdate autoUpdate)
{
autoUpdate.SetAutoUpdate(true);
}
if (ctx.ReadOnly)
{
element.inputField.interactable = false;
}
int span = ctx.OverrideSpan ?? DefaultSpan;
float height = ctx.OverrideHeight ?? DefaultHeight;
element.SetLayoutSize(span * LayoutPacker.TotalRowWidth / LayoutPacker.MaxSpanPerRow, height);
return element;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 00072d6a1502bc74d8b7d116cc648bd1

View File

@@ -0,0 +1,35 @@
using Ichni.RhythmGame;
using Lean.Pool;
using UnityEngine;
namespace Ichni.Editor
{
/// <summary>
/// 参数文本 Drawer绑定字段并显示只读文本支持自动更新。
/// 创建 DynamicUIParameterText。
/// </summary>
public class ParameterTextDrawer : IPropertyDrawer
{
public int DefaultSpan => 1;
public float DefaultHeight => LayoutPacker.DefaultRowHeight;
public DynamicUIElement Draw(DrawContext ctx)
{
var go = LeanPool.Spawn(ctx.Registry.GetPrefab("parameterText"), ctx.Parent);
var element = go.GetComponent<DynamicUIParameterText>();
element.Initialize(ctx.Target, ctx.Label, ctx.FieldName);
if (ctx.AutoUpdate)
{
element.SetAutoUpdate(true);
}
int span = ctx.OverrideSpan ?? DefaultSpan;
float height = ctx.OverrideHeight ?? DefaultHeight;
element.SetLayoutSize(span * LayoutPacker.TotalRowWidth / LayoutPacker.MaxSpanPerRow, height);
return element;
}
}
}

View File

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

View File

@@ -0,0 +1,33 @@
using Ichni.RhythmGame;
using Lean.Pool;
using UnityEngine;
using UnityEngine.UI;
namespace Ichni.Editor
{
/// <summary>
/// 处理带 [InspectorSlider] 属性的 float/int 类型的 Drawer。
/// 创建 DynamicUISlider 并配置范围参数。
/// </summary>
public class SliderDrawer : IPropertyDrawer
{
public int DefaultSpan => 3;
public float DefaultHeight => LayoutPacker.DefaultRowHeight;
public DynamicUIElement Draw(DrawContext ctx)
{
var go = LeanPool.Spawn(ctx.Registry.GetPrefab("slider"), ctx.Parent);
var element = go.GetComponent<DynamicUISlider>();
element.Initialize(ctx.Target, ctx.Label, ctx.FieldName,
ctx.SliderMin, ctx.SliderMax, ctx.SliderWholeNumbers);
DynamicUIElement.DisableNavigation(element.slider);
int span = ctx.OverrideSpan ?? DefaultSpan;
float height = ctx.OverrideHeight ?? DefaultHeight;
element.SetLayoutSize(span * LayoutPacker.TotalRowWidth / LayoutPacker.MaxSpanPerRow, height);
return element;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8740b15baf2fd0147a93e04b4fd41051

View File

@@ -0,0 +1,35 @@
using Ichni.RhythmGame;
using Lean.Pool;
using UnityEngine;
namespace Ichni.Editor
{
/// <summary>
/// 处理 List&lt;string&gt; 选择的 Drawer。
/// 创建 DynamicUIStringListDropdown 并配置选项列表。
/// </summary>
public class StringListDropdownDrawer : IPropertyDrawer
{
public int DefaultSpan => 1;
public float DefaultHeight => LayoutPacker.DefaultRowHeight;
public DynamicUIElement Draw(DrawContext ctx)
{
var go = LeanPool.Spawn(ctx.Registry.GetPrefab("stringListDropdown"), ctx.Parent);
var element = go.GetComponent<DynamicUIStringListDropdown>();
if (ctx.StringListOptions != null)
{
element.SetUpStringList(ctx.StringListOptions);
}
element.Initialize(ctx.Target, ctx.Label, ctx.FieldName);
int span = ctx.OverrideSpan ?? DefaultSpan;
float height = ctx.OverrideHeight ?? DefaultHeight;
element.SetLayoutSize(span * LayoutPacker.TotalRowWidth / LayoutPacker.MaxSpanPerRow, height);
return element;
}
}
}

View File

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

View File

@@ -0,0 +1,32 @@
using Ichni.RhythmGame;
using Lean.Pool;
using UnityEngine;
using UnityEngine.UI;
namespace Ichni.Editor
{
/// <summary>
/// 处理 bool 类型的 Drawer。
/// 创建 DynamicUIToggle。
/// </summary>
public class ToggleDrawer : IPropertyDrawer
{
public int DefaultSpan => 1;
public float DefaultHeight => LayoutPacker.DefaultRowHeight;
public DynamicUIElement Draw(DrawContext ctx)
{
var go = LeanPool.Spawn(ctx.Registry.GetPrefab("toggle"), ctx.Parent);
var element = go.GetComponent<DynamicUIToggle>();
element.Initialize(ctx.Target, ctx.Label, ctx.FieldName);
DynamicUIElement.DisableNavigation(element.toggle);
int span = ctx.OverrideSpan ?? DefaultSpan;
float height = ctx.OverrideHeight ?? DefaultHeight;
element.SetLayoutSize(span * LayoutPacker.TotalRowWidth / LayoutPacker.MaxSpanPerRow, height);
return element;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 86988bf7af4e8864094e9fc687e93d29

View File

@@ -0,0 +1,38 @@
using Ichni.RhythmGame;
using Lean.Pool;
using UnityEngine;
using UnityEngine.UI;
namespace Ichni.Editor
{
/// <summary>
/// 处理 Vector2 类型的 Drawer。
/// 创建 DynamicUIVector2InputField。
/// </summary>
public class Vector2FieldDrawer : IPropertyDrawer
{
public int DefaultSpan => 2;
public float DefaultHeight => LayoutPacker.DefaultRowHeight;
public DynamicUIElement Draw(DrawContext ctx)
{
var go = LeanPool.Spawn(ctx.Registry.GetPrefab("vector2InputField"), ctx.Parent);
var element = go.GetComponent<DynamicUIVector2InputField>();
element.Initialize(ctx.Target, ctx.Label, ctx.FieldName);
if (ctx.AutoUpdate)
{
element.SetAutoUpdate(true);
}
DynamicUIElement.DisableNavigation(element.inputFieldX, element.inputFieldY);
int span = ctx.OverrideSpan ?? DefaultSpan;
float height = ctx.OverrideHeight ?? DefaultHeight;
element.SetLayoutSize(span * LayoutPacker.TotalRowWidth / LayoutPacker.MaxSpanPerRow, height);
return element;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 77e741bd7c95a8d4690052986898525e

View File

@@ -0,0 +1,38 @@
using Ichni.RhythmGame;
using Lean.Pool;
using UnityEngine;
using UnityEngine.UI;
namespace Ichni.Editor
{
/// <summary>
/// 处理 Vector3 类型的 Drawer。
/// 创建 DynamicUIVector3InputField。
/// </summary>
public class Vector3FieldDrawer : IPropertyDrawer
{
public int DefaultSpan => 3;
public float DefaultHeight => LayoutPacker.DefaultRowHeight;
public DynamicUIElement Draw(DrawContext ctx)
{
var go = LeanPool.Spawn(ctx.Registry.GetPrefab("vector3InputField"), ctx.Parent);
var element = go.GetComponent<DynamicUIVector3InputField>();
element.Initialize(ctx.Target, ctx.Label, ctx.FieldName);
if (ctx.AutoUpdate)
{
element.SetAutoUpdate(true);
}
DynamicUIElement.DisableNavigation(element.inputFieldX, element.inputFieldY, element.inputFieldZ);
int span = ctx.OverrideSpan ?? DefaultSpan;
float height = ctx.OverrideHeight ?? DefaultHeight;
element.SetLayoutSize(span * LayoutPacker.TotalRowWidth / LayoutPacker.MaxSpanPerRow, height);
return element;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 09da90190e75fd14886f20c784b4bc6f

View File

@@ -74,7 +74,6 @@ namespace Ichni.Editor
float.Parse(inputFieldBaseB.text), float.Parse(inputFieldBaseA.text));
Ichni.Editor.Commands.CommandManager.ExecuteCommand(new Ichni.Editor.Commands.ChangeValueCommand(connectedBaseElement, parameterName, newValue));
colorPreview.color = newValue;
connectedBaseElement.Refresh();
// 同步到HSV轮盘
if (hsvDrawer != null)
{

View File

@@ -1,80 +1,123 @@
using System.Collections;
using System.Collections.Generic;
using DG.Tweening;
using Ichni.RhythmGame;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace Ichni.Editor
{
public abstract class DynamicUIElement : MonoBehaviour
{
Inspector Inspector => EditorManager.instance.uiManager.inspector;
public TMP_Text title;
public CanvasGroup canvasGroup;
public IBaseElement connectedBaseElement;
/// <summary>
/// 参数名,通过反射获取饿修改对应变量的值
/// </summary>
public string parameterName;
public virtual void Initialize(IBaseElement baseElement, string title, string parameterName)
{
if (canvasGroup == null) canvasGroup = gameObject.AddComponent<CanvasGroup>();
this.connectedBaseElement = baseElement;
this.parameterName = parameterName;
if (title != string.Empty)
{
this.title.text = title;
}
else
{
this.title.gameObject.SetActive(false);
}
}
public DynamicUIElement Mark(string mark = "Default", IHaveInspection inspection = null)
{
inspection ??= Inspector;
if (mark == "Default")
{
mark = title.text;
}
inspection.MarkedElements.TryAdd(mark, this);
return this;
}
public abstract DynamicUIElement AddListenerFunction(UnityAction action);
}
public interface IHaveAutoUpdate
{
public bool isAutoUpdate { get; set; }
public bool isReceiving { get; set; }
public void SetAutoUpdate(bool enable);
public void UpdateContent()
{
if (isAutoUpdate && isReceiving)
{
ApplyContent();
}
}
public void ApplyContent();
}
public interface IHaveTagLink
{
public IBaseElement connectedBaseElement { get; set; }
// public void GetApply()
// {
// EditorManager.instance.projectInformation.tagManager.SyncTagedElement(connectedBaseElement);
// }
}
}
using System.Collections;
using System.Collections.Generic;
using DG.Tweening;
using Ichni.RhythmGame;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace Ichni.Editor
{
public abstract class DynamicUIElement : MonoBehaviour
{
Inspector Inspector => EditorManager.instance.uiManager.inspector;
public TMP_Text title;
public CanvasGroup canvasGroup;
public IBaseElement connectedBaseElement;
/// <summary>
/// 参数名,通过反射获取饿修改对应变量的值
/// </summary>
public string parameterName;
/// <summary>
/// 缓存的 LayoutElement用于 InspectorBuilder 的 Flexible 布局模式。
/// </summary>
private LayoutElement _layoutElement;
public virtual void Initialize(IBaseElement baseElement, string title, string parameterName)
{
if (canvasGroup == null) canvasGroup = gameObject.AddComponent<CanvasGroup>();
this.connectedBaseElement = baseElement;
this.parameterName = parameterName;
if (title != string.Empty)
{
this.title.text = title;
}
else
{
this.title.gameObject.SetActive(false);
}
}
public DynamicUIElement Mark(string mark = "Default", IHaveInspection inspection = null)
{
inspection ??= Inspector;
if (mark == "Default")
{
mark = title.text;
}
inspection.MarkedElements.TryAdd(mark, this);
return this;
}
/// <summary>
/// 设置此元素在 Flexible 布局中的尺寸。
/// 由 InspectorBuilder 或 Drawer 在元素创建后调用。
/// </summary>
public void SetLayoutSize(float preferredWidth, float preferredHeight)
{
if (_layoutElement == null)
_layoutElement = GetComponent<LayoutElement>();
if (_layoutElement == null)
_layoutElement = gameObject.AddComponent<LayoutElement>();
_layoutElement.preferredWidth = preferredWidth;
_layoutElement.preferredHeight = preferredHeight;
}
/// <summary>
/// 禁用指定 Selectable 组件的导航(统一处理,避免各 Generate 方法重复编写)。
/// </summary>
public static void DisableNavigation(Selectable selectable)
{
if (selectable != null)
selectable.navigation = new Navigation { mode = Navigation.Mode.None };
}
/// <summary>
/// 批量禁用多个 Selectable 的导航。
/// </summary>
public static void DisableNavigation(params Selectable[] selectables)
{
var nav = new Navigation { mode = Navigation.Mode.None };
foreach (var s in selectables)
{
if (s != null)
s.navigation = nav;
}
}
public abstract DynamicUIElement AddListenerFunction(UnityAction action);
}
public interface IHaveAutoUpdate
{
public bool isAutoUpdate { get; set; }
public bool isReceiving { get; set; }
public void SetAutoUpdate(bool enable);
public void UpdateContent()
{
if (isAutoUpdate && isReceiving)
{
ApplyContent();
}
}
public void ApplyContent();
}
public interface IHaveTagLink
{
public IBaseElement connectedBaseElement { get; set; }
// public void GetApply()
// {
// EditorManager.instance.projectInformation.tagManager.SyncTagedElement(connectedBaseElement);
// }
}
}

View File

@@ -97,7 +97,6 @@ namespace Ichni.Editor
Ichni.Editor.Commands.CommandManager.ExecuteCommand(new Ichni.Editor.Commands.ChangeValueCommand(connectedBaseElement, colorParameterName, emissionColor));
colorPreview.color = emissionColor;
connectedBaseElement.Refresh();
}
public void SliderChange(float value)

View File

@@ -49,7 +49,6 @@ namespace Ichni.Editor
private void ApplyParameters(int value)
{
Ichni.Editor.Commands.CommandManager.ExecuteCommand(new Ichni.Editor.Commands.ChangeValueCommand(connectedBaseElement, parameterName, value));
connectedBaseElement.Refresh();
}
public override DynamicUIElement AddListenerFunction(UnityAction action)

View File

@@ -113,7 +113,6 @@ namespace Ichni.Editor
{
Ichni.Editor.Commands.CommandManager.ExecuteCommand(new Ichni.Editor.Commands.ChangeValueCommand(connectedBaseElement, parameterName, value));
}
connectedBaseElement.Refresh();
}
public override DynamicUIElement AddListenerFunction(UnityAction action)

View File

@@ -156,7 +156,6 @@ namespace Ichni.Editor
}
Ichni.Editor.Commands.CommandManager.ExecuteCommand(
new Ichni.Editor.Commands.ChangeValueCommand(connectedBaseElement, parameterName, convertedValue));
connectedBaseElement.Refresh();
}
#endregion

View File

@@ -76,7 +76,6 @@ namespace Ichni.Editor
private void ApplyParameters(string value)
{
Ichni.Editor.Commands.CommandManager.ExecuteCommand(new Ichni.Editor.Commands.ChangeValueCommand(connectedBaseElement, parameterName, value));
connectedBaseElement.Refresh();
}
public override DynamicUIElement AddListenerFunction(UnityAction action)

View File

@@ -53,7 +53,6 @@ namespace Ichni.Editor
private void ApplyParameters(bool value)
{
Ichni.Editor.Commands.CommandManager.ExecuteCommand(new Ichni.Editor.Commands.ChangeValueCommand(connectedBaseElement, parameterName, value));
connectedBaseElement.Refresh();
}
public override DynamicUIElement AddListenerFunction(UnityAction action)

View File

@@ -116,7 +116,6 @@ namespace Ichni.Editor
{
Vector2 newValue = new Vector2(float.Parse(inputFieldX.text), float.Parse(inputFieldY.text));
Ichni.Editor.Commands.CommandManager.ExecuteCommand(new Ichni.Editor.Commands.ChangeValueCommand(connectedBaseElement, parameterName, newValue));
connectedBaseElement.Refresh();
}
public override DynamicUIElement AddListenerFunction(UnityAction action)

View File

@@ -135,7 +135,6 @@ namespace Ichni.Editor
{
Vector3 newValue = new Vector3(float.Parse(inputFieldX.text), float.Parse(inputFieldY.text), float.Parse(inputFieldZ.text));
Ichni.Editor.Commands.CommandManager.ExecuteCommand(new Ichni.Editor.Commands.ChangeValueCommand(connectedBaseElement, parameterName, newValue));
connectedBaseElement.Refresh();
}
public override DynamicUIElement AddListenerFunction(UnityAction action)

View File

@@ -1,4 +1,3 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
@@ -9,21 +8,64 @@ namespace Ichni.Editor
{
Inspector Inspector => EditorManager.instance.uiManager.inspector;
public DynamicUIContainer parentContainer;
public GridLayoutGroup gridLayoutGroup;
public RectTransform rect;
public List<DynamicUIElement> dynamicUIElements;
/// <summary> 当前行高 </summary>
[HideInInspector]
public float rowHeight = 100f;
/// <summary>
/// 使用 GridLayoutGroup 实现 N 列自动换行布局。
/// 当添加的元素数量超过 elementCountPerRow 时,会自动换行到下一行。
/// </summary>
public void Initialize(DynamicUIContainer parentContainer, int elementCountPerRow, float height = 100)
{
this.parentContainer = parentContainer;
this.gridLayoutGroup.cellSize = new Vector2(600f / elementCountPerRow, height);
// 支持对象池复用:避免每次 new List 产生 GC直接清空
if (this.dynamicUIElements == null)
this.dynamicUIElements = new List<DynamicUIElement>();
this.rowHeight = height;
int columns = Mathf.Max(1, elementCountPerRow);
// ── 启用 GridLayoutGroup ──
var grid = GetComponent<GridLayoutGroup>();
if (grid == null)
grid = gameObject.AddComponent<GridLayoutGroup>();
grid.enabled = true;
grid.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
grid.constraintCount = columns;
grid.childAlignment = TextAnchor.UpperLeft;
grid.startCorner = GridLayoutGroup.Corner.UpperLeft;
grid.startAxis = GridLayoutGroup.Axis.Horizontal;
grid.padding = new RectOffset(10, 10, 0, 0);
float spacing = 10f;
float totalSpacing = (columns - 1) * spacing;
float cellWidth = (LayoutPacker.TotalRowWidth - totalSpacing) / columns;
grid.cellSize = new Vector2(cellWidth, height);
grid.spacing = new Vector2(spacing, spacing);
// ── LayoutElement: preferredHeight = -1 让 GridLayoutGroup 的 ILayoutElement 接管高度 ──
var layoutElement = GetComponent<LayoutElement>();
if (layoutElement == null)
layoutElement = gameObject.AddComponent<LayoutElement>();
layoutElement.preferredHeight = -1;
layoutElement.flexibleHeight = -1;
// ── ContentSizeFitter: 让子容器高度随网格行数动态增长 ──
var fitter = GetComponent<ContentSizeFitter>();
if (fitter == null)
fitter = gameObject.AddComponent<ContentSizeFitter>();
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
fitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
fitter.enabled = true;
if (dynamicUIElements == null)
dynamicUIElements = new List<DynamicUIElement>();
else
this.dynamicUIElements.Clear();
dynamicUIElements.Clear();
}
public DynamicUISubcontainer Mark(string mark, IHaveInspection inspection = null)
{
inspection ??= Inspector;
@@ -31,4 +73,4 @@ namespace Ichni.Editor
return this;
}
}
}
}

View File

@@ -1,98 +0,0 @@
using System;
namespace Ichni.Editor
{
/// <summary>
/// 数值或枚举控制参数标签,挂载在 Field 或 Property 上
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class DynamicUIAttribute : Attribute
{
public string Name { get; set; }
/// <summary>
/// 属于哪个 Container (比如 "InGame Settings" 或 "Path Node")
/// </summary>
public string Group { get; set; }
/// <summary>
/// 排版格子要求。一行最大宽度分为 3 份。
/// 填 1 代表需占 1/3。填 3 代表全宽占满整行。
/// 若填 0由内置工厂根据数据类型自动识别例如 Vector3 与 Color 自动为 3bool为1 等)。
/// </summary>
public int Span { get; set; }
/// <summary>
/// 垂直占位高度。填 0 由系统智能推断(如基础件 100ColorPicker 240也可显式填写如 150f 覆盖。
/// </summary>
public float Height { get; set; }
public bool AutoUpdate { get; set; }
public bool ReadOnly { get; set; }
public DynamicUIAttribute(string name = "", string group = "Default", int span = 0, float height = 0f, bool autoUpdate = false, bool readOnly = false)
{
Name = name;
Group = group;
Span = span;
Height = height;
AutoUpdate = autoUpdate;
ReadOnly = readOnly;
}
}
/// <summary>
/// 支持 HDR 以及独立 Emission 强度或开关控制通道的拾色器。
/// 若无独立通道字段可置为 "NULL",将自动转为读取并写入至 Target Color 的 Alpha 通道中。
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class DynamicUIEmissionColorAttribute : DynamicUIAttribute
{
public string EmissionEnabledName { get; set; }
public string EmissionIntensityName { get; set; }
public DynamicUIEmissionColorAttribute(string name, string enabledName = "NULL", string intensityName = "NULL", string group = "Default", int span = 3, float height = 0f)
: base(name, group, span, height)
{
EmissionEnabledName = enabledName;
EmissionIntensityName = intensityName;
}
}
/// <summary>
/// 滑块控制属性,适用于 float/int 类型字段,自动生成带范围限制的 Slider 替代 InputField。
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class DynamicUISliderAttribute : DynamicUIAttribute
{
public float Min { get; set; }
public float Max { get; set; }
public bool WholeNumbers { get; set; }
public DynamicUISliderAttribute(string name = "", string group = "Default",
float min = 0f, float max = 1f, bool wholeNumbers = false,
int span = 0, float height = 0f, bool autoUpdate = false, bool readOnly = false)
: base(name, group, span, height, autoUpdate, readOnly)
{
Min = min;
Max = max;
WholeNumbers = wholeNumbers;
}
}
/// <summary>
/// 操作按钮卡片,挂载在没有任何参数的 Method() 方法上
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class DynamicUIButtonAttribute : Attribute
{
public string Name { get; set; }
public string Group { get; set; }
public DynamicUIButtonAttribute(string name = "", string group = "Default")
{
Name = name;
Group = group;
}
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 8a540054c95f48544bd6efcea6b452cc

View File

@@ -1,228 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using Ichni.RhythmGame;
using UnityEngine.Events;
namespace Ichni.Editor
{
public static class DynamicUIAutoBuilder
{
// 反射信息快取
private class FieldDef
{
public MemberInfo Member;
public DynamicUIAttribute Attr;
public Type ValueType;
}
private class MethodDef
{
public MethodInfo Method;
public DynamicUIButtonAttribute Attr;
}
/// <summary>
/// 接管任何 IBaseElement 的 UI 生成作业
/// </summary>
public static void AutoBuild(IBaseElement element, IHaveInspection inspector)
{
Type t = element.GetType();
// 扫描所有带 DynamicUI 标记的字段或属性
var dynamicFields = new List<FieldDef>();
foreach (var member in t.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
var uiAttr = member.GetCustomAttribute<DynamicUIAttribute>();
if (uiAttr != null)
{
Type vType = member is FieldInfo f ? f.FieldType : (member as PropertyInfo)?.PropertyType;
if (vType != null) dynamicFields.Add(new FieldDef { Member = member, Attr = uiAttr, ValueType = vType });
}
}
// 扫描所有带 DynamicUIButton 的方法
var dynamicMethods = new List<MethodDef>();
foreach (var method in t.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
var btnAttr = method.GetCustomAttribute<DynamicUIButtonAttribute>();
if (btnAttr != null && method.GetParameters().Length == 0)
{
dynamicMethods.Add(new MethodDef { Method = method, Attr = btnAttr });
}
}
// 按照 Group 归类准备流式渲染
var allGroups = dynamicFields.Select(f => f.Attr.Group)
.Concat(dynamicMethods.Select(m => m.Attr.Group))
.Distinct().ToList();
foreach (var groupName in allGroups)
{
var container = inspector.GenerateContainer(groupName);
var fieldsInGroup = dynamicFields.Where(f => f.Attr.Group == groupName).ToList();
var methodsInGroup = dynamicMethods.Where(m => m.Attr.Group == groupName).ToList();
// ======== 智能栅格打包推演 ========
List<FieldDef> currentLineBuffer = new List<FieldDef>();
int currentSpanCount = 0; // 一行容量最高是 3
// 先排变量
foreach (var field in fieldsInGroup)
{
int span = GetEffectiveSpan(field);
span = Mathf.Clamp(span, 1, 3);
// 如果该行的槽被塞满了或者装不下了
if (currentSpanCount > 0 && currentSpanCount + span > 3)
{
FlushLine(element, inspector, container, currentLineBuffer, 3);
currentLineBuffer.Clear();
currentSpanCount = 0;
}
// 特殊兼容:由于 GridLayoutGroup 的强制统一 cellSize, 我们如果在同一行混搭不同 Span 宽度的,反而会让它变形。
// 因此若当前这行的空间要求和前一个需求跨度不一致,也必须强行断点分层!
if (currentLineBuffer.Count > 0)
{
var lastSpan = GetEffectiveSpan(currentLineBuffer[0]);
if (span != lastSpan)
{
FlushLine(element, inspector, container, currentLineBuffer, GetHighestSpanRequirement(currentLineBuffer));
currentLineBuffer.Clear();
currentSpanCount = 0;
}
}
currentLineBuffer.Add(field);
currentSpanCount += span;
}
if (currentLineBuffer.Count > 0) FlushLine(element, inspector, container, currentLineBuffer, GetHighestSpanRequirement(currentLineBuffer));
// ======== 最后把按钮并排排好 ========
if (methodsInGroup.Count > 0)
{
var btnSubcontainer = container.GenerateSubcontainer(3); // 按钮统统放在 1/3 的槽里(你可改规则)
foreach (var method in methodsInGroup)
{
MethodInfo mInfo = method.Method;
// 通过强行抓取生成 UnityAction 给工厂
UnityAction action = () => { mInfo.Invoke(element, null); element.Refresh(); };
string n = string.IsNullOrEmpty(method.Attr.Name) ? mInfo.Name : method.Attr.Name;
inspector.GenerateButton(element, btnSubcontainer, n, action);
}
}
}
}
// 核心工厂发射器
private static void FlushLine(IBaseElement element, IHaveInspection inspector, DynamicUIContainer container, List<FieldDef> fields, int lineSpanCapacity)
{
// 如果一行要放 3 份Span 都是 1 拼成的),那格子数量也是 3600/3=200一个
// 但是如果是独占的Span 都是 3格子数量反而应该是 1600/1=600一个
// 为了防止排版错开变形,我们通过 3 / span 来决定 Subcontainer 内部允许的等分元素数量。
int subcontainerDivision = Mathf.Max(1, Mathf.RoundToInt(3f / lineSpanCapacity));
float rowHeight = GetHighestHeightRequirement(fields);
var sub = container.GenerateSubcontainer(subcontainerDivision, rowHeight);
foreach (var fd in fields)
{
string title = string.IsNullOrEmpty(fd.Attr.Name) ? fd.Member.Name : fd.Attr.Name;
string pName = fd.Member.Name;
if (fd.Attr.ReadOnly)
{
var val = ReflectionHelper.GetDeepValue(element, pName);
inspector.GenerateHintText(element, sub, $"{title}: {val}");
continue;
}
// 智能类型分流
if (fd.Attr is DynamicUIEmissionColorAttribute emissionAttr)
{
// 使用之前为了消除扩展方法调不出来的尴尬而专门暴露的强转/普通调用,我们直接这里也安全使用
IHaveInspection rawInspector = inspector as IHaveInspection;
if(rawInspector != null) rawInspector.GenerateEmissionColorPicker(element, sub, title, emissionAttr.EmissionEnabledName, pName, emissionAttr.EmissionIntensityName);
}
else if (fd.ValueType == typeof(bool))
{
inspector.GenerateToggle(element, sub, title, pName);
}
else if (fd.ValueType == typeof(Vector2))
{
inspector.GenerateVector2InputField(element, sub, title, pName, fd.Attr.AutoUpdate);
}
else if (fd.ValueType == typeof(Vector3))
{
inspector.GenerateVector3InputField(element, sub, title, pName, fd.Attr.AutoUpdate);
}
else if (fd.ValueType == typeof(Color))
{
inspector.GenerateBaseColorPicker(element, sub, title, pName);
}
else if (fd.ValueType.IsEnum)
{
inspector.GenerateDropdown(element, sub, title, fd.ValueType, pName);
}
else if (fd.Attr is DynamicUISliderAttribute sliderAttr)
{
inspector.GenerateSlider(element, sub, title, pName, sliderAttr.Min, sliderAttr.Max, sliderAttr.WholeNumbers);
}
else // 容错给普通的 InputField
{
inspector.GenerateInputField(element, sub, title, pName, fd.Attr.AutoUpdate);
}
}
}
private static int SmartInferSpan(Type t)
{
if (t == typeof(bool)) return 1;
if (t == typeof(float) || t == typeof(int) || t == typeof(string)) return 1;
if (t.IsEnum) return 1;
if (t == typeof(Vector2)) return 2;
if (t == typeof(Vector3) || t == typeof(Color)) return 3; // 巨型组件通篇独占一行
return 1;
}
private static int GetHighestSpanRequirement(List<FieldDef> fields)
{
int highest = 1;
foreach (var f in fields)
{
int span = GetEffectiveSpan(f);
if (span > highest) highest = span;
}
return highest;
}
private static int GetEffectiveSpan(FieldDef fd)
{
return fd.Attr.Span > 0 ? fd.Attr.Span : SmartInferSpan(fd.ValueType);
}
private static float SmartInferHeight(FieldDef fd)
{
// EmissionColorPicker 有 Toggle/RGB滑块/Intensity输入框高度明显大于 BaseColorPicker
if (fd.Attr is DynamicUIEmissionColorAttribute) return 320f;
if (fd.ValueType == typeof(Color)) return 280f;
// 多数基础组件默认为 100
return 100f;
}
private static float GetHighestHeightRequirement(List<FieldDef> fields)
{
float highest = 100f;
foreach (var f in fields)
{
float height = f.Attr.Height > 0 ? f.Attr.Height : SmartInferHeight(f);
if (height > highest) highest = height;
}
return highest;
}
}
}

View File

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

View File

@@ -13,6 +13,11 @@ using Object = UnityEngine.Object;
namespace Ichni.Editor
{
/// <summary>
/// Inspector 工厂接口 —— 提供底层 UI 控件的实例化方法。
/// 外部代码应通过 <see cref="InspectorBuilder"/> 构建 Inspector
/// 而非直接调用本接口的 Generate 方法。
/// </summary>
public interface IHaveInspection
{
public RectTransform WindowRect { get; set; }
@@ -226,6 +231,7 @@ namespace Ichni.Editor
return hintText;
}
[Obsolete("GenerateParameterText 已无外部调用者,请使用 InspectorBuilder.HintText() 替代。")]
public DynamicUIParameterText GenerateParameterText(IBaseElement baseElement, DynamicUISubcontainer subcontainer,
string title, string parameterName, bool isAutoUpdate = false)
{

View File

@@ -22,6 +22,9 @@ namespace Ichni.Editor
public Dictionary<string, DynamicUISubcontainer> MarkedSubcontainers { get; set; }
public Dictionary<string, DynamicUIElement> MarkedElements { get; set; }
/// <summary> Drawer 注册表单例的便捷访问 </summary>
public DrawerRegistry DrawerRegistry => DrawerRegistry.Instance;
private void Awake()
{
WindowRect = inspectorRect;

View File

@@ -1,59 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Ichni.RhythmGame;
using UnityEngine;
namespace Ichni.Editor
{
public static class StandardInspectionElement
{
private static IHaveInspection inspector => EditorManager.instance.uiManager.inspector;
private static Inspector inspectorUI => EditorManager.instance.uiManager.inspector;
public static void GenerateForTransform(GameElement gameElement, DynamicUIContainer generateContainer = null)//关于有Transform
{
if (generateContainer is null)
{
generateContainer = inspector.GenerateContainer("Generate Elements");
}
var animationSubcontainer = generateContainer.GenerateSubcontainer(3);
var displacementButton = inspector.GenerateButton(gameElement, animationSubcontainer, "Displacement", () =>
{
Displacement.GenerateElement("New Displacement", Guid.NewGuid(), new List<string>(), true, gameElement,
new FlexibleFloat(true), new FlexibleFloat(true), new FlexibleFloat(true));
}); //位移
var swirlButton = inspector.GenerateButton(gameElement, animationSubcontainer, "Swirl", () =>
{
Swirl.GenerateElement("New Swirl", Guid.NewGuid(), new List<string>(), true, gameElement,
new FlexibleFloat(true), new FlexibleFloat(true), new FlexibleFloat(true));
}); //旋转
var scaleButton = inspector.GenerateButton(gameElement, animationSubcontainer, "Scale", () =>
{
Scale.GenerateElement("New Scale", Guid.NewGuid(), new List<string>(), true, gameElement,
new FlexibleFloat(true), new FlexibleFloat(true), new FlexibleFloat(true));
}); //缩放
var LookAtButton = inspector.GenerateButton(gameElement, animationSubcontainer, "Look At",
() => LookAt.GenerateElement("New Look At", Guid.NewGuid(),
new List<string>(), true, gameElement, null, new FlexibleBool()));
var displacementTrackerButton = inspector.GenerateButton(gameElement, animationSubcontainer, "Displacement Tracker", () =>
{
DisplacementTracker.GenerateElement("New Displacement Tracker", Guid.NewGuid(), new List<string>(), true, gameElement,
null, 0f);
});
var swirlTrackerButton = inspector.GenerateButton(gameElement, animationSubcontainer, "Swirl Tracker", () =>
{
SwirlTracker.GenerateElement("New Swirl Tracker", Guid.NewGuid(), new List<string>(), true, gameElement,
null, 0f);
}); var ScaleTrackerButton = inspector.GenerateButton(gameElement, animationSubcontainer, "Scale Tracker", () =>
{
ScaleTracker.GenerateElement("New Scale Tracker", Guid.NewGuid(), new List<string>(), true, gameElement,
null, 0f);
});
}
public static void GenerateForLoading()
{
inspectorUI.ClearInspector();
var container = inspector.GenerateContainer("Loading");
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 97779e95563721b4e8d4a28bb0d46cb9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -543,16 +543,14 @@ namespace ichni.RhythmGame // [修复] 修正命名空间首字母大写,符
{
base.SetUpInspector();
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Fast Note Tracker");
var sub = container.GenerateSubcontainer(2);
inspector.GenerateToggle(this, sub, "Enabled", nameof(IsEnabled));
// 修正变量名
inspector.GenerateInputField(this, sub, "Beat Diver", nameof(BeatDiver));
inspector.GenerateToggle(this, sub, "Force refresh (cost++)", nameof(ForceRefresh));
inspector.GenerateInputField(this, sub, "Horizon Width", nameof(horizonWidth));
inspector.GenerateToggle(this, sub, "Show Note Preview", nameof(showNotePreview));
InspectorBuilder.For(this)
.Section("Fast Note Tracker")
.Toggle(nameof(IsEnabled), "Enabled").Span(2)
.InputField(nameof(BeatDiver), "Beat Diver").Span(2)
.Toggle(nameof(ForceRefresh), "Force refresh (cost++)").Span(2)
.InputField(nameof(horizonWidth), "Horizon Width").Span(2)
.Toggle(nameof(showNotePreview), "Show Note Preview").Span(2)
.Build();
}
public static FastNoteTracker GenerateElement(string elementName, Guid id, List<string> tags,

View File

@@ -1,5 +1,4 @@
using Ichni.Editor;
using UnityEngine;
namespace Ichni.RhythmGame
{
@@ -8,13 +7,12 @@ namespace Ichni.RhythmGame
public override void SetUpInspector()
{
base.SetUpInspector();
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Camera Field of View");
var subcontainer = container.GenerateSubcontainer(3);
var fovButton = inspector.GenerateButton(this, subcontainer, "Field of View", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Field of View", nameof(fieldOfView)).SetAsFlexibleFloat();
});
IHaveInspection insp = EditorManager.instance.uiManager.inspector;
InspectorBuilder.For(this)
.Section("Camera Field of View")
.Button("Field of View", () => insp.GenerateCompositeParameterWindow(this, "Field of View", nameof(fieldOfView)).SetAsFlexibleFloat())
.Build();
}
}
}

View File

@@ -1,5 +1,4 @@
using Ichni.Editor;
using UnityEngine;
namespace Ichni.RhythmGame
{
@@ -7,31 +6,18 @@ namespace Ichni.RhythmGame
{
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
base.SetUpInspector();
var container = inspector.GenerateContainer("Base Color Change");
var subcontainer = container.GenerateSubcontainer(3);
var colorRButton = inspector.GenerateButton(this, subcontainer, "Color R", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Color R", nameof(colorR)).SetAsFlexibleFloat();
});
var colorGButton = inspector.GenerateButton(this, subcontainer, "Color G", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Color G", nameof(colorG)).SetAsFlexibleFloat();
});
var colorBButton = inspector.GenerateButton(this, subcontainer, "Color B", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Color B", nameof(colorB)).SetAsFlexibleFloat();
});
var colorAButton = inspector.GenerateButton(this, subcontainer, "Color A", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Color A", nameof(colorA)).SetAsFlexibleFloat();
});
var graphicEditor = inspector.GenerateButton(this, subcontainer, "GraphicEditor", () =>
{
inspector.GenerateGraphicalFlexibleFloatWindow(this, "Base Color",
new FlexibleFloat[] { colorR, colorG, colorB, colorA }, new string[] { "R", "G", "B", "A" });
});
IHaveInspection insp = EditorManager.instance.uiManager.inspector;
InspectorBuilder.For(this)
.Section("Base Color Change")
.Button("Color R", () => insp.GenerateCompositeParameterWindow(this, "Color R", nameof(colorR)).SetAsFlexibleFloat())
.Button("Color G", () => insp.GenerateCompositeParameterWindow(this, "Color G", nameof(colorG)).SetAsFlexibleFloat())
.Button("Color B", () => insp.GenerateCompositeParameterWindow(this, "Color B", nameof(colorB)).SetAsFlexibleFloat())
.Button("Color A", () => insp.GenerateCompositeParameterWindow(this, "Color A", nameof(colorA)).SetAsFlexibleFloat())
.Button("GraphicEditor", () => insp.GenerateGraphicalFlexibleFloatWindow(this, "Base Color",
new FlexibleFloat[] { colorR, colorG, colorB, colorA }, new string[] { "R", "G", "B", "A" }))
.Build();
}
}
}

View File

@@ -1,5 +1,4 @@
using Ichni.Editor;
using UnityEngine;
namespace Ichni.RhythmGame
{
@@ -7,31 +6,18 @@ namespace Ichni.RhythmGame
{
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
base.SetUpInspector();
var container = inspector.GenerateContainer("Emission Color Change");
var subcontainer = container.GenerateSubcontainer(3);
var colorRButton = inspector.GenerateButton(this, subcontainer, "Color R", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Color R", nameof(colorR)).SetAsFlexibleFloat();
});
var colorGButton = inspector.GenerateButton(this, subcontainer, "Color G", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Color G", nameof(colorG)).SetAsFlexibleFloat();
});
var colorBButton = inspector.GenerateButton(this, subcontainer, "Color B", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Color B", nameof(colorB)).SetAsFlexibleFloat();
});
var colorAButton = inspector.GenerateButton(this, subcontainer, "Color A", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Color I", nameof(colorI)).SetAsFlexibleFloat();
});
var graphicEditor = inspector.GenerateButton(this, subcontainer, "GraphicEditor", () =>
{
inspector.GenerateGraphicalFlexibleFloatWindow(this, "Emission Color",
new FlexibleFloat[] { colorR, colorG, colorB, colorI }, new string[] { "R", "G", "B", "I" });
});
IHaveInspection insp = EditorManager.instance.uiManager.inspector;
InspectorBuilder.For(this)
.Section("Emission Color Change")
.Button("Color R", () => insp.GenerateCompositeParameterWindow(this, "Color R", nameof(colorR)).SetAsFlexibleFloat())
.Button("Color G", () => insp.GenerateCompositeParameterWindow(this, "Color G", nameof(colorG)).SetAsFlexibleFloat())
.Button("Color B", () => insp.GenerateCompositeParameterWindow(this, "Color B", nameof(colorB)).SetAsFlexibleFloat())
.Button("Color I", () => insp.GenerateCompositeParameterWindow(this, "Color I", nameof(colorI)).SetAsFlexibleFloat())
.Button("GraphicEditor", () => insp.GenerateGraphicalFlexibleFloatWindow(this, "Emission Color",
new FlexibleFloat[] { colorR, colorG, colorB, colorI }, new string[] { "R", "G", "B", "I" }))
.Build();
}
}
}

View File

@@ -15,37 +15,23 @@ namespace Ichni.RhythmGame
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
base.SetUpInspector();
var container = inspector.GenerateContainer("Property Animation Color");
var settings = container.GenerateSubcontainer(3);
inspector.GenerateInputField(this, settings, "Component Name", nameof(componentName)).AddListenerFunction(() => AfterInitialize());
inspector.GenerateInputField(this, settings, "Property Name", nameof(propertyName)).AddListenerFunction(() => AfterInitialize());
var graphicEditor = inspector.GenerateButton(this, settings, "GraphicEditor", () =>
{
inspector.GenerateGraphicalFlexibleFloatWindow(this, "Property Color",
new FlexibleFloat[] { propertyValueR, propertyValueG, propertyValueB, propertyValueA }, new string[] { "R", "G", "B", "A" });
});
var colorRButton = inspector.GenerateButton(this, settings, "Color R", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Color R", nameof(propertyValueR)).SetAsFlexibleFloat();
});
var colorGButton = inspector.GenerateButton(this, settings, "Color G", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Color G", nameof(propertyValueG)).SetAsFlexibleFloat();
});
var colorBButton = inspector.GenerateButton(this, settings, "Color B", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Color B", nameof(propertyValueB)).SetAsFlexibleFloat();
});
var colorAButton = inspector.GenerateButton(this, settings, "Color A", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Color A", nameof(propertyValueA)).SetAsFlexibleFloat();
});
IHaveInspection insp = EditorManager.instance.uiManager.inspector;
InspectorBuilder.For(this)
.Section("Property Animation Color")
.InputField(nameof(componentName), "Component Name")
.OnChanged(() => AfterInitialize())
.InputField(nameof(propertyName), "Property Name")
.OnChanged(() => AfterInitialize())
.Button("GraphicEditor", () => insp.GenerateGraphicalFlexibleFloatWindow(this, "Property Color",
new FlexibleFloat[] { propertyValueR, propertyValueG, propertyValueB, propertyValueA },
new string[] { "R", "G", "B", "A" }))
.Button("Color R", () => insp.GenerateCompositeParameterWindow(this, "Color R", nameof(propertyValueR)).SetAsFlexibleFloat())
.Button("Color G", () => insp.GenerateCompositeParameterWindow(this, "Color G", nameof(propertyValueG)).SetAsFlexibleFloat())
.Button("Color B", () => insp.GenerateCompositeParameterWindow(this, "Color B", nameof(propertyValueB)).SetAsFlexibleFloat())
.Button("Color A", () => insp.GenerateCompositeParameterWindow(this, "Color A", nameof(propertyValueA)).SetAsFlexibleFloat())
.Build();
}
}
}

View File

@@ -14,25 +14,19 @@ namespace Ichni.RhythmGame
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
base.SetUpInspector();
var container = inspector.GenerateContainer("Property Animation Float");
var settings = container.GenerateSubcontainer(3);
inspector.GenerateInputField(this, settings, "Component Name", nameof(componentName)).AddListenerFunction(() => AfterInitialize());
inspector.GenerateInputField(this, settings, "Property Name", nameof(propertyName)).AddListenerFunction(() => AfterInitialize());
var graphicEditor = inspector.GenerateButton(this, settings, "GraphicEditor", () =>
{
inspector.GenerateGraphicalFlexibleFloatWindow(this, "Property Float",
new FlexibleFloat[] { propertyValue }, new string[] { "Value" });
});
var floatButton = inspector.GenerateButton(this, settings, "Float Value", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Float Value", nameof(propertyValue)).SetAsFlexibleFloat();
});
IHaveInspection insp = EditorManager.instance.uiManager.inspector;
InspectorBuilder.For(this)
.Section("Property Animation Float")
.InputField(nameof(componentName), "Component Name")
.OnChanged(() => AfterInitialize())
.InputField(nameof(propertyName), "Property Name")
.OnChanged(() => AfterInitialize())
.Button("GraphicEditor", () => insp.GenerateGraphicalFlexibleFloatWindow(this, "Property Float",
new FlexibleFloat[] { propertyValue }, new string[] { "Value" }))
.Button("Float Value", () => insp.GenerateCompositeParameterWindow(this, "Float Value", nameof(propertyValue)).SetAsFlexibleFloat())
.Build();
}
}
}

View File

@@ -14,33 +14,21 @@ namespace Ichni.RhythmGame
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
base.SetUpInspector();
var container = inspector.GenerateContainer("Property Animation Vector3");
var settings = container.GenerateSubcontainer(3);
inspector.GenerateInputField(this, settings, "Component Name", nameof(componentName)).AddListenerFunction(() => AfterInitialize());
inspector.GenerateInputField(this, settings, "Property Name", nameof(propertyName)).AddListenerFunction(() => AfterInitialize());
var graphicEditor = inspector.GenerateButton(this, settings, "GraphicEditor", () =>
{
inspector.GenerateGraphicalFlexibleFloatWindow(this, "Property Vector3",
new FlexibleFloat[] { propertyValueX, propertyValueY, propertyValueZ }, new string[] { "X", "Y", "Z" });
});
var xButton = inspector.GenerateButton(this, settings, "Vector X", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Vector X", nameof(propertyValueX)).SetAsFlexibleFloat();
});
var yButton = inspector.GenerateButton(this, settings, "Vector Y", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Vector Y", nameof(propertyValueY)).SetAsFlexibleFloat();
});
var zButton = inspector.GenerateButton(this, settings, "Vector Z", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Vector Z", nameof(propertyValueZ)).SetAsFlexibleFloat();
});
IHaveInspection insp = EditorManager.instance.uiManager.inspector;
InspectorBuilder.For(this)
.Section("Property Animation Vector3")
.InputField(nameof(componentName), "Component Name")
.OnChanged(() => AfterInitialize())
.InputField(nameof(propertyName), "Property Name")
.OnChanged(() => AfterInitialize())
.Button("GraphicEditor", () => insp.GenerateGraphicalFlexibleFloatWindow(this, "Property Vector3",
new FlexibleFloat[] { propertyValueX, propertyValueY, propertyValueZ }, new string[] { "X", "Y", "Z" }))
.Button("Vector X", () => insp.GenerateCompositeParameterWindow(this, "Vector X", nameof(propertyValueX)).SetAsFlexibleFloat())
.Button("Vector Y", () => insp.GenerateCompositeParameterWindow(this, "Vector Y", nameof(propertyValueY)).SetAsFlexibleFloat())
.Button("Vector Z", () => insp.GenerateCompositeParameterWindow(this, "Vector Z", nameof(propertyValueZ)).SetAsFlexibleFloat())
.Build();
}
}
}

View File

@@ -11,30 +11,36 @@ namespace Ichni.RhythmGame
public override void SetUpInspector()
{
base.SetUpInspector();
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
Inspector inspectorMain = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Displacement Tracker");
var effectSettings = container.GenerateSubcontainer(2);
var connectedGameElementInputField = inspector.GenerateInputField(effectSettings, "Try Get Displacement");
var connectGameElementButton = inspector.GenerateButton(this, effectSettings, "Connect Displacement", () =>
{
ICanBeTrackedDisplacement targetElement = EditorManager.instance.beatmapContainer.gameElementList.OfType<ICanBeTrackedDisplacement>()
.First(e => ((GameElement)e).elementName == connectedGameElementInputField.GetValue<string>());
if (targetElement == null)
{
LogWindow.Log("Game Element not found.", Color.yellow);
}
SetTargetDisplacement(targetElement);
inspectorMain.SetInspector(this);
});
string ShowConnection() => targetDisplacement == null ? "No Displacement Connected" : "Connected With: " + ((GameElement)targetDisplacement).elementName;
var connectHintText = inspector.GenerateHintText(this, effectSettings, ShowConnection);
var InputField = inspector.GenerateInputField(this, effectSettings, "Offset", nameof(TimeOffset));
var interferometerButton = inspector.GenerateButton(this, effectSettings, "Interferometer", () =>
{
Vector3Interferometer.GenerateElement("New Vector3 Interferometer", Guid.NewGuid(), new List<string>(), true,
this, InterferomType.Additive, Vector3.zero);
});
var nameRef = new ElementRef<DynamicUIInputField>();
InspectorBuilder.For(this)
.Section("Displacement Tracker")
.UnboundInputField("Try Get Displacement").WithRef(nameRef)
.Button("Connect Displacement", () =>
{
ICanBeTrackedDisplacement found = EditorManager.instance.beatmapContainer.gameElementList
.OfType<ICanBeTrackedDisplacement>()
.FirstOrDefault(e => ((GameElement)e).elementName == nameRef.Value?.GetValue<string>());
if (found == null)
{
LogWindow.Log("Game Element not found.", Color.yellow);
return;
}
SetTargetDisplacement(found);
inspectorMain.SetInspector(this);
})
.HintText(() => targetDisplacement == null
? "No Displacement Connected"
: "Connected With: " + ((GameElement)targetDisplacement).elementName)
.InputField(nameof(TimeOffset), "Offset")
.Button("Interferometer", () =>
{
Vector3Interferometer.GenerateElement("New Vector3 Interferometer", Guid.NewGuid(),
new List<string>(), true, this, InterferomType.Additive, Vector3.zero);
})
.Build();
}
}
}

View File

@@ -11,31 +11,36 @@ namespace Ichni.RhythmGame
public override void SetUpInspector()
{
base.SetUpInspector();
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
Inspector inspectorMain = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Scale Tracker");
var effectSettings = container.GenerateSubcontainer(2);
var connectedGameElementInputField = inspector.GenerateInputField(effectSettings, "Try Get Scale");
var connectGameElementButton = inspector.GenerateButton(this, effectSettings, "Connect Scale", () =>
{
ICanBeTrackedScale targetElement = EditorManager.instance.beatmapContainer.gameElementList.OfType<ICanBeTrackedScale>()
.First(e => ((GameElement)e).elementName == connectedGameElementInputField.GetValue<string>());
if (targetElement == null)
{
LogWindow.Log("Game Element not found.", Color.yellow);
}
targetScale = targetElement;
inspectorMain.SetInspector(this);
});
string ShowConnection() => targetScale == null ? "No Scale Connected" : "Connected With: " + ((GameElement)targetScale).elementName;
var connectHintText = inspector.GenerateHintText(this, effectSettings, ShowConnection);
var InputField = inspector.GenerateInputField(this, effectSettings, "Offset", nameof(TimeOffset));
var interferometerButton = inspector.GenerateButton(this, effectSettings, "Interferometer", () =>
{
Vector3Interferometer.GenerateElement("New Vector3 Interferometer", Guid.NewGuid(), new List<string>(), true,
this, InterferomType.Additive, Vector3.zero);
});
var nameRef = new ElementRef<DynamicUIInputField>();
InspectorBuilder.For(this)
.Section("Scale Tracker")
.UnboundInputField("Try Get Scale").WithRef(nameRef)
.Button("Connect Scale", () =>
{
ICanBeTrackedScale found = EditorManager.instance.beatmapContainer.gameElementList
.OfType<ICanBeTrackedScale>()
.FirstOrDefault(e => ((GameElement)e).elementName == nameRef.Value?.GetValue<string>());
if (found == null)
{
LogWindow.Log("Game Element not found.", Color.yellow);
return;
}
targetScale = found;
inspectorMain.SetInspector(this);
})
.HintText(() => targetScale == null
? "No Scale Connected"
: "Connected With: " + ((GameElement)targetScale).elementName)
.InputField(nameof(TimeOffset), "Offset")
.Button("Interferometer", () =>
{
Vector3Interferometer.GenerateElement("New Vector3 Interferometer", Guid.NewGuid(),
new List<string>(), true, this, InterferomType.Additive, Vector3.zero);
})
.Build();
}
}
}

View File

@@ -11,31 +11,36 @@ namespace Ichni.RhythmGame
public override void SetUpInspector()
{
base.SetUpInspector();
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
Inspector inspectorMain = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Swirl Tracker");
var effectSettings = container.GenerateSubcontainer(2);
var connectedGameElementInputField = inspector.GenerateInputField(effectSettings, "Try Get Swirl");
var connectGameElementButton = inspector.GenerateButton(this, effectSettings, "Connect Swirl", () =>
{
ICanBeTrackedSwirl targetElement = EditorManager.instance.beatmapContainer.gameElementList.OfType<ICanBeTrackedSwirl>()
.First(e => ((GameElement)e).elementName == connectedGameElementInputField.GetValue<string>());
if (targetElement == null)
{
LogWindow.Log("Game Element not found.", Color.yellow);
}
targetSwirl = targetElement;
inspectorMain.SetInspector(this);
});
string ShowConnection() => targetSwirl == null ? "No Swirl Connected" : "Connected With: " + ((GameElement)targetSwirl).elementName;
var connectHintText = inspector.GenerateHintText(this, effectSettings, ShowConnection);
var InputField = inspector.GenerateInputField(this, effectSettings, "Offset", nameof(TimeOffset));
var interferometerButton = inspector.GenerateButton(this, effectSettings, "Interferometer", () =>
{
Vector3Interferometer.GenerateElement("New Vector3 Interferometer", Guid.NewGuid(), new List<string>(), true,
this, InterferomType.Additive, Vector3.zero);
});
var nameRef = new ElementRef<DynamicUIInputField>();
InspectorBuilder.For(this)
.Section("Swirl Tracker")
.UnboundInputField("Try Get Swirl").WithRef(nameRef)
.Button("Connect Swirl", () =>
{
ICanBeTrackedSwirl found = EditorManager.instance.beatmapContainer.gameElementList
.OfType<ICanBeTrackedSwirl>()
.FirstOrDefault(e => ((GameElement)e).elementName == nameRef.Value?.GetValue<string>());
if (found == null)
{
LogWindow.Log("Game Element not found.", Color.yellow);
return;
}
targetSwirl = found;
inspectorMain.SetInspector(this);
})
.HintText(() => targetSwirl == null
? "No Swirl Connected"
: "Connected With: " + ((GameElement)targetSwirl).elementName)
.InputField(nameof(TimeOffset), "Offset")
.Button("Interferometer", () =>
{
Vector3Interferometer.GenerateElement("New Vector3 Interferometer", Guid.NewGuid(),
new List<string>(), true, this, InterferomType.Additive, Vector3.zero);
})
.Build();
}
}
}

View File

@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using Ichni.Editor;
using Ichni.RhythmGame.Beatmap;
using UnityEngine;
namespace Ichni.RhythmGame
{
@@ -11,11 +7,12 @@ namespace Ichni.RhythmGame
public override void SetUpInspector()
{
base.SetUpInspector();
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Vector3 Interferometer");
var subcontainer = container.GenerateSubcontainer(1);
inspector.GenerateVector3InputField(this, subcontainer, "Interferom Value", nameof(InterferomValueVector3));
inspector.GenerateDropdown(this, subcontainer, "Interferom Type", typeof(InterferomType), nameof(InterferomType));
InspectorBuilder.For(this)
.Section("Vector3 Interferometer")
.Vector3Field(nameof(InterferomValueVector3), "Interferom Value")
.Dropdown<InterferomType>(nameof(InterferomType), "Interferom Type")
.Build();
}
}
}

View File

@@ -1,5 +1,4 @@
using Ichni.Editor;
using UnityEngine;
namespace Ichni.RhythmGame
{
@@ -7,32 +6,18 @@ namespace Ichni.RhythmGame
{
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
base.SetUpInspector();
var container = inspector.GenerateContainer("Track Global Color Change");
IHaveInspection insp = EditorManager.instance.uiManager.inspector;
var subcontainer = container.GenerateSubcontainer(3);
var colorRButton = inspector.GenerateButton(this, subcontainer, "Color R", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Color R", nameof(colorR)).SetAsFlexibleFloat();
});
var colorGButton = inspector.GenerateButton(this, subcontainer, "Color G", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Color G", nameof(colorG)).SetAsFlexibleFloat();
});
var colorBButton = inspector.GenerateButton(this, subcontainer, "Color B", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Color B", nameof(colorB)).SetAsFlexibleFloat();
});
var colorAButton = inspector.GenerateButton(this, subcontainer, "Color A", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Color A", nameof(colorA)).SetAsFlexibleFloat();
});
var graphicEditor = inspector.GenerateButton(this, subcontainer, "GraphicEditor", () =>
{
inspector.GenerateGraphicalFlexibleFloatWindow(this, "Track Global Color Change",
new FlexibleFloat[] { colorR, colorG, colorB, colorA }, new string[] { "ColorR", "ColorG", "ColorB", "ColorA" });
});
InspectorBuilder.For(this)
.Section("Track Global Color Change")
.Button("Color R", () => insp.GenerateCompositeParameterWindow(this, "Color R", nameof(colorR)).SetAsFlexibleFloat())
.Button("Color G", () => insp.GenerateCompositeParameterWindow(this, "Color G", nameof(colorG)).SetAsFlexibleFloat())
.Button("Color B", () => insp.GenerateCompositeParameterWindow(this, "Color B", nameof(colorB)).SetAsFlexibleFloat())
.Button("Color A", () => insp.GenerateCompositeParameterWindow(this, "Color A", nameof(colorA)).SetAsFlexibleFloat())
.Button("GraphicEditor", () => insp.GenerateGraphicalFlexibleFloatWindow(this, "Track Global Color Change",
new FlexibleFloat[] { colorR, colorG, colorB, colorA }, new string[] { "ColorR", "ColorG", "ColorB", "ColorA" }))
.Build();
}
}
}

View File

@@ -1,5 +1,4 @@
using Ichni.Editor;
using UnityEngine;
namespace Ichni.RhythmGame
{
@@ -7,14 +6,13 @@ namespace Ichni.RhythmGame
{
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
base.SetUpInspector();
var container = inspector.GenerateContainer("Track Total Time Change");
var subcontainer = container.GenerateSubcontainer(3);
var totalTimeButton = inspector.GenerateButton(this, subcontainer, "Total Time", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Total Time", nameof(totalTime)).SetAsFlexibleFloat();
});
IHaveInspection insp = EditorManager.instance.uiManager.inspector;
InspectorBuilder.For(this)
.Section("Track Total Time Change")
.Button("Total Time", () => insp.GenerateCompositeParameterWindow(this, "Total Time", nameof(totalTime)).SetAsFlexibleFloat())
.Build();
}
}
}

View File

@@ -9,35 +9,22 @@ namespace Ichni.RhythmGame
{
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
base.SetUpInspector();
var container = inspector.GenerateContainer("Displacement");
var subcontainer = container.GenerateSubcontainer(3);
var positionXButton = inspector.GenerateButton(this, subcontainer, "Position X", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Position X", nameof(positionX)).SetAsFlexibleFloat();
});
var positionYButton = inspector.GenerateButton(this, subcontainer, "Position Y", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Position Y", nameof(positionY)).SetAsFlexibleFloat();
});
var positionZButton = inspector.GenerateButton(this, subcontainer, "Position Z", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Position Z", nameof(positionZ)).SetAsFlexibleFloat();
});
var graphicEditor = inspector.GenerateButton(this, subcontainer, "GraphicEditor", () =>
{
inspector.GenerateGraphicalFlexibleFloatWindow(this, "Displacement",
new FlexibleFloat[] { positionX, positionY, positionZ }, new string[] { "PosX", "PosY", "PosZ" });
});
var subcontainer2 = container.GenerateSubcontainer(1);
var valuetext = inspector.GenerateParameterText(this, subcontainer2, "value:", nameof(PreviewValue), true);
IHaveInspection insp = EditorManager.instance.uiManager.inspector;
var interferometerButton = inspector.GenerateButton(this, subcontainer2, "Interferometer", () =>
{
Vector3Interferometer.GenerateElement("New Vector3 Interferometer", Guid.NewGuid(), new List<string>(), true,
this, InterferomType.Additive, Vector3.zero);
});
InspectorBuilder.For(this)
.Section("Displacement")
.Button("Position X", () => insp.GenerateCompositeParameterWindow(this, "Position X", nameof(positionX)).SetAsFlexibleFloat())
.Button("Position Y", () => insp.GenerateCompositeParameterWindow(this, "Position Y", nameof(positionY)).SetAsFlexibleFloat())
.Button("Position Z", () => insp.GenerateCompositeParameterWindow(this, "Position Z", nameof(positionZ)).SetAsFlexibleFloat())
.Button("GraphicEditor", () => insp.GenerateGraphicalFlexibleFloatWindow(this, "Displacement",
new FlexibleFloat[] { positionX, positionY, positionZ }, new string[] { "PosX", "PosY", "PosZ" }))
.ParameterText(nameof(PreviewValue), "value:", autoUpdate: true)
.Span(3)
.Button("Interferometer", () => Vector3Interferometer.GenerateElement("New Vector3 Interferometer",
Guid.NewGuid(), new List<string>(), true, this, InterferomType.Additive, Vector3.zero))
.Span(3)
.Build();
}
}
}

View File

@@ -8,32 +8,35 @@ namespace Ichni.RhythmGame
{
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
Inspector inspectorMain = EditorManager.instance.uiManager.inspector;
IHaveInspection inspection = inspectorMain;
var container = inspector.GenerateContainer("Look At");
var effectSettings = container.GenerateSubcontainer(2);
var connectedGameElementInputField = inspector.GenerateInputField(effectSettings, "Try Get Element");
var connectGameElementButton = inspector.GenerateButton(this, effectSettings, "Connect Game Element", () =>
{
GameElement targetElement = EditorManager.instance.beatmapContainer.gameElementList
.First(e => e.elementName == connectedGameElementInputField.GetValue<string>());
var nameRef = new ElementRef<DynamicUIInputField>();
if (targetElement == null)
{
LogWindow.Log("Game Element not found.", Color.yellow);
}
lookAtObject = targetElement;
inspectorMain.SetInspector(this);
});
string ShowConnection() => lookAtObject == null ? "No Game Element Connected" : "Connected With: " + lookAtObject.elementName;
var connectHintText = inspector.GenerateHintText(this, effectSettings, ShowConnection);
var enablingButton = inspector.GenerateButton(this, effectSettings, "Enabling", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Enabling", nameof(enabling)).SetAsFlexibleBool();
});
InspectorBuilder.For(this)
.Section("Look At")
.UnboundInputField("Try Get Element").WithRef(nameRef)
.Button("Connect Game Element", () =>
{
GameElement found = EditorManager.instance.beatmapContainer.gameElementList
.FirstOrDefault(e => e.elementName == nameRef.Value?.GetValue<string>());
if (found == null)
{
LogWindow.Log("Game Element not found.", Color.yellow);
return;
}
lookAtObject = found;
inspectorMain.SetInspector(this);
})
.HintText(() => lookAtObject == null
? "No Game Element Connected"
: "Connected With: " + lookAtObject.elementName)
.Button("Enabling", () =>
{
inspection.GenerateCompositeParameterWindow(this, "Enabling", nameof(enabling))
.SetAsFlexibleBool();
})
.Build();
}
}
}

View File

@@ -10,35 +10,22 @@ namespace Ichni.RhythmGame
public override void SetUpInspector()
{
base.SetUpInspector();
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Scale");
var subcontainer = container.GenerateSubcontainer(3);
var scaleXButton = inspector.GenerateButton(this, subcontainer, "Scale X", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Scale X", nameof(scaleX)).SetAsFlexibleFloat();
});
var scaleYButton = inspector.GenerateButton(this, subcontainer, "Scale Y", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Scale Y", nameof(scaleY)).SetAsFlexibleFloat();
});
var scaleZButton = inspector.GenerateButton(this, subcontainer, "Scale Z", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Scale Z", nameof(scaleZ)).SetAsFlexibleFloat();
});
var graphicEditor = inspector.GenerateButton(this, subcontainer, "GraphicEditor", () =>
{
inspector.GenerateGraphicalFlexibleFloatWindow(this, "Scale",
new FlexibleFloat[] { scaleX, scaleY, scaleZ },
new string[] { "ScaleX", "ScaleY", "ScaleZ" });
});
var subcontainer2 = container.GenerateSubcontainer(1);
var valuetext = inspector.GenerateParameterText(this, subcontainer2, "value:", nameof(PreviewValue), true);
IHaveInspection insp = EditorManager.instance.uiManager.inspector;
var interferometerButton = inspector.GenerateButton(this, subcontainer2, "Interferometer", () =>
{
Vector3Interferometer.GenerateElement("New Vector3 Interferometer", Guid.NewGuid(), new List<string>(), true,
this, InterferomType.Additive, Vector3.zero);
});
InspectorBuilder.For(this)
.Section("Scale")
.Button("Scale X", () => insp.GenerateCompositeParameterWindow(this, "Scale X", nameof(scaleX)).SetAsFlexibleFloat())
.Button("Scale Y", () => insp.GenerateCompositeParameterWindow(this, "Scale Y", nameof(scaleY)).SetAsFlexibleFloat())
.Button("Scale Z", () => insp.GenerateCompositeParameterWindow(this, "Scale Z", nameof(scaleZ)).SetAsFlexibleFloat())
.Button("GraphicEditor", () => insp.GenerateGraphicalFlexibleFloatWindow(this, "Scale",
new FlexibleFloat[] { scaleX, scaleY, scaleZ },
new string[] { "ScaleX", "ScaleY", "ScaleZ" }))
.ParameterText(nameof(PreviewValue), "value:", autoUpdate: true)
.Span(3)
.Button("Interferometer", () => Vector3Interferometer.GenerateElement("New Vector3 Interferometer",
Guid.NewGuid(), new List<string>(), true, this, InterferomType.Additive, Vector3.zero))
.Span(3)
.Build();
}
}
}

View File

@@ -10,35 +10,22 @@ namespace Ichni.RhythmGame
public override void SetUpInspector()
{
base.SetUpInspector();
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Swirl");
var subcontainer = container.GenerateSubcontainer(3);
var eulerAngleXButton = inspector.GenerateButton(this, subcontainer, "Euler Angle X", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Euler Angle X", nameof(eulerAngleX)).SetAsFlexibleFloat();
});
var eulerAngleYButton = inspector.GenerateButton(this, subcontainer, "Euler Angle Y", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Euler Angle Y", nameof(eulerAngleY)).SetAsFlexibleFloat();
});
var eulerAngleZButton = inspector.GenerateButton(this, subcontainer, "Euler Angle Z", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Euler Angle Z", nameof(eulerAngleZ)).SetAsFlexibleFloat();
});
var graphicEditor = inspector.GenerateButton(this, subcontainer, "GraphicEditor", () =>
{
inspector.GenerateGraphicalFlexibleFloatWindow(this, "Swirl",
new FlexibleFloat[] { eulerAngleX, eulerAngleY, eulerAngleZ },
new string[] { "EulerX", "EulerY", "EulerZ" });
});
IHaveInspection insp = EditorManager.instance.uiManager.inspector;
var subcontainer2 = container.GenerateSubcontainer(1);
var valuetext = inspector.GenerateParameterText(this, subcontainer2, "value:", nameof(PreviewValue), true);
var interferometerButton = inspector.GenerateButton(this, subcontainer2, "Interferometer", () =>
{
Vector3Interferometer.GenerateElement("New Vector3 Interferometer", Guid.NewGuid(), new List<string>(), true,
this, InterferomType.Additive, Vector3.zero);
});
InspectorBuilder.For(this)
.Section("Swirl")
.Button("Euler Angle X", () => insp.GenerateCompositeParameterWindow(this, "Euler Angle X", nameof(eulerAngleX)).SetAsFlexibleFloat())
.Button("Euler Angle Y", () => insp.GenerateCompositeParameterWindow(this, "Euler Angle Y", nameof(eulerAngleY)).SetAsFlexibleFloat())
.Button("Euler Angle Z", () => insp.GenerateCompositeParameterWindow(this, "Euler Angle Z", nameof(eulerAngleZ)).SetAsFlexibleFloat())
.Button("GraphicEditor", () => insp.GenerateGraphicalFlexibleFloatWindow(this, "Swirl",
new FlexibleFloat[] { eulerAngleX, eulerAngleY, eulerAngleZ },
new string[] { "EulerX", "EulerY", "EulerZ" }))
.ParameterText(nameof(PreviewValue), "value:", autoUpdate: true)
.Span(3)
.Button("Interferometer", () => Vector3Interferometer.GenerateElement("New Vector3 Interferometer",
Guid.NewGuid(), new List<string>(), true, this, InterferomType.Additive, Vector3.zero))
.Span(3)
.Build();
}
}
}

View File

@@ -11,38 +11,57 @@ namespace Ichni.RhythmGame
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Color");
// ColorSubmodule 的 BaseColorPicker ↔ HsvDrawer 双向引用需要
// 直接操作控件实例,使用 RawSection 处理这一复杂交互
var builder = InspectorBuilder.For(this);
if ((attachedGameElement as IHaveColorSubmodule).haveBaseColor)
{
var subBase = container.GenerateSubcontainer(1, 280f);
var baseColor = inspector.GenerateBaseColorPicker(this, subBase, "Base Color", nameof(originalBaseColor));
baseColor.AddListenerFunction(Refresh);
var hsvDrawer = inspector.GenerateHsvDrawer(this, subBase, "HSV", nameof(originalBaseColor));
hsvDrawer.AddListenerFunction(Refresh);
baseColor.hsvDrawer = hsvDrawer;
hsvDrawer.baseColorPicker = baseColor;
if (attachedGameElement.childElementList.Exists(x => x is BaseColorChange))
builder.RawSection("Color", int.MaxValue, (insp, container) =>
{
baseColor.title.text += " (Occupied by Animation)";
baseColor.canvasGroup.interactable = false;
hsvDrawer.title.text += " (Occupied by Animation)";
hsvDrawer.gameObject.SetActive(false);
}
bool occupiedByAnimation = attachedGameElement.childElementList.Exists(x => x is BaseColorChange);
var subBase = container.GenerateSubcontainer(1, 280f);
var baseColor = insp.GenerateBaseColorPicker(this, subBase, "Base Color", nameof(originalBaseColor));
baseColor.AddListenerFunction(Refresh);
var hsvDrawer = insp.GenerateHsvDrawer(this, subBase, "HSV", nameof(originalBaseColor));
hsvDrawer.AddListenerFunction(Refresh);
// 双向引用
baseColor.hsvDrawer = hsvDrawer;
hsvDrawer.baseColorPicker = baseColor;
if (occupiedByAnimation)
{
baseColor.title.text += " (Occupied by Animation)";
baseColor.canvasGroup.interactable = false;
hsvDrawer.title.text += " (Occupied by Animation)";
hsvDrawer.gameObject.SetActive(false);
}
});
}
if ((attachedGameElement as IHaveColorSubmodule).haveEmissionColor)
{
var subEmission = container.GenerateSubcontainer(1, 320f);
var emissionColor = inspector.GenerateEmissionColorPicker(this, subEmission, "Emission Color",
nameof(emissionEnabled), nameof(originalEmissionColor), nameof(originalEmissionIntensity));
emissionColor.AddListenerFunction(Refresh);
if (attachedGameElement.childElementList.Exists(x => x is EmissionColorChange))
builder.RawSection("Emission", int.MaxValue, (insp, container) =>
{
emissionColor.title.text += " (Occupied by Animation)";
emissionColor.canvasGroup.interactable = false;
}
bool occupiedByAnimation = attachedGameElement.childElementList.Exists(x => x is EmissionColorChange);
var subEmission = container.GenerateSubcontainer(1, 320f);
var emissionColor = insp.GenerateEmissionColorPicker(this, subEmission, "Emission Color",
nameof(emissionEnabled), nameof(originalEmissionColor), nameof(originalEmissionIntensity));
emissionColor.AddListenerFunction(Refresh);
if (occupiedByAnimation)
{
emissionColor.title.text += " (Occupied by Animation)";
emissionColor.canvasGroup.interactable = false;
}
});
}
builder.Build();
}
}
}

View File

@@ -8,35 +8,38 @@ namespace Ichni.RhythmGame
{
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
Inspector inspectorMain = EditorManager.instance.uiManager.inspector;
foreach (var effect in effectCollection)
{
var container = inspector.GenerateContainer(effect.Key);
var subcontainer = container.GenerateSubcontainer(3);
var effectNameInputField = inspector.GenerateInputField(subcontainer, "Effect Name");
var addEffectButton = inspector.GenerateButton(this, subcontainer, "Add Effect", () =>
{
if (EffectCollection.TryGetValue(effectNameInputField.GetValue<string>(), out var factory))
{
EffectBase newEffect = factory.Invoke();
newEffect.attachedGameElement = attachedGameElement;
var list = effectCollection[effect.Key];
if (list.Count > 0)
var nameRef = new ElementRef<DynamicUIInputField>();
string effectKey = effect.Key;
InspectorBuilder.For(this)
.Section(effectKey)
.UnboundInputField("Effect Name").WithRef(nameRef)
.Button("Add Effect", () =>
{
var last = list[list.Count - 1];
newEffect.CopyParametersFrom(last);
}
list.Add(newEffect);
newEffect.AccommodatingList = list;
inspectorMain.SetInspector(attachedGameElement);
}
else
{
LogWindow.Log("Effect Type not found.", Color.yellow);
}
});
if (EffectCollection.TryGetValue(nameRef.Value?.GetValue<string>(), out var factory))
{
EffectBase newEffect = factory.Invoke();
newEffect.attachedGameElement = attachedGameElement;
var list = effectCollection[effectKey];
if (list.Count > 0)
{
var last = list[list.Count - 1];
newEffect.CopyParametersFrom(last);
}
list.Add(newEffect);
newEffect.AccommodatingList = list;
inspectorMain.SetInspector(attachedGameElement);
}
else
{
LogWindow.Log("Effect Type not found.", Color.yellow);
}
})
.Build();
foreach (var effectBase in effect.Value)
{

View File

@@ -1,5 +1,4 @@
using Ichni.Editor;
using UnityEngine;
namespace Ichni.RhythmGame
{
@@ -7,21 +6,30 @@ namespace Ichni.RhythmGame
{
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Time Duration");
var subcontainer = container.GenerateSubcontainer(3);
var overrideToggle = inspector.GenerateToggle(this, subcontainer, "Override Duration", nameof(isOverridingDuration));
var startTimeInputField = inspector.GenerateInputField(this, subcontainer, "Start Time", nameof(startTime));
var endTimeInputField = inspector.GenerateInputField(this, subcontainer, "End Time", nameof(endTime));
void SetInputFieldInteractable(bool interactable)
{
startTimeInputField.inputField.interactable = interactable;
endTimeInputField.inputField.interactable = interactable;
}
SetInputFieldInteractable(isOverridingDuration);
overrideToggle.AddListenerFunction(() => SetInputFieldInteractable(isOverridingDuration));
InspectorBuilder.For(this)
.Section("Time Duration")
.Toggle(nameof(isOverridingDuration), "Override Duration")
.OnChanged(() =>
{
var insp = EditorManager.instance.uiManager.inspector as IHaveInspection;
if (insp.MarkedElements.TryGetValue("tdStartTime", out var st))
{
st.canvasGroup.interactable = isOverridingDuration;
st.canvasGroup.alpha = isOverridingDuration ? 1f : 0.5f;
}
if (insp.MarkedElements.TryGetValue("tdEndTime", out var et))
{
et.canvasGroup.interactable = isOverridingDuration;
et.canvasGroup.alpha = isOverridingDuration ? 1f : 0.5f;
}
})
.InputField(nameof(startTime), "Start Time")
.EnabledIf(() => isOverridingDuration)
.Mark("tdStartTime")
.InputField(nameof(endTime), "End Time")
.EnabledIf(() => isOverridingDuration)
.Mark("tdEndTime")
.Build();
}
}
}

View File

@@ -7,33 +7,28 @@ namespace Ichni.RhythmGame
{
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var builder = InspectorBuilder.For(this)
.Section("Transform")
.Vector3Field(nameof(originalPosition), "Start Position")
.AutoUpdate().OnChanged(Refresh)
.Vector3Field(nameof(originalEulerAngles), "Start Rotation")
.AutoUpdate().OnChanged(Refresh)
.Vector3Field(nameof(originalScale), "Start Scale")
.AutoUpdate().OnChanged(Refresh)
.ParameterText(nameof(currentPosition), "Current Position", autoUpdate: true)
.Span(3)
.ParameterText(nameof(currentEulerAngles), "Current Rotation", autoUpdate: true)
.Span(3);
var container = inspector.GenerateContainer("Transform");
var subcontainer = container.GenerateSubcontainer(1);
var originalPosInputField =
inspector.GenerateVector3InputField(this, subcontainer, "Start Position", nameof(originalPosition), true);
var originalRotInputField =
inspector.GenerateVector3InputField(this, subcontainer, "Start Rotation", nameof(originalEulerAngles), true);
var originalScaleInputField =
inspector.GenerateVector3InputField(this, subcontainer, "Start Scale", nameof(originalScale), true);
var currentPosText =
inspector.GenerateParameterText(this, subcontainer, "Current Position", nameof(currentPosition), true);
var currentRotText =
inspector.GenerateParameterText(this, subcontainer, "Current Rotation", nameof(currentEulerAngles), true);
if (attachedGameElement is PathNode or Trail) // 如果是PathNode显示法线方向
if (attachedGameElement is PathNode or Trail)
{
var currentNormalText =
inspector.GenerateHintText(this, subcontainer, () => "Normal: " + attachedGameElement.transform.forward);
builder.HintText(() => "Normal: " + attachedGameElement.transform.forward)
.Span(3);
}
var currentScaleText =
inspector.GenerateParameterText(this, subcontainer, "Current Scale", nameof(currentScale), true);
originalPosInputField.AddListenerFunction(Refresh);
originalRotInputField.AddListenerFunction(Refresh);
originalScaleInputField.AddListenerFunction(Refresh);
builder.ParameterText(nameof(currentScale), "Current Scale", autoUpdate: true)
.Span(3)
.Build();
}
}
}

View File

@@ -20,45 +20,45 @@ namespace Ichni.RhythmGame
{
base.SetUpInspector();
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Generate");
var builder = InspectorBuilder.For(this)
.Section("Generate")
.Button("Environment Object", () =>
TemporaryObject.GenerateElement("New Environment Object", Guid.NewGuid(),
new List<string>(), true, this))
.Toggle(nameof(isStatic), "Is Static")
.OnChanged(() => gameObject.isStatic = isStatic)
.Preset(InspectorBuilder.TransformPreset)
.Section("Generate Animations")
.Button("Base Color Change", () =>
BaseColorChange.GenerateElement("New Base Color Change", Guid.NewGuid(), new List<string>(), true,
this,
new FlexibleFloat(new List<AnimatedFloat> { new AnimatedFloat(0f, 0.1f, colorSubmodule.originalBaseColor.r, colorSubmodule.originalBaseColor.r, animationCurveType: AnimationCurveType.Linear) }),
new FlexibleFloat(new List<AnimatedFloat> { new AnimatedFloat(0f, 0.1f, colorSubmodule.originalBaseColor.g, colorSubmodule.originalBaseColor.g, animationCurveType: AnimationCurveType.Linear) }),
new FlexibleFloat(new List<AnimatedFloat> { new AnimatedFloat(0f, 0.1f, colorSubmodule.originalBaseColor.b, colorSubmodule.originalBaseColor.b, animationCurveType: AnimationCurveType.Linear) }),
new FlexibleFloat(new List<AnimatedFloat> { new AnimatedFloat(0f, 0.1f, colorSubmodule.originalBaseColor.a, colorSubmodule.originalBaseColor.a, animationCurveType: AnimationCurveType.Linear) })
));
var objectSettings = container.GenerateSubcontainer(3);
var environmentObjectButton = inspector.GenerateButton(this, objectSettings, "Environment Object",
() => TemporaryObject.GenerateElement("New Environment Object", Guid.NewGuid(), new List<string>(),
true, this));
var isStaticToggle = inspector.GenerateToggle(this, objectSettings, "Is Static", nameof(isStatic))
.AddListenerFunction(() => gameObject.isStatic = isStatic);
StandardInspectionElement.GenerateForTransform(this, container); //关于有Transform的元素
var generateAnimation = container.GenerateSubcontainer(3);
var generateBaseColorChangeButton = inspector.GenerateButton(this, generateAnimation, "Base Color Change",
() => BaseColorChange.GenerateElement("New Base Color Change", Guid.NewGuid(), new List<string>(), true,
this,
new FlexibleFloat(new List<AnimatedFloat> { new AnimatedFloat(0f, 0.1f, colorSubmodule.originalBaseColor.r, colorSubmodule.originalBaseColor.r, animationCurveType: AnimationCurveType.Linear) }),
new FlexibleFloat(new List<AnimatedFloat> { new AnimatedFloat(0f, 0.1f, colorSubmodule.originalBaseColor.g, colorSubmodule.originalBaseColor.g, animationCurveType: AnimationCurveType.Linear) }),
new FlexibleFloat(new List<AnimatedFloat> { new AnimatedFloat(0f, 0.1f, colorSubmodule.originalBaseColor.b, colorSubmodule.originalBaseColor.b, animationCurveType: AnimationCurveType.Linear) }),
new FlexibleFloat(new List<AnimatedFloat> { new AnimatedFloat(0f, 0.1f, colorSubmodule.originalBaseColor.a, colorSubmodule.originalBaseColor.a, animationCurveType: AnimationCurveType.Linear) })
));
if (haveEmissionColor)
{
var generateEmissionColorChangeButton = inspector.GenerateButton(this, generateAnimation, "Emission Color Change",
() => EmissionColorChange.GenerateElement("New Emission Color Change", Guid.NewGuid(), new List<string>(), true,
this, new FlexibleFloat(), new FlexibleFloat(), new FlexibleFloat(), new FlexibleFloat()));
builder.Button("Emission Color Change", () =>
EmissionColorChange.GenerateElement("New Emission Color Change", Guid.NewGuid(),
new List<string>(), true, this,
new FlexibleFloat(), new FlexibleFloat(), new FlexibleFloat(), new FlexibleFloat()));
}
var generatePropertyAnimationColor = inspector.GenerateButton(this, generateAnimation, "Property Animation Color",
() => PropertyAnimationColor.GenerateElement("New Property Animation Color", Guid.NewGuid(), new List<string>(), true,
this, this.GetType().FullName, "", new FlexibleFloat(), new FlexibleFloat(), new FlexibleFloat(), new FlexibleFloat()));
var generatePropertyAnimationFloat = inspector.GenerateButton(this, generateAnimation, "Property Animation Float",
() => PropertyAnimationFloat.GenerateElement("New Property Animation Float", Guid.NewGuid(), new List<string>(), true,
this, this.GetType().FullName, "", new FlexibleFloat()));
var generatePropertyAnimationVector3 = inspector.GenerateButton(this, generateAnimation, "Property Animation Vector3",
() => PropertyAnimationVector3.GenerateElement("New Property Animation Vector3", Guid.NewGuid(), new List<string>(), true,
this, this.GetType().FullName, "", new FlexibleFloat(), new FlexibleFloat(), new FlexibleFloat()));
builder
.Button("Property Animation Color", () =>
PropertyAnimationColor.GenerateElement("New Property Animation Color", Guid.NewGuid(),
new List<string>(), true, this, GetType().FullName, "",
new FlexibleFloat(), new FlexibleFloat(), new FlexibleFloat(), new FlexibleFloat()))
.Button("Property Animation Float", () =>
PropertyAnimationFloat.GenerateElement("New Property Animation Float", Guid.NewGuid(),
new List<string>(), true, this, GetType().FullName, "", new FlexibleFloat()))
.Button("Property Animation Vector3", () =>
PropertyAnimationVector3.GenerateElement("New Property Animation Vector3", Guid.NewGuid(),
new List<string>(), true, this, GetType().FullName, "",
new FlexibleFloat(), new FlexibleFloat(), new FlexibleFloat()))
.Build();
}
#endregion
}

View File

@@ -22,83 +22,71 @@ namespace Ichni.RhythmGame
#region [] Inspector GUI
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
base.SetUpInspector();
var container = inspector.GenerateContainer("Generate");
var generateBase = container.GenerateSubcontainer(3);
var folderButton = inspector.GenerateButton(this, generateBase, "Folder",
() => ElementFolder.GenerateElement("New Folder", Guid.NewGuid(), new List<string>(), true, this));
var trackButton = inspector.GenerateButton(this, generateBase, "Track",
() => Track.GenerateElement("New Track", Guid.NewGuid(), new List<string>(), true, this));
var cameraButton = inspector.GenerateButton(this, generateBase, "Camera",
() => GameCamera.GenerateElement("New Camera", Guid.NewGuid(), new List<string>(), true, this,
GameCamera.CameraViewType.Perspective, 60, 10));
var crossTrackPoint = inspector.GenerateButton(this, generateBase, "Cross Track Point",
() => CrossTrackPoint.GenerateElement("New Cross Track Point", Guid.NewGuid(), new List<string>(), true,
this, new FlexibleInt(), new FlexibleFloat()));
StandardInspectionElement.GenerateForTransform(this, container); // 关于有Transform的元素
var generateNote = container.GenerateSubcontainer(3);
var tapButton = inspector.GenerateButton(this, generateNote, "Tap",
() => Tap.GenerateElement("New Tap", Guid.NewGuid(), new List<string>(), true, this, 0f));
var stayButton = inspector.GenerateButton(this, generateNote, "Stay",
() => Stay.GenerateElement("New Stay", Guid.NewGuid(), new List<string>(), true, this, 0f));
var holdButton = inspector.GenerateButton(this, generateNote, "Hold",
() => Hold.GenerateElement("New Hold", Guid.NewGuid(), new List<string>(), true, this, 0f, 1f));
var flickButton = inspector.GenerateButton(this, generateNote, "Flick",
() => Flick.GenerateElement("New Flick", Guid.NewGuid(), new List<string>(), true, this, 0f,
new List<Vector2>()));
InspectorBuilder.For(this)
.Section("Generate", sectionOrder: 10)
.Button("Folder", () => ElementFolder.GenerateElement("New Folder", Guid.NewGuid(), new List<string>(), true, this))
.Button("Track", () => Track.GenerateElement("New Track", Guid.NewGuid(), new List<string>(), true, this))
.Button("Camera", () => GameCamera.GenerateElement("New Camera", Guid.NewGuid(), new List<string>(), true, this, GameCamera.CameraViewType.Perspective, 60, 10))
.Button("Cross Track Point", () => CrossTrackPoint.GenerateElement("New Cross Track Point", Guid.NewGuid(), new List<string>(), true, this, new FlexibleInt(), new FlexibleFloat()))
.Build();
var generateEnvironment = container.GenerateSubcontainer(3);
var environmentObjectButton = inspector.GenerateButton(this, generateEnvironment, "Environment Object",
() => TemporaryObject.GenerateElement("New Environment Object", Guid.NewGuid(), new List<string>(),
true, this));
var timeEffectsCollectionButton = inspector.GenerateButton(this, generateEnvironment, "Time Effects Collection",
() => TimeEffectsCollection.GenerateElement("New Time Effects Collection", Guid.NewGuid(),
new List<string>(), true, this, 0));
var generateParticleEmitterButton = inspector.GenerateButton(this, generateEnvironment, "Generate Particle Emitter", () =>
{
ParticleEmitter.GenerateElement("New Particle Emitter", Guid.NewGuid(), new List<string>(), true,
this, "", "", false, 0, 1, ParticleSystemSimulationSpace.World,
10, 5, 1, 1, true, Vector3.zero);
});
var generateTools = container.GenerateSubcontainer(1);
inspector.GenerateButton(this, generateTools, "Time Shift Tool", () =>
{
GeneralSecondaryWindow timeShiftWindow = UnityEngine.Object.Instantiate(
EditorManager.instance.basePrefabs.generalSecondaryWindow,
EditorManager.instance.uiManager.WindowsCanvas.GetComponent<RectTransform>())
.GetComponent<GeneralSecondaryWindow>();
timeShiftWindow.Initialize("Time Shift Tool");
var windowContainer = timeShiftWindow.GenerateContainer("Settings");
InspectorBuilder.For(this)
.Section("Generate Elements", sectionOrder: 15)
.Preset(InspectorBuilder.TransformPreset)
.Section("Generate Notes", sectionOrder: 20)
.Button("Tap", () => Tap.GenerateElement("New Tap", Guid.NewGuid(), new List<string>(), true, this, 0f))
.Button("Stay", () => Stay.GenerateElement("New Stay", Guid.NewGuid(), new List<string>(), true, this, 0f))
.Button("Hold", () => Hold.GenerateElement("New Hold", Guid.NewGuid(), new List<string>(), true, this, 0f, 1f))
.Button("Flick", () => Flick.GenerateElement("New Flick", Guid.NewGuid(), new List<string>(), true, this, 0f, new List<Vector2>()))
.Section("Generate Environment", sectionOrder: 30)
.Button("Environment Object", () => TemporaryObject.GenerateElement("New Environment Object", Guid.NewGuid(), new List<string>(), true, this))
.Button("Time Effects Collection", () => TimeEffectsCollection.GenerateElement("New Time Effects Collection", Guid.NewGuid(),
new List<string>(), true, this, 0))
.Button("Particle Emitter", () =>
{
ParticleEmitter.GenerateElement("New Particle Emitter", Guid.NewGuid(), new List<string>(), true,
this, "", "", false, 0, 1, ParticleSystemSimulationSpace.World,
10, 5, 1, 1, true, Vector3.zero);
})
var subBase = windowContainer.GenerateSubcontainer(3);
var affectNoteToggle = timeShiftWindow.GenerateToggle(subBase, "Affect Notes", true);
var offsetInput = timeShiftWindow.GenerateInputField(subBase, "Time Offset (seconds)", "0");
.Section("Tools", sectionOrder: 40)
.Button("Time Shift Tool", () =>
{
GeneralSecondaryWindow timeShiftWindow = UnityEngine.Object.Instantiate(
EditorManager.instance.basePrefabs.generalSecondaryWindow,
EditorManager.instance.uiManager.WindowsCanvas.GetComponent<RectTransform>())
.GetComponent<GeneralSecondaryWindow>();
var btnBase = windowContainer.GenerateSubcontainer(2);
timeShiftWindow.GenerateButton(btnBase, "Apply", () =>
{
if (float.TryParse(offsetInput.inputField.text, out float offset))
{
ApplyTimeShiftRecursive(affectNoteToggle.toggle.isOn, offset);
UnityEngine.Object.Destroy(timeShiftWindow.gameObject);
LogWindow.Log($"Time shifted by {offset}.", Color.green);
}
else
{
LogWindow.Log("Invalid number format!", Color.red);
}
});
timeShiftWindow.GenerateButton(btnBase, "Cancel", () =>
{
UnityEngine.Object.Destroy(timeShiftWindow.gameObject);
});
});
timeShiftWindow.Initialize("Time Shift Tool");
var windowContainer = timeShiftWindow.GenerateContainer("Settings");
var subBase = windowContainer.GenerateSubcontainer(3);
var affectNoteToggle = timeShiftWindow.GenerateToggle(subBase, "Affect Notes", true);
var offsetInput = timeShiftWindow.GenerateInputField(subBase, "Time Offset (seconds)", "0");
var btnBase = windowContainer.GenerateSubcontainer(2);
timeShiftWindow.GenerateButton(btnBase, "Apply", () =>
{
if (float.TryParse(offsetInput.inputField.text, out float offset))
{
ApplyTimeShiftRecursive(affectNoteToggle.toggle.isOn, offset);
UnityEngine.Object.Destroy(timeShiftWindow.gameObject);
LogWindow.Log($"Time shifted by {offset}.", Color.green);
}
else
{
LogWindow.Log("Invalid number format!", Color.red);
}
});
timeShiftWindow.GenerateButton(btnBase, "Cancel", () =>
{
UnityEngine.Object.Destroy(timeShiftWindow.gameObject);
});
})
.Span(3)
.Build();
}
#endregion

View File

@@ -18,12 +18,13 @@ namespace Ichni.RhythmGame
#region [] Inspector Setup
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
base.SetUpInspector();
var container = inspector.GenerateContainer("Settings");
var settingsSubcontainer = container.GenerateSubcontainer(3);
var farClipRangeButton = inspector.GenerateInputField(this, settingsSubcontainer, "Far Clip Range",
nameof(farClipRange)).AddListenerFunction(ApplyExtension);
InspectorBuilder.For(this)
.Section("Settings")
.InputField(nameof(farClipRange), "Far Clip Range")
.OnChanged(ApplyExtension)
.Build();
}
#endregion
}

View File

@@ -18,21 +18,22 @@ namespace Ichni.RhythmGame
#region [] Inspector Setup
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
base.SetUpInspector();
var container = inspector.GenerateContainer("Generate");
StandardInspectionElement.GenerateForTransform(this, container); //关于有Transform的元素
var generateAnimation = container.GenerateSubcontainer(3);
var fovAnimationButton = inspector.GenerateButton(this, generateAnimation, "Field of View",
() => CameraFieldOfView.GenerateElement("New Field of View", Guid.NewGuid(),
new List<string>(), true, this, new FlexibleFloat(new List<AnimatedFloat>
{
new AnimatedFloat(0f, 1f, 60f, 60f, AnimationCurveType.Linear)
})));
var extensionButton = inspector.GenerateButton(this, generateAnimation, "Extension",
() => GameCameraExtension.GenerateElement("New Extension", Guid.NewGuid(),
new List<string>(), true, this, 1000f));
InspectorBuilder.For(this)
.Section("Generate Elements")
.Preset(InspectorBuilder.TransformPreset)
.Section("Generate")
.Button("Field of View", () =>
CameraFieldOfView.GenerateElement("New Field of View", Guid.NewGuid(),
new List<string>(), true, this, new FlexibleFloat(new List<AnimatedFloat>
{
new AnimatedFloat(0f, 1f, 60f, 60f, AnimationCurveType.Linear)
})))
.Button("Extension", () =>
GameCameraExtension.GenerateElement("New Extension", Guid.NewGuid(),
new List<string>(), true, this, 1000f))
.Build();
}
#endregion

View File

@@ -101,41 +101,31 @@ namespace Ichni.RhythmGame
public virtual void SetUpInspector()
{
ScanAndAddEnableTypes();
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Element Info");
var info = container.GenerateSubcontainer(3);
var nameInputField = inspector.GenerateInputField(this, info, GetType().Name + "'s Name", nameof(elementName));
var guidText = inspector.GenerateParameterText(this, info, "Element GUID", nameof(elementGuid));
var tagsListButton = inspector.GenerateButton(this, info, "Tags List", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Tags List", nameof(tags)).SetAsStringList();
});
if (enableTypes != null && enableTypes.Count > 0)
{
var elcontainer = inspector.GenerateContainer("Enable Children Display");
var enableTypeContainer = elcontainer.GenerateSubcontainer(3);
var type = enableTypes.GetType().GetGenericArguments()[0];
int elcount = 0;
for (int idx = 0; idx < enableTypes.Count; idx++)
{
elcount++;
if (elcount > 3)
// Element Info
InspectorBuilder.For(this)
.Section("Element Info", sectionOrder: 0)
.InputField(nameof(elementName), GetType().Name + "'s Name")
.ParameterText(nameof(elementGuid), "Element GUID")
.Button("Tags List", () =>
{
elcount = 0;
enableTypeContainer = elcontainer.GenerateSubcontainer(3);
}
var et = enableTypes[idx];
inspector.GenerateToggle(
et,
enableTypeContainer,
et.type.Name,
nameof(et.enable)
);
}
IHaveInspection insp = EditorManager.instance.uiManager.inspector;
insp.GenerateCompositeParameterWindow(this, "Tags List", nameof(tags)).SetAsStringList();
})
.Build();
// Enable Children Display — 每个 Toggle 绑定不同 EnableType 实例
if (enableTypes is { Count: > 0 })
{
var enableBuilder = InspectorBuilder.For(this)
.Section("Enable Children Display", sectionOrder: 1);
enableBuilder.ForEach(enableTypes, (b, et, i) =>
{
b.Toggle(nameof(EnableType.enable), et.type.Name).BoundTo(et);
});
enableBuilder.Build();
}
foreach (var submodule in submoduleList)
{
submodule.SetUpInspector();

View File

@@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
using Ichni.Editor; // 仅限于在Editor中处理Inspector
using Ichni.Editor;
using UnityEngine;
namespace Ichni.RhythmGame
{
public partial class TemporaryObject // Editor UI Setup Partial
public partial class TemporaryObject
{
public List<string> themeBundleList;
public List<string> objectNameList;
@@ -13,42 +13,30 @@ namespace Ichni.RhythmGame
#region [Inspector面版定制] Override Inspector UI Setup
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
Inspector inspectorMain = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Temporary Object");
// 生成实体
var generate = container.GenerateSubcontainer(3);
var themeBundleDropdown =
inspector.GenerateDropdown(this, generate, "Theme Bundle", themeBundleList, nameof(themeBundleName))
.AddListenerFunction(() => inspectorMain.SetInspector(this));
if (themeBundleName != String.Empty && ThemeBundleManager.instance.TryGetThemeBundle(themeBundleName, out ThemeBundle themeBundle))
{
objectNameList = themeBundle.assetList_GameObject.ConvertAll(x => x.name);
var objectNameDropdown =
inspector.GenerateDropdown(this, generate, "Object Name", objectNameList, nameof(objectName))
.AddListenerFunction(() => inspectorMain.SetInspector(this));
}
else
{
var objectNameDropdown =
inspector.GenerateDropdown(this, generate, "Object Name", new List<string>(), nameof(objectName));
objectNameDropdown.dropdown.interactable = false;
} // 如果没有选择主题包,则物体名称下拉框不可用
bool hasThemeBundle = themeBundleName != String.Empty;
ThemeBundle themeBundle = null;
if (hasThemeBundle)
hasThemeBundle = ThemeBundleManager.instance.TryGetThemeBundle(themeBundleName, out themeBundle);
objectNameList = hasThemeBundle ? themeBundle.assetList_GameObject.ConvertAll(x => x.name) : new List<string>();
var generateButton = inspector.GenerateButton(this, generate, "Generate", () =>
{
EditorManager.instance.operationManager.CopyPasteDeleteModule.DeleteElement(this);
inspectorMain.ClearInspector();
SubstantialObject.GenerateElement(elementName, elementGuid, tags, true, themeBundleName, objectName, parentElement);
});
if (themeBundleName == String.Empty || objectName == String.Empty)
{
generateButton.button.interactable = false;
}
InspectorBuilder.For(this)
.Section("Temporary Object")
.Dropdown(nameof(themeBundleName), themeBundleList, "Theme Bundle")
.OnChanged(() => inspectorMain.SetInspector(this))
.Dropdown(nameof(objectName), objectNameList, "Object Name")
.EnabledIf(() => hasThemeBundle)
.OnChanged(() => inspectorMain.SetInspector(this))
.Button("Generate", () =>
{
EditorManager.instance.operationManager.CopyPasteDeleteModule.DeleteElement(this);
inspectorMain.ClearInspector();
SubstantialObject.GenerateElement(elementName, elementGuid, tags, true,
themeBundleName, objectName, parentElement);
})
.EnabledIf(() => themeBundleName != String.Empty && objectName != String.Empty)
.Build();
}
#endregion
}

View File

@@ -16,24 +16,18 @@ namespace Ichni.RhythmGame
#region [] Inspector Setup
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Variables Container");
var subcontainer = container.GenerateSubcontainer(3);
var originalVariablesButton = inspector.GenerateButton(this, subcontainer, "Original Variables", () =>
{
var ov =
inspector.GenerateCompositeParameterWindow(this, "Original Variables List", nameof(originalVariables))
.SetAsStringIntDictionary()
.AddListenerFunction(RevertAllVariables);
});
var currentVariablesButton = inspector.GenerateButton(this, subcontainer, "Current Variables", () =>
{
var cv =
inspector.GenerateCompositeParameterWindow(this, "Current Variables List", nameof(currentVariables))
.SetAsStringIntDictionary();
});
IHaveInspection insp = EditorManager.instance.uiManager.inspector;
InspectorBuilder.For(this)
.Section("Variables Container")
.Button("Original Variables", () =>
insp.GenerateCompositeParameterWindow(this, "Original Variables List", nameof(originalVariables))
.SetAsStringIntDictionary()
.AddListenerFunction(RevertAllVariables))
.Button("Current Variables", () =>
insp.GenerateCompositeParameterWindow(this, "Current Variables List", nameof(currentVariables))
.SetAsStringIntDictionary())
.Build();
}
#endregion
}

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using Ichni.Editor;
using Ichni.RhythmGame.Beatmap;
namespace Ichni.RhythmGame
{
@@ -9,17 +6,15 @@ namespace Ichni.RhythmGame
{
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Bloom Shake");
var effectSettings = container.GenerateSubcontainer(3);
var effectTimeField = inspector.GenerateInputField(this, effectSettings, "Bloom Time", nameof(effectTime));
var bloomPeakField = inspector.GenerateInputField(this, effectSettings, "Bloom Peak", nameof(peak));
var intensityCurveButton = inspector.GenerateButton(this, effectSettings, "Intensity Curve", () =>
{
var intensityCurveWindow =
inspector.GenerateCompositeParameterWindow(this, "Intensity Curve", nameof(intensityCurve)).SetAsCustomCurve();
});
SetRemove(effectSettings);
IHaveInspection insp = EditorManager.instance.uiManager.inspector;
InspectorBuilder.For(this)
.Section("Bloom Shake")
.InputField(nameof(effectTime), "Bloom Time")
.InputField(nameof(peak), "Bloom Peak")
.Button("Intensity Curve", () => insp.GenerateCompositeParameterWindow(this, "Intensity Curve", nameof(intensityCurve)).SetAsCustomCurve())
.Button("Remove", () => { nowEffectState = EffectState.Before; AccommodatingList.Remove(this); EditorManager.instance.uiManager.inspector.SetInspector(attachedGameElement); })
.Build();
}
}
}

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using Ichni.Editor;
using Ichni.RhythmGame.Beatmap;
namespace Ichni.RhythmGame
{
@@ -9,19 +6,15 @@ namespace Ichni.RhythmGame
{
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Camera Tilt");
var subcontainer1 = container.GenerateSubcontainer(3);
var durationField = inspector.GenerateInputField(this, subcontainer1, "Duration", nameof(effectTime));
var curveButton = inspector.GenerateButton(this, subcontainer1, "Offset Curve", () =>
{
var intensityCurveWindow =
inspector.GenerateCompositeParameterWindow(this, "Offset Curve", nameof(offsetCurve)).SetAsCustomCurve();
});
var subcontainer2 = container.GenerateSubcontainer(1);
var offsetPeakField = inspector.GenerateVector3InputField(this, subcontainer2, "Offset Value", nameof(offsetValue));
IHaveInspection insp = EditorManager.instance.uiManager.inspector;
SetRemove(subcontainer2);
InspectorBuilder.For(this)
.Section("Camera Offset")
.InputField(nameof(effectTime), "Duration")
.Button("Offset Curve", () => insp.GenerateCompositeParameterWindow(this, "Offset Curve", nameof(offsetCurve)).SetAsCustomCurve())
.Vector3Field(nameof(offsetValue), "Offset Value")
.Button("Remove", () => { nowEffectState = EffectState.Before; AccommodatingList.Remove(this); EditorManager.instance.uiManager.inspector.SetInspector(attachedGameElement); })
.Build();
}
}
}

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using Ichni.Editor;
using Ichni.RhythmGame.Beatmap;
namespace Ichni.RhythmGame
{
@@ -9,15 +6,15 @@ namespace Ichni.RhythmGame
{
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Camera Shake");
var effectSettings = container.GenerateSubcontainer(3);
var durationInputField = inspector.GenerateInputField(this, effectSettings, "Effect Time", nameof(effectTime));
var frequencyInputField = inspector.GenerateInputField(this, effectSettings, "Frequency", nameof(frequency));
var amplitudeXInputField = inspector.GenerateInputField(this, effectSettings, "Amplitude X", nameof(amplitudeX));
var amplitudeYInputField = inspector.GenerateInputField(this, effectSettings, "Amplitude Y", nameof(amplitudeY));
var amplitudeZInputField = inspector.GenerateInputField(this, effectSettings, "Amplitude Z", nameof(amplitudeZ));
SetRemove(effectSettings);
InspectorBuilder.For(this)
.Section("Camera Shake")
.InputField(nameof(effectTime), "Effect Time")
.InputField(nameof(frequency), "Frequency")
.InputField(nameof(amplitudeX), "Amplitude X")
.InputField(nameof(amplitudeY), "Amplitude Y")
.InputField(nameof(amplitudeZ), "Amplitude Z")
.Button("Remove", () => { nowEffectState = EffectState.Before; AccommodatingList.Remove(this); EditorManager.instance.uiManager.inspector.SetInspector(attachedGameElement); })
.Build();
}
}
}

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using Ichni.Editor;
using Ichni.RhythmGame.Beatmap;
namespace Ichni.RhythmGame
{
@@ -9,20 +6,15 @@ namespace Ichni.RhythmGame
{
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Camera Tilt");
IHaveInspection insp = EditorManager.instance.uiManager.inspector;
var subcontainer1 = container.GenerateSubcontainer(3);
var durationField = inspector.GenerateInputField(this, subcontainer1, "Duration", nameof(effectTime));
var tiltCurveButton = inspector.GenerateButton(this, subcontainer1, "Tilt Curve", () =>
{
var intensityCurveWindow =
inspector.GenerateCompositeParameterWindow(this, "Tilt Curve", nameof(tiltCurve)).SetAsCustomCurve();
});
var subcontainer2 = container.GenerateSubcontainer(1);
var tiltValueField = inspector.GenerateVector3InputField(this, subcontainer2, "Tilt Value", nameof(tiltValue));
SetRemove(subcontainer2);
InspectorBuilder.For(this)
.Section("Camera Tilt")
.InputField(nameof(effectTime), "Duration")
.Button("Tilt Curve", () => insp.GenerateCompositeParameterWindow(this, "Tilt Curve", nameof(tiltCurve)).SetAsCustomCurve())
.Vector3Field(nameof(tiltValue), "Tilt Value")
.Button("Remove", () => { nowEffectState = EffectState.Before; AccommodatingList.Remove(this); EditorManager.instance.uiManager.inspector.SetInspector(attachedGameElement); })
.Build();
}
}
}

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using Ichni.Editor;
using Ichni.RhythmGame.Beatmap;
namespace Ichni.RhythmGame
{
@@ -9,17 +6,15 @@ namespace Ichni.RhythmGame
{
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Camera Zoom");
var effectSettings = container.GenerateSubcontainer(3);
var zoomDurationInputField = inspector.GenerateInputField(this, effectSettings, "Zoom Duration", nameof(effectTime));
var relativeZoomInputField = inspector.GenerateInputField(this, effectSettings, "Relative Zoom", nameof(relativeZoom));
var zoomCurveButton = inspector.GenerateButton(this, effectSettings, "Intensity Curve", () =>
{
var zoomCurveWindow =
inspector.GenerateCompositeParameterWindow(this, "Intensity Curve", nameof(zoomCurve)).SetAsCustomCurve();
});
SetRemove(effectSettings);
IHaveInspection insp = EditorManager.instance.uiManager.inspector;
InspectorBuilder.For(this)
.Section("Camera Zoom")
.InputField(nameof(effectTime), "Zoom Duration")
.InputField(nameof(relativeZoom), "Relative Zoom")
.Button("Intensity Curve", () => insp.GenerateCompositeParameterWindow(this, "Intensity Curve", nameof(zoomCurve)).SetAsCustomCurve())
.Button("Remove", () => { nowEffectState = EffectState.Before; AccommodatingList.Remove(this); EditorManager.instance.uiManager.inspector.SetInspector(attachedGameElement); })
.Build();
}
}
}

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using Ichni.Editor;
using Ichni.RhythmGame.Beatmap;
namespace Ichni.RhythmGame
{
@@ -9,17 +6,15 @@ namespace Ichni.RhythmGame
{
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Chromatic Aberration");
var effectSettings = container.GenerateSubcontainer(3);
var effectTimeField = inspector.GenerateInputField(this, effectSettings, "Duration", nameof(effectTime));
var bloomPeakField = inspector.GenerateInputField(this, effectSettings, "Peak Value", nameof(peak));
var intensityCurveButton = inspector.GenerateButton(this, effectSettings, "Intensity Curve", () =>
{
var intensityCurveWindow =
inspector.GenerateCompositeParameterWindow(this, "Intensity Curve", nameof(intensityCurve)).SetAsCustomCurve();
});
SetRemove(effectSettings);
IHaveInspection insp = EditorManager.instance.uiManager.inspector;
InspectorBuilder.For(this)
.Section("Chromatic Aberration")
.InputField(nameof(effectTime), "Duration")
.InputField(nameof(peak), "Peak Value")
.Button("Intensity Curve", () => insp.GenerateCompositeParameterWindow(this, "Intensity Curve", nameof(intensityCurve)).SetAsCustomCurve())
.Button("Remove", () => { nowEffectState = EffectState.Before; AccommodatingList.Remove(this); EditorManager.instance.uiManager.inspector.SetInspector(attachedGameElement); })
.Build();
}
}
}

View File

@@ -11,37 +11,40 @@ namespace Ichni.RhythmGame
{
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
Inspector inspectorMain = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Enable Control");
var effectSettings = container.GenerateSubcontainer(3);
var connectedGameElementInputField = inspector.GenerateInputField(effectSettings, "Game Element Name");
var connectGameElementButton = inspector.GenerateButton(this, effectSettings, "Connect Game Element", () =>
{
connectedGameElement = EditorManager.instance.beatmapContainer.gameElementList
.First(e => e.elementName == connectedGameElementInputField.GetValue<string>());
var nameRef = new ElementRef<DynamicUIInputField>();
if (connectedGameElement == null)
{
LogWindow.Log("Game Element not found.", Color.red);
}
inspectorMain.SetInspector(connectedGameElement);
});
string ShowConnection() => connectedGameElement == null ? "No Game Element Connected" : "Connected With: " + connectedGameElement.elementName;
var connectHintText = inspector.GenerateHintText(this, effectSettings, ShowConnection);
var connectedVariableNameInputField = inspector.GenerateInputField(this, effectSettings, "Connected Variable Name", nameof(connectedVariableName));
var enableValueInputField = inspector.GenerateInputField(this, effectSettings, "Enable Value", nameof(enableValue));
// 自定义表达式暂时不可用
var useExpressionToggle = inspector.GenerateToggle(this, effectSettings, "Use Expression", nameof(useExpression));
useExpressionToggle.toggle.interactable = false;
var expressionInputField = inspector.GenerateInputField(this, effectSettings, "Expression", nameof(expression));
expressionInputField.inputField.interactable = false;
SetRemove(effectSettings);
InspectorBuilder.For(this)
.Section("Enable Control")
.UnboundInputField("Game Element Name").WithRef(nameRef)
.Button("Connect Game Element", () =>
{
connectedGameElement = EditorManager.instance.beatmapContainer.gameElementList
.FirstOrDefault(e => e.elementName == nameRef.Value?.GetValue<string>());
if (connectedGameElement == null)
{
LogWindow.Log("Game Element not found.", Color.red);
return;
}
inspectorMain.SetInspector(connectedGameElement);
})
.HintText(() => connectedGameElement == null
? "No Game Element Connected"
: "Connected With: " + connectedGameElement.elementName)
.InputField(nameof(connectedVariableName), "Connected Variable Name")
.InputField(nameof(enableValue), "Enable Value")
.Toggle(nameof(useExpression), "Use Expression")
.EnabledIf(() => false)
.InputField(nameof(expression), "Expression")
.EnabledIf(() => false)
.Button("Remove", () =>
{
nowEffectState = EffectState.Before;
AccommodatingList.Remove(this);
inspectorMain.SetInspector(attachedGameElement);
})
.Build();
}
}
}

Some files were not shown because too many files have changed in this diff Show More