feat: add world chat UI
This commit is contained in:
@@ -24,6 +24,7 @@ namespace XGame.EditorTools
|
||||
// 不能用相对路径(Unity 工作目录是工程目录),需基于 dataPath 解析为绝对路径。
|
||||
private const string JsonRootRelative = "Doc/UIPrefabCreater";
|
||||
private const string TextureRoot = "Assets/Game/Art/UI/Texture";
|
||||
private static readonly int UiLayer = LayerMask.NameToLayer("UI");
|
||||
private static readonly int[] RequiredRefResolution = { 2048, 1024 };
|
||||
|
||||
// 解析 JSON 根目录绝对路径:优先 仓库根/Doc/UIPrefabCreater,回退 工程根/Doc/UIPrefabCreater
|
||||
@@ -49,7 +50,7 @@ namespace XGame.EditorTools
|
||||
// 已识别但本期未实现的组件:遇到只告警、不报错(让 LoginPanelControls.json 仍能通过)
|
||||
private static readonly HashSet<string> KnownUnimplemented = new HashSet<string>
|
||||
{
|
||||
"Toggle", "TMP_InputField", "RotationAnimation"
|
||||
"Toggle", "RotationAnimation"
|
||||
};
|
||||
|
||||
[MenuItem("Tools/UI/Generate Prefab From JSON...")]
|
||||
@@ -258,6 +259,7 @@ namespace XGame.EditorTools
|
||||
string parentName = node["parent"]?.Type == JTokenType.Null ? null : (string)node["parent"];
|
||||
|
||||
GameObject go = new GameObject(name, typeof(RectTransform));
|
||||
ApplyUiLayer(go);
|
||||
map[name] = go;
|
||||
if (parentName == null)
|
||||
{
|
||||
@@ -534,6 +536,21 @@ namespace XGame.EditorTools
|
||||
// onClick 仅作事件标记,不在 Prefab 写死回调
|
||||
break;
|
||||
}
|
||||
case "TMP_InputField":
|
||||
{
|
||||
TMP_InputField input = GetOrAdd<TMP_InputField>(go);
|
||||
Image target = go.GetComponent<Image>();
|
||||
if (target != null) input.targetGraphic = target;
|
||||
if (comp["characterLimit"] != null) input.characterLimit = (int)comp["characterLimit"];
|
||||
input.contentType = ParseInputContentType((string)comp["contentType"]);
|
||||
input.lineType = ParseInputLineType((string)comp["lineType"]);
|
||||
|
||||
string textName = (string)comp["textComponent"];
|
||||
string placeholderName = (string)comp["placeholderComponent"];
|
||||
string placeholderText = (string)comp["placeholder"];
|
||||
deferred.Add(() => BindTmpInputField(go, input, map, textName, placeholderName, placeholderText));
|
||||
break;
|
||||
}
|
||||
case "Shadow":
|
||||
{
|
||||
Shadow shadow = GetOrAdd<Shadow>(go);
|
||||
@@ -630,7 +647,154 @@ namespace XGame.EditorTools
|
||||
}
|
||||
}
|
||||
|
||||
private static void BindTmpInputField(
|
||||
GameObject go,
|
||||
TMP_InputField input,
|
||||
Dictionary<string, GameObject> map,
|
||||
string textName,
|
||||
string placeholderName,
|
||||
string placeholderText)
|
||||
{
|
||||
if (go == null || input == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RectTransform textViewport = EnsureInputTextViewport(go);
|
||||
input.textViewport = textViewport;
|
||||
|
||||
TextMeshProUGUI text = ResolveOrCreateInputText(
|
||||
go,
|
||||
map,
|
||||
textName,
|
||||
textViewport,
|
||||
"Text",
|
||||
string.Empty,
|
||||
"#23384CFF");
|
||||
TextMeshProUGUI placeholder = ResolveOrCreateInputText(
|
||||
go,
|
||||
map,
|
||||
placeholderName,
|
||||
textViewport,
|
||||
"Placeholder",
|
||||
placeholderText ?? string.Empty,
|
||||
"#7A8DA1FF");
|
||||
|
||||
input.textComponent = text;
|
||||
input.placeholder = placeholder;
|
||||
if (placeholder != null && !string.IsNullOrEmpty(placeholderText))
|
||||
{
|
||||
placeholder.text = placeholderText;
|
||||
}
|
||||
}
|
||||
|
||||
private static RectTransform EnsureInputTextViewport(GameObject go)
|
||||
{
|
||||
Transform existing = go.transform.Find("Text Area");
|
||||
GameObject textArea = existing == null ? null : existing.gameObject;
|
||||
if (textArea == null)
|
||||
{
|
||||
textArea = new GameObject("Text Area", typeof(RectTransform), typeof(RectMask2D));
|
||||
ApplyUiLayer(textArea);
|
||||
textArea.transform.SetParent(go.transform, false);
|
||||
RectTransform rt = textArea.GetComponent<RectTransform>();
|
||||
rt.anchorMin = Vector2.zero;
|
||||
rt.anchorMax = Vector2.one;
|
||||
rt.pivot = new Vector2(0.5f, 0.5f);
|
||||
rt.offsetMin = new Vector2(24f, 0f);
|
||||
rt.offsetMax = new Vector2(-24f, 0f);
|
||||
}
|
||||
RectTransform viewport = textArea.GetComponent<RectTransform>();
|
||||
if (textArea.GetComponent<RectMask2D>() == null)
|
||||
{
|
||||
textArea.AddComponent<RectMask2D>();
|
||||
}
|
||||
return viewport;
|
||||
}
|
||||
|
||||
private static TextMeshProUGUI ResolveOrCreateInputText(
|
||||
GameObject inputRoot,
|
||||
Dictionary<string, GameObject> map,
|
||||
string nodeName,
|
||||
RectTransform viewport,
|
||||
string fallbackName,
|
||||
string defaultText,
|
||||
string defaultColor)
|
||||
{
|
||||
TextMeshProUGUI text = null;
|
||||
if (!string.IsNullOrEmpty(nodeName) && map.TryGetValue(nodeName, out GameObject mapped) && mapped != null)
|
||||
{
|
||||
text = mapped.GetComponent<TextMeshProUGUI>();
|
||||
if (text == null)
|
||||
{
|
||||
text = mapped.AddComponent<TextMeshProUGUI>();
|
||||
}
|
||||
if (mapped.transform.parent != viewport)
|
||||
{
|
||||
mapped.transform.SetParent(viewport, false);
|
||||
}
|
||||
StretchToFill(mapped.GetComponent<RectTransform>());
|
||||
}
|
||||
|
||||
if (text == null)
|
||||
{
|
||||
Transform existing = viewport.Find(fallbackName);
|
||||
GameObject textGo = existing == null ? null : existing.gameObject;
|
||||
if (textGo == null)
|
||||
{
|
||||
textGo = new GameObject(fallbackName, typeof(RectTransform));
|
||||
ApplyUiLayer(textGo);
|
||||
textGo.transform.SetParent(viewport, false);
|
||||
}
|
||||
StretchToFill(textGo.GetComponent<RectTransform>());
|
||||
text = GetOrAdd<TextMeshProUGUI>(textGo);
|
||||
text.text = defaultText ?? string.Empty;
|
||||
}
|
||||
|
||||
ConfigureInputText(text, defaultColor);
|
||||
return text;
|
||||
}
|
||||
|
||||
private static void ConfigureInputText(TextMeshProUGUI text, string color)
|
||||
{
|
||||
if (text == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (text.fontSize <= 0f)
|
||||
{
|
||||
text.fontSize = 28f;
|
||||
}
|
||||
text.alignment = text.alignment == TextAlignmentOptions.TopLeft ? TextAlignmentOptions.Left : text.alignment;
|
||||
text.raycastTarget = false;
|
||||
if (!string.IsNullOrEmpty(color))
|
||||
{
|
||||
text.color = ParseColor(color, text.color);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- 资源/解析辅助 ----------
|
||||
private static void StretchToFill(RectTransform rt)
|
||||
{
|
||||
if (rt == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
rt.anchorMin = Vector2.zero;
|
||||
rt.anchorMax = Vector2.one;
|
||||
rt.pivot = new Vector2(0.5f, 0.5f);
|
||||
rt.offsetMin = Vector2.zero;
|
||||
rt.offsetMax = Vector2.zero;
|
||||
}
|
||||
|
||||
private static void ApplyUiLayer(GameObject go)
|
||||
{
|
||||
if (go != null && UiLayer >= 0)
|
||||
{
|
||||
go.layer = UiLayer;
|
||||
}
|
||||
}
|
||||
|
||||
private static Type FindComponentType(string typeName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(typeName))
|
||||
@@ -739,6 +903,36 @@ namespace XGame.EditorTools
|
||||
}
|
||||
}
|
||||
|
||||
private static TMP_InputField.ContentType ParseInputContentType(string contentType)
|
||||
{
|
||||
switch (contentType)
|
||||
{
|
||||
case "IntegerNumber": return TMP_InputField.ContentType.IntegerNumber;
|
||||
case "DecimalNumber": return TMP_InputField.ContentType.DecimalNumber;
|
||||
case "Alphanumeric": return TMP_InputField.ContentType.Alphanumeric;
|
||||
case "Name": return TMP_InputField.ContentType.Name;
|
||||
case "EmailAddress": return TMP_InputField.ContentType.EmailAddress;
|
||||
case "Password": return TMP_InputField.ContentType.Password;
|
||||
case "Pin": return TMP_InputField.ContentType.Pin;
|
||||
case "Custom": return TMP_InputField.ContentType.Custom;
|
||||
case "Standard":
|
||||
default:
|
||||
return TMP_InputField.ContentType.Standard;
|
||||
}
|
||||
}
|
||||
|
||||
private static TMP_InputField.LineType ParseInputLineType(string lineType)
|
||||
{
|
||||
switch (lineType)
|
||||
{
|
||||
case "MultiLineSubmit": return TMP_InputField.LineType.MultiLineSubmit;
|
||||
case "MultiLineNewline": return TMP_InputField.LineType.MultiLineNewline;
|
||||
case "SingleLine":
|
||||
default:
|
||||
return TMP_InputField.LineType.SingleLine;
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<string> EnumStrings(JToken token)
|
||||
{
|
||||
if (token is JArray a)
|
||||
|
||||
Reference in New Issue
Block a user