UI系统
This commit is contained in:
157
Assets/_Game/Scripts/Editor/UI/UIInputPromptScaffold.cs
Normal file
157
Assets/_Game/Scripts/Editor/UI/UIInputPromptScaffold.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using BaseGames.UI;
|
||||
using BaseGames.UI.Theme;
|
||||
|
||||
namespace BaseGames.Editor.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// 输入提示控件脚手架:生成 / 放置 <c>UI_Control_InputPrompt</c> 预制件
|
||||
/// —— 一个「按键图标 + 文字」的可复用提示(如菜单底部「Ⓐ 确定」「Ⓑ 返回」)。
|
||||
///
|
||||
/// <para>图标走已有的设备适配链路(零代码):</para>
|
||||
/// 控件内 <see cref="InputIconImage"/> 据 ActionName 经 <c>IInputIconService</c> 解析
|
||||
/// <b>当前设备</b>(键鼠/PS/Xbox/Switch)+ 改键后的实际绑定 → 自动显示正确按键图标,
|
||||
/// 设备切换/改键时自动刷新。<b>前提:美术把按键 sprite 填入 ICN_Keyboard/Xbox/PlayStation/Switch 资产。</b>
|
||||
///
|
||||
/// <para>策划/美术用法:</para>
|
||||
/// 菜单「向选中节点放置 ▸ InputPrompt」放进任意 Canvas,
|
||||
/// 在 Icon 的 InputIconImage 填 ActionName(如 Interact/Jump),Label 填提示文字(可挂 LocalizedText)。
|
||||
///
|
||||
/// 菜单:BaseGames/UI/控件库/...
|
||||
/// </summary>
|
||||
public static class UIInputPromptScaffold
|
||||
{
|
||||
private const string ControlsDir = "Assets/_Game/Prefabs/UI/Controls";
|
||||
private const string ThemePath = "Assets/_Game/Data/UI/Themes/UI_Theme_Default.asset";
|
||||
private const string PfPrompt = "UI_Control_InputPrompt";
|
||||
|
||||
[MenuItem("BaseGames/UI/控件库/生成输入提示控件(UI_Control_InputPrompt)")]
|
||||
public static void GeneratePrompt()
|
||||
{
|
||||
EnsureFolder(ControlsDir);
|
||||
BuildPrompt();
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
Debug.Log($"[UIInputPrompt] 已生成 {ControlsDir}/{PfPrompt}.prefab。\n" +
|
||||
"用法:菜单「向选中节点放置 ▸ InputPrompt」放进 UI,Icon 填 ActionName、Label 填文字。\n" +
|
||||
"图标随当前设备自动切换;需美术把按键 sprite 填入 ICN_Keyboard/Xbox/PlayStation/Switch 资产。");
|
||||
}
|
||||
|
||||
[MenuItem("BaseGames/UI/控件库/向选中节点放置 ▸ InputPrompt")]
|
||||
private static void PlacePrompt()
|
||||
{
|
||||
string path = $"{ControlsDir}/{PfPrompt}.prefab";
|
||||
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
||||
if (prefab == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("控件库",
|
||||
$"未找到预制件:{path}\n请先执行「生成输入提示控件(UI_Control_InputPrompt)」。", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
Transform parent = Selection.activeTransform;
|
||||
if (parent == null)
|
||||
{
|
||||
var canvas = Object.FindObjectOfType<Canvas>();
|
||||
parent = canvas != null ? canvas.transform : null;
|
||||
}
|
||||
|
||||
var instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab, parent);
|
||||
if (instance == null) return;
|
||||
Undo.RegisterCreatedObjectUndo(instance, $"Place {PfPrompt}");
|
||||
Selection.activeGameObject = instance;
|
||||
EditorGUIUtility.PingObject(instance);
|
||||
}
|
||||
|
||||
// ── 构建 ─────────────────────────────────────────────────────────────
|
||||
private static void BuildPrompt()
|
||||
{
|
||||
var theme = AssetDatabase.LoadAssetAtPath<UIThemeSO>(ThemePath);
|
||||
|
||||
// 根:横向布局(图标 + 文字),自适应内容宽度
|
||||
var root = NewUI(PfPrompt, 120f, 44f);
|
||||
var h = root.AddComponent<HorizontalLayoutGroup>();
|
||||
h.spacing = 8f; h.childAlignment = TextAnchor.MiddleLeft;
|
||||
h.childForceExpandWidth = false; h.childForceExpandHeight = false;
|
||||
h.childControlWidth = true; h.childControlHeight = true;
|
||||
var fitter = root.AddComponent<ContentSizeFitter>();
|
||||
fitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
|
||||
// 图标:Image + InputIconImage(同物体,InputIconImage 要求 Image)
|
||||
var iconGo = NewUIChild(root.transform, "Icon", out _);
|
||||
var iconImg = iconGo.AddComponent<Image>();
|
||||
iconImg.preserveAspect = true;
|
||||
iconImg.sprite = Knob(); // 编辑期占位图;运行时由 InputIconImage 替换为当前设备按键图(无图则自动隐藏)
|
||||
var iconLe = iconGo.AddComponent<LayoutElement>();
|
||||
iconLe.preferredWidth = 40f; iconLe.preferredHeight = 40f;
|
||||
iconLe.minWidth = 32f; iconLe.minHeight = 32f;
|
||||
var iconComp = iconGo.AddComponent<InputIconImage>();
|
||||
SetEnum(iconComp, "_mode", (int)InputIconImage.LookupMode.ByActionName);
|
||||
SetString(iconComp, "_actionName", "Interact"); // 占位 ActionName,策划按需改
|
||||
|
||||
// 文字:提示标签(可挂 LocalizedText;默认占位)
|
||||
var lblGo = NewUIChild(root.transform, "Label", out _);
|
||||
var lbl = lblGo.AddComponent<TextMeshProUGUI>();
|
||||
lbl.text = "提示"; lbl.fontSize = 24f; lbl.alignment = TextAlignmentOptions.MidlineLeft;
|
||||
lbl.color = new Color(0.80f, 0.80f, 0.82f, 1f);
|
||||
lbl.raycastTarget = false;
|
||||
var lblLe = lblGo.AddComponent<LayoutElement>();
|
||||
lblLe.preferredHeight = 40f;
|
||||
SetEnum(lblGo.AddComponent<UIThemeRole>(), "_kind", (int)UIThemeRoleKind.Text_Secondary);
|
||||
|
||||
_ = theme; // 配色随就近父级 UIThemeApplier;本控件不自带 applier,避免嵌套重复应用
|
||||
|
||||
string path = $"{ControlsDir}/{PfPrompt}.prefab";
|
||||
PrefabUtility.SaveAsPrefabAsset(root, path);
|
||||
Object.DestroyImmediate(root);
|
||||
}
|
||||
|
||||
// ── 助手 ─────────────────────────────────────────────────────────────
|
||||
private static GameObject NewUI(string name, float w, float h)
|
||||
{
|
||||
var go = new GameObject(name, typeof(RectTransform));
|
||||
((RectTransform)go.transform).sizeDelta = new Vector2(w, h);
|
||||
return go;
|
||||
}
|
||||
|
||||
private static GameObject NewUIChild(Transform parent, string name, out RectTransform rt)
|
||||
{
|
||||
var go = new GameObject(name, typeof(RectTransform));
|
||||
rt = (RectTransform)go.transform;
|
||||
rt.SetParent(parent, false);
|
||||
return go;
|
||||
}
|
||||
|
||||
private static void SetEnum(Component c, string prop, int value)
|
||||
{
|
||||
var so = new SerializedObject(c);
|
||||
var p = so.FindProperty(prop);
|
||||
if (p != null) { p.enumValueIndex = value; so.ApplyModifiedPropertiesWithoutUndo(); }
|
||||
}
|
||||
|
||||
private static void SetString(Component c, string prop, string value)
|
||||
{
|
||||
var so = new SerializedObject(c);
|
||||
var p = so.FindProperty(prop);
|
||||
if (p != null) { p.stringValue = value; so.ApplyModifiedPropertiesWithoutUndo(); }
|
||||
}
|
||||
|
||||
private static Sprite Knob() => AssetDatabase.GetBuiltinExtraResource<Sprite>("UI/Skin/Knob.psd");
|
||||
|
||||
private static void EnsureFolder(string dir)
|
||||
{
|
||||
string[] parts = dir.Split('/');
|
||||
string cur = parts[0];
|
||||
for (int i = 1; i < parts.Length; i++)
|
||||
{
|
||||
string next = $"{cur}/{parts[i]}";
|
||||
if (!AssetDatabase.IsValidFolder(next)) AssetDatabase.CreateFolder(cur, parts[i]);
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user