galgame,编辑器内蓝图
This commit is contained in:
@@ -0,0 +1,625 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using System.Text;
|
||||
|
||||
[CustomEditor(typeof(AllyHero_SO))]
|
||||
public class AllyHero_SO_Editor : Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
if (GUILayout.Button("打开蓝图视图"))
|
||||
{
|
||||
AllyHeroBlueprintWindow.Open(target as AllyHero_SO);
|
||||
}
|
||||
EditorGUILayout.Space();
|
||||
DrawDefaultInspector();
|
||||
}
|
||||
}
|
||||
|
||||
public class AllyHeroBlueprintWindow : EditorWindow
|
||||
{
|
||||
private AllyHero_SO currentSo;
|
||||
private AllyHeroGraphView graphView;
|
||||
private ObjectField soField;
|
||||
|
||||
public static void Open(AllyHero_SO so)
|
||||
{
|
||||
var window = GetWindow<AllyHeroBlueprintWindow>("AllyHero 蓝图");
|
||||
window.Show();
|
||||
window.SetSo(so);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
CreateGraphView();
|
||||
CreateToolbar();
|
||||
if (currentSo == null)
|
||||
{
|
||||
currentSo = Selection.activeObject as AllyHero_SO;
|
||||
}
|
||||
SetSo(currentSo);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
rootVisualElement.Clear();
|
||||
graphView = null;
|
||||
}
|
||||
|
||||
private void CreateGraphView()
|
||||
{
|
||||
graphView = new AllyHeroGraphView();
|
||||
graphView.StretchToParentSize();
|
||||
rootVisualElement.Add(graphView);
|
||||
}
|
||||
|
||||
private void CreateToolbar()
|
||||
{
|
||||
var toolbar = new Toolbar();
|
||||
soField = new ObjectField("数据") { objectType = typeof(AllyHero_SO), allowSceneObjects = false };
|
||||
soField.RegisterValueChangedCallback(evt => SetSo(evt.newValue as AllyHero_SO));
|
||||
toolbar.Add(soField);
|
||||
var refreshBtn = new ToolbarButton(RefreshGraph) { text = "刷新" };
|
||||
toolbar.Add(refreshBtn);
|
||||
rootVisualElement.Add(toolbar);
|
||||
}
|
||||
|
||||
private void SetSo(AllyHero_SO so)
|
||||
{
|
||||
currentSo = so;
|
||||
if (soField != null && soField.value != so)
|
||||
{
|
||||
soField.SetValueWithoutNotify(so);
|
||||
}
|
||||
RefreshGraph();
|
||||
}
|
||||
|
||||
private void RefreshGraph()
|
||||
{
|
||||
if (graphView == null) return;
|
||||
graphView.Build(currentSo);
|
||||
}
|
||||
}
|
||||
|
||||
class AllyHeroGraphView : GraphView
|
||||
{
|
||||
public AllyHeroGraphView()
|
||||
{
|
||||
this.AddManipulator(new ContentZoomer());
|
||||
this.AddManipulator(new ContentDragger());
|
||||
this.AddManipulator(new SelectionDragger());
|
||||
this.AddManipulator(new RectangleSelector());
|
||||
var grid = new GridBackground();
|
||||
Insert(0, grid);
|
||||
grid.StretchToParentSize();
|
||||
SetupZoom(ContentZoomer.DefaultMinScale, ContentZoomer.DefaultMaxScale);
|
||||
}
|
||||
|
||||
public void Build(AllyHero_SO so)
|
||||
{
|
||||
ClearGraph();
|
||||
if (so == null) return;
|
||||
|
||||
var basicNode = CreateNode("基础信息", BuildBasicInfo(so));
|
||||
basicNode.SetPosition(new Rect(40, 80, 360, 340));
|
||||
AddElement(basicNode);
|
||||
|
||||
var levelNode = CreateNode("等级信息", BuildLevelInfoMerged(so));
|
||||
levelNode.SetPosition(new Rect(420, 80, 480, 460));
|
||||
AddElement(levelNode);
|
||||
|
||||
var skillNode = CreateNode("技能配置", BuildSkillOverview(so));
|
||||
skillNode.SetPosition(new Rect(40, 500, 520, 220));
|
||||
AddElement(skillNode);
|
||||
AddElement(basicNode.output.ConnectTo(levelNode.input));
|
||||
AddElement(levelNode.output.ConnectTo(skillNode.input));
|
||||
|
||||
var skillCards = BuildSkillCards(so, skillNode);
|
||||
if (skillCards != null)
|
||||
{
|
||||
foreach (var card in skillCards)
|
||||
{
|
||||
AddElement(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearGraph()
|
||||
{
|
||||
DeleteElements(graphElements);
|
||||
}
|
||||
|
||||
private AllyHeroNode CreateNode(string title, VisualElement content)
|
||||
{
|
||||
var node = new AllyHeroNode();
|
||||
node.title = title;
|
||||
node.style.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.95f);
|
||||
node.style.borderTopWidth = 1;
|
||||
node.style.borderBottomWidth = 1;
|
||||
node.style.borderLeftWidth = 1;
|
||||
node.style.borderRightWidth = 1;
|
||||
node.style.borderTopColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
node.style.borderBottomColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
node.style.borderLeftColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
node.style.borderRightColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
node.titleContainer.style.backgroundColor = new Color(0.14f, 0.14f, 0.14f, 1f);
|
||||
node.titleContainer.style.paddingLeft = 8;
|
||||
node.titleContainer.style.paddingRight = 8;
|
||||
node.titleContainer.style.paddingTop = 4;
|
||||
node.titleContainer.style.paddingBottom = 4;
|
||||
node.input = node.InstantiatePort(Orientation.Horizontal, Direction.Input, Port.Capacity.Multi, typeof(float));
|
||||
node.output = node.InstantiatePort(Orientation.Horizontal, Direction.Output, Port.Capacity.Single, typeof(float));
|
||||
node.input.portName = "";
|
||||
node.output.portName = "";
|
||||
node.inputContainer.Add(node.input);
|
||||
node.outputContainer.Add(node.output);
|
||||
|
||||
if (content != null)
|
||||
{
|
||||
content.style.marginLeft = 6;
|
||||
content.style.marginRight = 6;
|
||||
content.style.marginTop = 4;
|
||||
content.style.marginBottom = 4;
|
||||
node.extensionContainer.Add(content);
|
||||
}
|
||||
|
||||
node.RefreshExpandedState();
|
||||
node.RefreshPorts();
|
||||
return node;
|
||||
}
|
||||
|
||||
private VisualElement BuildBasicInfo(AllyHero_SO so)
|
||||
{
|
||||
var container = new VisualElement();
|
||||
container.style.flexDirection = FlexDirection.Column;
|
||||
|
||||
container.Add(CreateTitleLabel($"名称: {so.ally_heroName}"));
|
||||
container.Add(CreatePrimaryLabel($"称号: {so.ally_heroDesignation}"));
|
||||
container.Add(CreatePrimaryLabel($"ID: {so.ally_heroID}"));
|
||||
container.Add(CreateMutedLabel($"主题色: {so.ally_heroThemeColor}"));
|
||||
container.Add(CreatePrimaryLabel($"当前经验: {so.ally_currentEXP}"));
|
||||
if (!string.IsNullOrEmpty(so.ally_heroDescription))
|
||||
{
|
||||
container.Add(CreatePrimaryLabel("描述:"));
|
||||
var desc = CreateMutedLabel(WrapText(so.ally_heroDescription, 24));
|
||||
desc.style.whiteSpace = WhiteSpace.Normal;
|
||||
container.Add(desc);
|
||||
}
|
||||
|
||||
container.Add(CreatePrimaryLabel("缩略图:"));
|
||||
var thumbRow = new VisualElement();
|
||||
thumbRow.style.flexDirection = FlexDirection.Row;
|
||||
thumbRow.style.flexWrap = Wrap.Wrap;
|
||||
thumbRow.Add(CreateSpriteThumb("形象", so.ally_heroImage));
|
||||
thumbRow.Add(CreateSpriteThumb("头像", so.ally_heroProfile));
|
||||
thumbRow.Add(CreateSpriteThumb("选择图标", so.ally_heroSelectIcon));
|
||||
thumbRow.Add(CreateSpriteThumb("图标", so.ally_heroIcon));
|
||||
thumbRow.Add(CreateSpriteThumb("海报", so.ally_heroPoster));
|
||||
thumbRow.Add(CreateSpriteThumb("高清图", so.ally_hero_HD_image));
|
||||
thumbRow.Add(CreateSpriteThumb("方形头像", so.ally_hero_squareProfile));
|
||||
container.Add(thumbRow);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement BuildLevelInfoMerged(AllyHero_SO so)
|
||||
{
|
||||
var container = new VisualElement();
|
||||
container.style.flexDirection = FlexDirection.Column;
|
||||
|
||||
if (so.levelStats == null || so.levelStats.Count == 0)
|
||||
{
|
||||
container.Add(CreateLineLabel("空"));
|
||||
return container;
|
||||
}
|
||||
|
||||
int currentIndex = GetCurrentLevelIndex(so);
|
||||
var levelNames = new string[so.levelStats.Count];
|
||||
var levelIds = new string[so.levelStats.Count];
|
||||
var attacks = new string[so.levelStats.Count];
|
||||
var maxHPs = new string[so.levelStats.Count];
|
||||
var resistances = new string[so.levelStats.Count];
|
||||
var maxManas = new string[so.levelStats.Count];
|
||||
var scoreEffs = new string[so.levelStats.Count];
|
||||
var reqExps = new string[so.levelStats.Count];
|
||||
var slotLimits = new string[so.levelStats.Count];
|
||||
var manaGains = new string[so.levelStats.Count];
|
||||
var dmgMultis = new string[so.levelStats.Count];
|
||||
var missLoss = new string[so.levelStats.Count];
|
||||
|
||||
for (int i = 0; i < so.levelStats.Count; i++)
|
||||
{
|
||||
var lvl = so.levelStats[i];
|
||||
if (lvl == null) continue;
|
||||
levelNames[i] = lvl.levelName;
|
||||
levelIds[i] = lvl.levelID.ToString();
|
||||
attacks[i] = lvl.attack.ToString();
|
||||
maxHPs[i] = lvl.maxHP.ToString();
|
||||
resistances[i] = FormatNumber(lvl.damageResistance);
|
||||
maxManas[i] = lvl.maxMana.ToString();
|
||||
scoreEffs[i] = FormatNumber(lvl.scoreEfficiency);
|
||||
reqExps[i] = lvl.requiredEXP.ToString();
|
||||
slotLimits[i] = lvl.skill_slot_limited.ToString();
|
||||
manaGains[i] = $"{lvl.manaGainGood}/{lvl.manaGainGreat}/{lvl.manaGainPerfect}/{lvl.manaGainOnMiss}";
|
||||
dmgMultis[i] = $"{FormatNumber(lvl.damageMultiplierGood)}/{FormatNumber(lvl.damageMultiplierGreat)}/{FormatNumber(lvl.damageMultiplierPerfect)}";
|
||||
missLoss[i] = FormatNumber(lvl.missHpLossBase);
|
||||
}
|
||||
|
||||
AddSlashRow(container, "等级名", levelNames, currentIndex);
|
||||
AddSlashRow(container, "等级ID", levelIds, currentIndex);
|
||||
AddSlashRow(container, "攻击", attacks, currentIndex);
|
||||
AddSlashRow(container, "最大生命", maxHPs, currentIndex);
|
||||
AddSlashRow(container, "减伤", resistances, currentIndex);
|
||||
AddSlashRow(container, "最大法力", maxManas, currentIndex);
|
||||
AddSlashRow(container, "偶像分效率", scoreEffs, currentIndex);
|
||||
AddSlashRow(container, "所需经验", reqExps, currentIndex);
|
||||
AddSlashRow(container, "技能槽上限", slotLimits, currentIndex);
|
||||
AddSlashRow(container, "回蓝 Good/Great/Perfect/Miss", manaGains, currentIndex);
|
||||
AddSlashRow(container, "伤害倍率 Good/Great/Perfect", dmgMultis, currentIndex);
|
||||
AddSlashRow(container, "Miss 基础扣血", missLoss, currentIndex);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement BuildSkillOverview(AllyHero_SO so)
|
||||
{
|
||||
var container = new VisualElement();
|
||||
container.style.flexDirection = FlexDirection.Column;
|
||||
|
||||
container.Add(CreatePrimaryLabel("概览"));
|
||||
container.Add(CreateMutedLabel($"可用技能数: {(so.availableSkills == null ? 0 : so.availableSkills.Length)}"));
|
||||
container.Add(CreateMutedLabel($"主技能索引: {so.primarySkillIndex}"));
|
||||
container.Add(CreateMutedLabel($"已选技能索引: {JoinInts(so.selectedSkillIndices)}"));
|
||||
container.Add(CreateMutedLabel($"技能组数量: {(so.skillGroups == null ? 0 : so.skillGroups.Length)}"));
|
||||
container.Add(CreateMutedLabel($"已装备技能组ID: {JoinInts(so.equippedSkillGroupIDs)}"));
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private string GetSpriteName(Sprite sprite)
|
||||
{
|
||||
return sprite != null ? sprite.name : "(无)";
|
||||
}
|
||||
|
||||
private string GetSkillName(SkillDefinition def)
|
||||
{
|
||||
if (def == null) return "(无)";
|
||||
var name = string.IsNullOrEmpty(def.displayName) ? def.name : def.displayName;
|
||||
var id = string.IsNullOrEmpty(def.skillId) ? "-" : def.skillId;
|
||||
return $"{name} ({id})";
|
||||
}
|
||||
|
||||
private string JoinInts(int[] arr)
|
||||
{
|
||||
if (arr == null || arr.Length == 0) return "(无)";
|
||||
return string.Join(",", arr);
|
||||
}
|
||||
|
||||
private Label CreateLineLabel(string text)
|
||||
{
|
||||
return CreateLineLabel(text, 11, new Color(0.75f, 0.75f, 0.75f), false);
|
||||
}
|
||||
|
||||
private Label CreateLineLabel(string text, int fontSize, Color? color, bool bold)
|
||||
{
|
||||
var label = new Label(text ?? "");
|
||||
label.style.whiteSpace = WhiteSpace.Normal;
|
||||
label.style.unityTextAlign = TextAnchor.UpperLeft;
|
||||
label.style.fontSize = fontSize;
|
||||
if (bold) label.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
if (color.HasValue) label.style.color = color.Value;
|
||||
return label;
|
||||
}
|
||||
|
||||
private Label CreateTitleLabel(string text)
|
||||
{
|
||||
return CreateLineLabel(text, 14, new Color(0.95f, 0.95f, 0.95f), true);
|
||||
}
|
||||
|
||||
private Label CreatePrimaryLabel(string text)
|
||||
{
|
||||
return CreateLineLabel(text, 12, new Color(0.85f, 0.85f, 0.85f), true);
|
||||
}
|
||||
|
||||
private Label CreateMutedLabel(string text)
|
||||
{
|
||||
return CreateLineLabel(text, 11, new Color(0.7f, 0.7f, 0.7f), false);
|
||||
}
|
||||
|
||||
private VisualElement CreateSpriteThumb(string label, Sprite sprite)
|
||||
{
|
||||
var container = new VisualElement();
|
||||
container.style.flexDirection = FlexDirection.Column;
|
||||
container.style.width = 88;
|
||||
container.style.marginRight = 6;
|
||||
container.style.marginBottom = 6;
|
||||
|
||||
var imageBox = new VisualElement();
|
||||
imageBox.style.width = 80;
|
||||
imageBox.style.height = 60;
|
||||
imageBox.style.borderTopWidth = 1;
|
||||
imageBox.style.borderBottomWidth = 1;
|
||||
imageBox.style.borderLeftWidth = 1;
|
||||
imageBox.style.borderRightWidth = 1;
|
||||
imageBox.style.borderTopColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
imageBox.style.borderBottomColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
imageBox.style.borderLeftColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
imageBox.style.borderRightColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
|
||||
var img = new Image();
|
||||
img.image = sprite != null ? sprite.texture : null;
|
||||
img.scaleMode = ScaleMode.ScaleToFit;
|
||||
img.style.width = 80;
|
||||
img.style.height = 60;
|
||||
imageBox.Add(img);
|
||||
|
||||
var nameLabel = new Label(string.IsNullOrEmpty(label) ? GetSpriteName(sprite) : $"{label}\n{GetSpriteName(sprite)}");
|
||||
nameLabel.style.whiteSpace = WhiteSpace.Normal;
|
||||
nameLabel.style.fontSize = 9;
|
||||
nameLabel.style.unityTextAlign = TextAnchor.UpperCenter;
|
||||
|
||||
container.Add(imageBox);
|
||||
container.Add(nameLabel);
|
||||
return container;
|
||||
}
|
||||
|
||||
private void AddSlashRow(VisualElement container, string title, string[] values, int highlightIndex)
|
||||
{
|
||||
var row = new VisualElement();
|
||||
row.style.flexDirection = FlexDirection.Row;
|
||||
row.style.flexWrap = Wrap.Wrap;
|
||||
row.style.alignItems = Align.Center;
|
||||
row.style.marginBottom = 2;
|
||||
|
||||
var titleLabel = new Label($"{title}: ");
|
||||
titleLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
titleLabel.style.fontSize = 12;
|
||||
titleLabel.style.color = new Color(0.85f, 0.85f, 0.85f);
|
||||
row.Add(titleLabel);
|
||||
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
row.Add(new Label("/"));
|
||||
}
|
||||
var valueText = string.IsNullOrEmpty(values[i]) ? "-" : values[i];
|
||||
var valueLabel = new Label(valueText);
|
||||
valueLabel.style.fontSize = 11;
|
||||
valueLabel.style.color = new Color(0.75f, 0.75f, 0.75f);
|
||||
if (i == highlightIndex)
|
||||
{
|
||||
valueLabel.style.color = new Color(0.25f, 0.6f, 1f);
|
||||
valueLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
}
|
||||
row.Add(valueLabel);
|
||||
}
|
||||
|
||||
container.Add(row);
|
||||
}
|
||||
|
||||
private int GetCurrentLevelIndex(AllyHero_SO so)
|
||||
{
|
||||
if (so.levelStats == null || so.levelStats.Count == 0) return -1;
|
||||
int bestIndex = -1;
|
||||
int currentExp = so.ally_currentEXP;
|
||||
for (int i = 0; i < so.levelStats.Count; i++)
|
||||
{
|
||||
var lvl = so.levelStats[i];
|
||||
if (lvl == null) continue;
|
||||
if (bestIndex < 0)
|
||||
{
|
||||
bestIndex = i;
|
||||
if (currentExp < lvl.requiredEXP) continue;
|
||||
}
|
||||
var best = so.levelStats[bestIndex];
|
||||
if (best == null) continue;
|
||||
if (currentExp >= lvl.requiredEXP && lvl.requiredEXP >= best.requiredEXP)
|
||||
{
|
||||
bestIndex = i;
|
||||
}
|
||||
}
|
||||
return bestIndex < 0 ? 0 : bestIndex;
|
||||
}
|
||||
|
||||
private string FormatNumber(float value)
|
||||
{
|
||||
return value.ToString("0.###");
|
||||
}
|
||||
|
||||
private System.Collections.Generic.HashSet<int> BuildEquippedSet(int[] ids)
|
||||
{
|
||||
var set = new System.Collections.Generic.HashSet<int>();
|
||||
if (ids == null) return set;
|
||||
for (int i = 0; i < ids.Length; i++)
|
||||
{
|
||||
set.Add(ids[i]);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
private string GetSkillEffectSummary(SkillDefinition def)
|
||||
{
|
||||
if (def == null) return "(无)";
|
||||
if (!string.IsNullOrEmpty(def.summaryInfo)) return def.summaryInfo;
|
||||
if (!string.IsNullOrEmpty(def.description)) return def.description;
|
||||
if (!string.IsNullOrEmpty(def.formula)) return def.formula;
|
||||
return "(无)";
|
||||
}
|
||||
|
||||
private System.Collections.Generic.List<AllyHeroNode> BuildSkillCards(AllyHero_SO so, AllyHeroNode rootNode)
|
||||
{
|
||||
if (so == null || so.skillGroups == null || so.skillGroups.Length == 0) return null;
|
||||
var list = new System.Collections.Generic.List<AllyHeroNode>();
|
||||
int currentLevelNumber = GetCurrentLevelIndex(so) + 1;
|
||||
var equippedSet = BuildEquippedSet(so.equippedSkillGroupIDs);
|
||||
|
||||
float startX = 580f;
|
||||
float startY = 500f;
|
||||
float cardW = 300f;
|
||||
float cardH = 180f;
|
||||
float gapX = 26f;
|
||||
float gapY = 22f;
|
||||
int cols = 2;
|
||||
int index = 0;
|
||||
|
||||
for (int i = 0; i < so.skillGroups.Length; i++)
|
||||
{
|
||||
var g = so.skillGroups[i];
|
||||
if (g == null) continue;
|
||||
bool equipped = equippedSet.Contains(g.skillGroupID);
|
||||
bool unlocked = currentLevelNumber >= g.thisSkill_levelLimit;
|
||||
|
||||
if (g.skills == null || g.skills.Length == 0)
|
||||
{
|
||||
var node = CreateNode(g.groupName, BuildSkillCard(g, null, equipped, unlocked));
|
||||
node.SetPosition(new Rect(startX + (index % cols) * (cardW + gapX), startY + (index / cols) * (cardH + gapY), cardW, cardH));
|
||||
AddElement(rootNode.output.ConnectTo(node.input));
|
||||
list.Add(node);
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int s = 0; s < g.skills.Length; s++)
|
||||
{
|
||||
var sd = g.skills[s];
|
||||
var node = CreateNode(g.groupName, BuildSkillCard(g, sd, equipped, unlocked));
|
||||
node.SetPosition(new Rect(startX + (index % cols) * (cardW + gapX), startY + (index / cols) * (cardH + gapY), cardW, cardH));
|
||||
AddElement(rootNode.output.ConnectTo(node.input));
|
||||
list.Add(node);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private VisualElement BuildSkillCard(SkillGroup group, SkillDefinition skill, bool equipped, bool unlocked)
|
||||
{
|
||||
var container = new VisualElement();
|
||||
container.style.flexDirection = FlexDirection.Row;
|
||||
container.style.flexWrap = Wrap.NoWrap;
|
||||
|
||||
var accent = new VisualElement();
|
||||
accent.style.width = 4;
|
||||
accent.style.marginRight = 6;
|
||||
accent.style.backgroundColor = equipped ? new Color(0.25f, 0.6f, 1f) : (unlocked ? new Color(0.2f, 0.8f, 0.35f) : new Color(0.95f, 0.3f, 0.3f));
|
||||
container.Add(accent);
|
||||
|
||||
var body = new VisualElement();
|
||||
body.style.flexDirection = FlexDirection.Column;
|
||||
|
||||
var status = equipped ? "已装备" : (unlocked ? "已解锁" : "未解锁");
|
||||
var header = CreatePrimaryLabel($"{group.groupName} {status} 等级限制:{group.thisSkill_levelLimit}");
|
||||
header.style.fontSize = 12;
|
||||
header.style.color = equipped ? new Color(0.25f, 0.6f, 1f) : (unlocked ? new Color(0.2f, 0.8f, 0.35f) : new Color(0.95f, 0.3f, 0.3f));
|
||||
body.Add(header);
|
||||
|
||||
var sub = CreateMutedLabel($"ID:{group.skillGroupID} 列:{group.columnID}-{group.columnName}");
|
||||
body.Add(sub);
|
||||
|
||||
if (!string.IsNullOrEmpty(group.skillDescriptionsText))
|
||||
{
|
||||
var desc = CreateMutedLabel(WrapText(group.skillDescriptionsText, 22));
|
||||
desc.style.whiteSpace = WhiteSpace.Normal;
|
||||
body.Add(desc);
|
||||
}
|
||||
|
||||
if (skill != null)
|
||||
{
|
||||
var name = CreatePrimaryLabel(GetSkillName(skill));
|
||||
name.style.fontSize = 12;
|
||||
body.Add(name);
|
||||
var effect = CreateMutedLabel(WrapText(GetSkillEffectSummary(skill), 22));
|
||||
effect.style.whiteSpace = WhiteSpace.Normal;
|
||||
body.Add(effect);
|
||||
}
|
||||
|
||||
container.Add(body);
|
||||
return container;
|
||||
}
|
||||
|
||||
private string WrapText(string text, int maxCharsPerLine)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text) || maxCharsPerLine <= 0) return text;
|
||||
var sb = new System.Text.StringBuilder();
|
||||
var lines = text.Split('\n');
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
var line = lines[i];
|
||||
int count = 0;
|
||||
for (int c = 0; c < line.Length; c++)
|
||||
{
|
||||
sb.Append(line[c]);
|
||||
count++;
|
||||
if (count >= maxCharsPerLine && c < line.Length - 1)
|
||||
{
|
||||
sb.Append('\n');
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
if (i < lines.Length - 1) sb.Append('\n');
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private VisualElement CreateCard()
|
||||
{
|
||||
var card = new VisualElement();
|
||||
card.style.backgroundColor = new Color(0.12f, 0.12f, 0.12f, 0.6f);
|
||||
card.style.borderTopWidth = 1;
|
||||
card.style.borderBottomWidth = 1;
|
||||
card.style.borderLeftWidth = 1;
|
||||
card.style.borderRightWidth = 1;
|
||||
card.style.borderTopColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
card.style.borderBottomColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
card.style.borderLeftColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
card.style.borderRightColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
card.style.paddingLeft = 6;
|
||||
card.style.paddingRight = 6;
|
||||
card.style.paddingTop = 6;
|
||||
card.style.paddingBottom = 6;
|
||||
card.style.marginTop = 6;
|
||||
return card;
|
||||
}
|
||||
|
||||
private VisualElement CreateSkillChip(SkillDefinition def)
|
||||
{
|
||||
var chip = new VisualElement();
|
||||
chip.style.backgroundColor = new Color(0.18f, 0.18f, 0.18f, 0.9f);
|
||||
chip.style.borderTopWidth = 1;
|
||||
chip.style.borderBottomWidth = 1;
|
||||
chip.style.borderLeftWidth = 1;
|
||||
chip.style.borderRightWidth = 1;
|
||||
chip.style.borderTopColor = new Color(0.25f, 0.25f, 0.25f);
|
||||
chip.style.borderBottomColor = new Color(0.25f, 0.25f, 0.25f);
|
||||
chip.style.borderLeftColor = new Color(0.25f, 0.25f, 0.25f);
|
||||
chip.style.borderRightColor = new Color(0.25f, 0.25f, 0.25f);
|
||||
chip.style.paddingLeft = 6;
|
||||
chip.style.paddingRight = 6;
|
||||
chip.style.paddingTop = 4;
|
||||
chip.style.paddingBottom = 4;
|
||||
chip.style.marginRight = 6;
|
||||
chip.style.marginBottom = 6;
|
||||
chip.style.maxWidth = 300;
|
||||
|
||||
var name = CreatePrimaryLabel(GetSkillName(def));
|
||||
name.style.fontSize = 12;
|
||||
var effect = CreateMutedLabel(GetSkillEffectSummary(def));
|
||||
effect.style.whiteSpace = WhiteSpace.Normal;
|
||||
chip.Add(name);
|
||||
chip.Add(effect);
|
||||
return chip;
|
||||
}
|
||||
}
|
||||
|
||||
class AllyHeroNode : Node
|
||||
{
|
||||
public Port input;
|
||||
public Port output;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 46f5c89b016a1254fbdae28aa0c4fd49
|
||||
@@ -0,0 +1,484 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
[CustomEditor(typeof(SongData))]
|
||||
public class SongData_Editor : Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
if (GUILayout.Button("打开蓝图视图"))
|
||||
{
|
||||
SongDataBlueprintWindow.Open(target as SongData);
|
||||
}
|
||||
EditorGUILayout.Space();
|
||||
DrawDefaultInspector();
|
||||
}
|
||||
}
|
||||
|
||||
public class SongDataBlueprintWindow : EditorWindow
|
||||
{
|
||||
private SongData currentSo;
|
||||
private SongDataGraphView graphView;
|
||||
private ObjectField soField;
|
||||
|
||||
public static void Open(SongData so)
|
||||
{
|
||||
var window = GetWindow<SongDataBlueprintWindow>("SongData 蓝图");
|
||||
window.Show();
|
||||
window.SetSo(so);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
CreateGraphView();
|
||||
CreateToolbar();
|
||||
if (currentSo == null)
|
||||
{
|
||||
currentSo = Selection.activeObject as SongData;
|
||||
}
|
||||
SetSo(currentSo);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
rootVisualElement.Clear();
|
||||
graphView = null;
|
||||
}
|
||||
|
||||
private void CreateGraphView()
|
||||
{
|
||||
graphView = new SongDataGraphView();
|
||||
graphView.StretchToParentSize();
|
||||
rootVisualElement.Add(graphView);
|
||||
}
|
||||
|
||||
private void CreateToolbar()
|
||||
{
|
||||
var toolbar = new Toolbar();
|
||||
soField = new ObjectField("数据") { objectType = typeof(SongData), allowSceneObjects = false };
|
||||
soField.RegisterValueChangedCallback(evt => SetSo(evt.newValue as SongData));
|
||||
toolbar.Add(soField);
|
||||
var refreshBtn = new ToolbarButton(RefreshGraph) { text = "刷新" };
|
||||
toolbar.Add(refreshBtn);
|
||||
rootVisualElement.Add(toolbar);
|
||||
}
|
||||
|
||||
private void SetSo(SongData so)
|
||||
{
|
||||
currentSo = so;
|
||||
if (soField != null && soField.value != so)
|
||||
{
|
||||
soField.SetValueWithoutNotify(so);
|
||||
}
|
||||
RefreshGraph();
|
||||
}
|
||||
|
||||
private void RefreshGraph()
|
||||
{
|
||||
if (graphView == null) return;
|
||||
graphView.Build(currentSo);
|
||||
}
|
||||
}
|
||||
|
||||
class SongDataGraphView : GraphView
|
||||
{
|
||||
public SongDataGraphView()
|
||||
{
|
||||
this.AddManipulator(new ContentZoomer());
|
||||
this.AddManipulator(new ContentDragger());
|
||||
this.AddManipulator(new SelectionDragger());
|
||||
this.AddManipulator(new RectangleSelector());
|
||||
var grid = new GridBackground();
|
||||
Insert(0, grid);
|
||||
grid.StretchToParentSize();
|
||||
SetupZoom(ContentZoomer.DefaultMinScale, ContentZoomer.DefaultMaxScale);
|
||||
}
|
||||
|
||||
public void Build(SongData so)
|
||||
{
|
||||
ClearGraph();
|
||||
if (so == null) return;
|
||||
|
||||
var basicNode = CreateNode("基础信息", BuildBasicInfo(so));
|
||||
basicNode.SetPosition(new Rect(40, 80, 420, 360));
|
||||
AddElement(basicNode);
|
||||
|
||||
var mediaNode = CreateNode("媒体资源", BuildMediaInfo(so));
|
||||
mediaNode.SetPosition(new Rect(480, 80, 520, 360));
|
||||
AddElement(mediaNode);
|
||||
AddElement(basicNode.output.ConnectTo(mediaNode.input));
|
||||
|
||||
var chartsNode = CreateNode("谱面/难度概览", BuildChartsOverview(so));
|
||||
chartsNode.SetPosition(new Rect(40, 470, 620, 260));
|
||||
AddElement(chartsNode);
|
||||
AddElement(mediaNode.output.ConnectTo(chartsNode.input));
|
||||
|
||||
var chartNodes = BuildChartCards(so, chartsNode);
|
||||
if (chartNodes != null)
|
||||
{
|
||||
foreach (var n in chartNodes)
|
||||
{
|
||||
AddElement(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearGraph()
|
||||
{
|
||||
DeleteElements(graphElements);
|
||||
}
|
||||
|
||||
private SongDataNode CreateNode(string title, VisualElement content)
|
||||
{
|
||||
var node = new SongDataNode();
|
||||
node.title = title;
|
||||
node.style.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.95f);
|
||||
node.style.borderTopWidth = 1;
|
||||
node.style.borderBottomWidth = 1;
|
||||
node.style.borderLeftWidth = 1;
|
||||
node.style.borderRightWidth = 1;
|
||||
node.style.borderTopColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
node.style.borderBottomColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
node.style.borderLeftColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
node.style.borderRightColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
node.titleContainer.style.backgroundColor = new Color(0.14f, 0.14f, 0.14f, 1f);
|
||||
node.titleContainer.style.paddingLeft = 8;
|
||||
node.titleContainer.style.paddingRight = 8;
|
||||
node.titleContainer.style.paddingTop = 4;
|
||||
node.titleContainer.style.paddingBottom = 4;
|
||||
node.input = node.InstantiatePort(Orientation.Horizontal, Direction.Input, Port.Capacity.Multi, typeof(float));
|
||||
node.output = node.InstantiatePort(Orientation.Horizontal, Direction.Output, Port.Capacity.Single, typeof(float));
|
||||
node.input.portName = "";
|
||||
node.output.portName = "";
|
||||
node.inputContainer.Add(node.input);
|
||||
node.outputContainer.Add(node.output);
|
||||
|
||||
if (content != null)
|
||||
{
|
||||
content.style.marginLeft = 6;
|
||||
content.style.marginRight = 6;
|
||||
content.style.marginTop = 4;
|
||||
content.style.marginBottom = 4;
|
||||
node.extensionContainer.Add(content);
|
||||
}
|
||||
|
||||
node.RefreshExpandedState();
|
||||
node.RefreshPorts();
|
||||
return node;
|
||||
}
|
||||
|
||||
private VisualElement BuildBasicInfo(SongData so)
|
||||
{
|
||||
var container = new VisualElement();
|
||||
container.style.flexDirection = FlexDirection.Column;
|
||||
|
||||
container.Add(CreateTitleLabel($"{Safe(so.songName)}"));
|
||||
container.Add(CreatePrimaryLabel($"ID: {so.songID}"));
|
||||
container.Add(CreateMutedLabel($"用户: {Safe(so.usernameID)}"));
|
||||
container.Add(CreatePrimaryLabel($"艺术家: {Safe(so.artistName)}"));
|
||||
container.Add(CreateMutedLabel($"曲绘: {Safe(so.painter)}"));
|
||||
container.Add(CreateMutedLabel($"谱师: {Safe(so.level_creator)}"));
|
||||
container.Add(CreateMutedLabel($"DLC: {Safe(so.belongsTo_whichDLC)}"));
|
||||
container.Add(CreatePrimaryLabel($"BPM: {so.bpm} 时长(s): {so.songLength_second} Duration: {so.songDuration:0.###}"));
|
||||
|
||||
container.Add(CreateMutedLabel($"进入次数: {so.game_enterTimes} 总游玩时长: {so.time_totalPlayingTime:0.###}"));
|
||||
container.Add(CreateMutedLabel($"上传时间: {FormatDateTime(so.uploadData)}"));
|
||||
|
||||
if (!string.IsNullOrEmpty(so.thisLevel_addDateString))
|
||||
{
|
||||
container.Add(CreateMutedLabel($"添加日期: {so.thisLevel_addDateString}"));
|
||||
}
|
||||
|
||||
container.Add(CreateMutedLabel($"当前选择难度ID: {so.thisLevel_selectedDifficultyID}"));
|
||||
|
||||
if (!string.IsNullOrEmpty(so.thisLevel_overallDescription))
|
||||
{
|
||||
container.Add(CreatePrimaryLabel("总体描述:"));
|
||||
var desc = CreateMutedLabel(WrapText(so.thisLevel_overallDescription, 26));
|
||||
desc.style.whiteSpace = WhiteSpace.Normal;
|
||||
container.Add(desc);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement BuildMediaInfo(SongData so)
|
||||
{
|
||||
var container = new VisualElement();
|
||||
container.style.flexDirection = FlexDirection.Column;
|
||||
|
||||
container.Add(CreatePrimaryLabel("图片:"));
|
||||
var thumbs = new VisualElement();
|
||||
thumbs.style.flexDirection = FlexDirection.Row;
|
||||
thumbs.style.flexWrap = Wrap.Wrap;
|
||||
thumbs.Add(CreateSpriteThumb("插画", so.illustration));
|
||||
thumbs.Add(CreateSpriteThumb("背景", so.backgroudPIC));
|
||||
thumbs.Add(CreateSpriteThumb("全屏图", so.fullscreen_songPicture));
|
||||
thumbs.Add(CreateSpriteThumb("卡片", so.card_profileImage));
|
||||
thumbs.Add(CreateSpriteThumb("右下图", so.right_pic_bottom_image));
|
||||
thumbs.Add(CreateSpriteThumb("右上图", so.right_pic_top_image));
|
||||
container.Add(thumbs);
|
||||
|
||||
container.Add(CreatePrimaryLabel("音频:"));
|
||||
container.Add(CreateMutedLabel($"主音频: {GetObjName(so.audioFile)}"));
|
||||
container.Add(CreateMutedLabel($"预览音频: {GetObjName(so.audio_previewAudio)}"));
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement BuildChartsOverview(SongData so)
|
||||
{
|
||||
var container = new VisualElement();
|
||||
container.style.flexDirection = FlexDirection.Column;
|
||||
|
||||
int chartCount = so.chartFiles == null ? 0 : so.chartFiles.Count;
|
||||
container.Add(CreatePrimaryLabel($"ChartFiles: {chartCount}"));
|
||||
|
||||
container.Add(CreateMutedLabel($"difficultyID: {so.difficultyID}"));
|
||||
container.Add(CreateMutedLabel($"personalRecord: {so.personalRecord}"));
|
||||
container.Add(CreateMutedLabel($"maxComboRecord: {so.max_comboRecord}"));
|
||||
container.Add(CreateMutedLabel($"levelProgress: {so.current_levelProgress:0.###} ifEase: {so._if_level_is_EASE}"));
|
||||
|
||||
container.Add(CreateMutedLabel($"difficultyNumberMap: {CountDict(so.difficultyNumberMap)}"));
|
||||
container.Add(CreateMutedLabel($"personalRecordMap: {CountDict(so.personalRecordMap)}"));
|
||||
container.Add(CreateMutedLabel($"idolRecordMap: {CountDict(so.idolRecordMap)}"));
|
||||
container.Add(CreateMutedLabel($"chartScoreMap: {CountDict(so.chartScoreMap)}"));
|
||||
container.Add(CreateMutedLabel($"levelProgressMap: {CountDict(so.levelProgressMap)}"));
|
||||
container.Add(CreateMutedLabel($"chartFileMap: {CountDict(so.chartFileMap)}"));
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private System.Collections.Generic.List<SongDataNode> BuildChartCards(SongData so, SongDataNode rootNode)
|
||||
{
|
||||
if (so == null || so.chartFiles == null || so.chartFiles.Count == 0) return null;
|
||||
|
||||
var list = new System.Collections.Generic.List<SongDataNode>();
|
||||
float startX = 680f;
|
||||
float startY = 470f;
|
||||
float cardW = 360f;
|
||||
float cardH = 240f;
|
||||
float gapX = 26f;
|
||||
float gapY = 22f;
|
||||
int cols = 2;
|
||||
int index = 0;
|
||||
|
||||
for (int i = 0; i < so.chartFiles.Count; i++)
|
||||
{
|
||||
var entry = so.chartFiles[i];
|
||||
if (entry == null) continue;
|
||||
var node = CreateNode($"难度 {entry.difficulty}", BuildChartEntry(entry));
|
||||
node.SetPosition(new Rect(startX + (index % cols) * (cardW + gapX), startY + (index / cols) * (cardH + gapY), cardW, cardH));
|
||||
AddElement(rootNode.output.ConnectTo(node.input));
|
||||
list.Add(node);
|
||||
index++;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private VisualElement BuildChartEntry(ChartFileEntry entry)
|
||||
{
|
||||
var container = new VisualElement();
|
||||
container.style.flexDirection = FlexDirection.Column;
|
||||
|
||||
container.Add(CreateTitleLabel($"{Safe(entry.difficultyName)} Lv:{entry.difficultyLEVEL:0.###}"));
|
||||
container.Add(CreateMutedLabel($"difficulty: {entry.difficulty}"));
|
||||
container.Add(CreateMutedLabel($"总血倍率: {entry.enemyTotalHP_Multiplier:0.###} 经验: {entry.experienceProvidedForThisDifficulty}"));
|
||||
|
||||
container.Add(CreateMutedLabel($"lastScore: {entry.lastScoreForThisDifficulty}"));
|
||||
container.Add(CreateMutedLabel($"PR: chart {entry.chartPersonalRecordForThisDifficulty} + idol {entry.idolPersonalRecordForThisDifficulty} = total {entry.totalPersonalRecordForThisDifficulty}"));
|
||||
container.Add(CreateMutedLabel($"进度: {entry.levelProgressForThisDifficulty:0.###} 上次游玩: {FormatDateTime(entry.lastPlayTimeForThisDifficulty)}"));
|
||||
|
||||
container.Add(CreateMutedLabel($"谱面文件: {GetObjName(entry.chartFile)}"));
|
||||
|
||||
if (!string.IsNullOrEmpty(entry.difficultyDescription))
|
||||
{
|
||||
container.Add(CreatePrimaryLabel("描述:"));
|
||||
var desc = CreateMutedLabel(WrapText(entry.difficultyDescription, 26));
|
||||
desc.style.whiteSpace = WhiteSpace.Normal;
|
||||
container.Add(desc);
|
||||
}
|
||||
|
||||
container.Add(CreatePrimaryLabel("敌人配置:"));
|
||||
var enemies = new VisualElement();
|
||||
enemies.style.flexDirection = FlexDirection.Column;
|
||||
enemies.style.flexWrap = Wrap.NoWrap;
|
||||
enemies.style.marginTop = 2;
|
||||
|
||||
if (entry.enemyConfigList == null || entry.enemyConfigList.Count == 0)
|
||||
{
|
||||
enemies.Add(CreateMutedLabel("(无)"));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < entry.enemyConfigList.Count; i++)
|
||||
{
|
||||
var c = entry.enemyConfigList[i];
|
||||
enemies.Add(CreateEnemyChip(i, c.enemyID, c.hpPercentage));
|
||||
}
|
||||
}
|
||||
|
||||
container.Add(enemies);
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement CreateEnemyChip(int index, int enemyId, int hpPercent)
|
||||
{
|
||||
var chip = new VisualElement();
|
||||
chip.style.backgroundColor = new Color(0.18f, 0.18f, 0.18f, 0.9f);
|
||||
chip.style.borderTopWidth = 1;
|
||||
chip.style.borderBottomWidth = 1;
|
||||
chip.style.borderLeftWidth = 1;
|
||||
chip.style.borderRightWidth = 1;
|
||||
chip.style.borderTopColor = new Color(0.25f, 0.25f, 0.25f);
|
||||
chip.style.borderBottomColor = new Color(0.25f, 0.25f, 0.25f);
|
||||
chip.style.borderLeftColor = new Color(0.25f, 0.25f, 0.25f);
|
||||
chip.style.borderRightColor = new Color(0.25f, 0.25f, 0.25f);
|
||||
chip.style.paddingLeft = 6;
|
||||
chip.style.paddingRight = 6;
|
||||
chip.style.paddingTop = 4;
|
||||
chip.style.paddingBottom = 4;
|
||||
chip.style.marginBottom = 6;
|
||||
chip.style.width = Length.Percent(100);
|
||||
chip.style.maxWidth = Length.Percent(100);
|
||||
|
||||
var idColor = enemyId == 0 ? new Color(0.6f, 0.6f, 0.6f) : new Color(0.2f, 0.8f, 0.35f);
|
||||
var header = CreatePrimaryLabel($"#{index + 1} EnemyID:{enemyId} HP:{hpPercent}%");
|
||||
header.style.fontSize = 11;
|
||||
header.style.color = idColor;
|
||||
chip.Add(header);
|
||||
return chip;
|
||||
}
|
||||
|
||||
private Label CreateLineLabel(string text)
|
||||
{
|
||||
return CreateLineLabel(text, 11, new Color(0.75f, 0.75f, 0.75f), false);
|
||||
}
|
||||
|
||||
private Label CreateLineLabel(string text, int fontSize, Color? color, bool bold)
|
||||
{
|
||||
var label = new Label(text ?? "");
|
||||
label.style.whiteSpace = WhiteSpace.Normal;
|
||||
label.style.unityTextAlign = TextAnchor.UpperLeft;
|
||||
label.style.fontSize = fontSize;
|
||||
if (bold) label.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
if (color.HasValue) label.style.color = color.Value;
|
||||
return label;
|
||||
}
|
||||
|
||||
private Label CreateTitleLabel(string text)
|
||||
{
|
||||
return CreateLineLabel(text, 14, new Color(0.95f, 0.95f, 0.95f), true);
|
||||
}
|
||||
|
||||
private Label CreatePrimaryLabel(string text)
|
||||
{
|
||||
return CreateLineLabel(text, 12, new Color(0.85f, 0.85f, 0.85f), true);
|
||||
}
|
||||
|
||||
private Label CreateMutedLabel(string text)
|
||||
{
|
||||
return CreateLineLabel(text, 11, new Color(0.7f, 0.7f, 0.7f), false);
|
||||
}
|
||||
|
||||
private VisualElement CreateSpriteThumb(string label, Sprite sprite)
|
||||
{
|
||||
var container = new VisualElement();
|
||||
container.style.flexDirection = FlexDirection.Column;
|
||||
container.style.width = 100;
|
||||
container.style.marginRight = 6;
|
||||
container.style.marginBottom = 6;
|
||||
|
||||
var imageBox = new VisualElement();
|
||||
imageBox.style.width = 92;
|
||||
imageBox.style.height = 64;
|
||||
imageBox.style.borderTopWidth = 1;
|
||||
imageBox.style.borderBottomWidth = 1;
|
||||
imageBox.style.borderLeftWidth = 1;
|
||||
imageBox.style.borderRightWidth = 1;
|
||||
imageBox.style.borderTopColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
imageBox.style.borderBottomColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
imageBox.style.borderLeftColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
imageBox.style.borderRightColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
|
||||
var img = new Image();
|
||||
img.image = sprite != null ? sprite.texture : null;
|
||||
img.scaleMode = ScaleMode.ScaleToFit;
|
||||
img.style.width = 92;
|
||||
img.style.height = 64;
|
||||
imageBox.Add(img);
|
||||
|
||||
var nameLabel = new Label(string.IsNullOrEmpty(label) ? GetSpriteName(sprite) : $"{label}\n{GetSpriteName(sprite)}");
|
||||
nameLabel.style.whiteSpace = WhiteSpace.Normal;
|
||||
nameLabel.style.fontSize = 9;
|
||||
nameLabel.style.unityTextAlign = TextAnchor.UpperCenter;
|
||||
|
||||
container.Add(imageBox);
|
||||
container.Add(nameLabel);
|
||||
return container;
|
||||
}
|
||||
|
||||
private string GetSpriteName(Sprite sprite)
|
||||
{
|
||||
return sprite != null ? sprite.name : "(无)";
|
||||
}
|
||||
|
||||
private string WrapText(string text, int maxCharsPerLine)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text) || maxCharsPerLine <= 0) return text;
|
||||
var sb = new StringBuilder();
|
||||
var lines = text.Split('\n');
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
var line = lines[i];
|
||||
int count = 0;
|
||||
for (int c = 0; c < line.Length; c++)
|
||||
{
|
||||
sb.Append(line[c]);
|
||||
count++;
|
||||
if (count >= maxCharsPerLine && c < line.Length - 1)
|
||||
{
|
||||
sb.Append('\n');
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
if (i < lines.Length - 1) sb.Append('\n');
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private string Safe(string s)
|
||||
{
|
||||
return string.IsNullOrEmpty(s) ? "-" : s;
|
||||
}
|
||||
|
||||
private string GetObjName(UnityEngine.Object obj)
|
||||
{
|
||||
return obj != null ? obj.name : "(无)";
|
||||
}
|
||||
|
||||
private string FormatDateTime(DateTime dt)
|
||||
{
|
||||
if (dt == default) return "-";
|
||||
return dt.ToString("yyyy-MM-dd HH:mm");
|
||||
}
|
||||
|
||||
private string CountDict<TKey, TValue>(System.Collections.Generic.Dictionary<TKey, TValue> dict)
|
||||
{
|
||||
return dict == null ? "0" : dict.Count.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
class SongDataNode : Node
|
||||
{
|
||||
public Port input;
|
||||
public Port output;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48642eab80a548c488d1a743efff1792
|
||||
Reference in New Issue
Block a user