策划工具更新
This commit is contained in:
parent
f3be6dbec3
commit
cc1400d2dd
@ -498,21 +498,14 @@ namespace DesignTools.Collections
|
||||
int.TryParse(initText, out init);
|
||||
}
|
||||
|
||||
int iconId = -1;
|
||||
if (!string.IsNullOrEmpty(iconText))
|
||||
{
|
||||
if (!int.TryParse(iconText, out iconId))
|
||||
{
|
||||
iconId = -1;
|
||||
}
|
||||
}
|
||||
string iconName = iconText ?? "";
|
||||
|
||||
var emojiData = new EmojiData
|
||||
{
|
||||
Id = id,
|
||||
NameKey = nameKey,
|
||||
Init = init,
|
||||
IconId = iconId
|
||||
IconName = iconName
|
||||
};
|
||||
|
||||
emojiDataList.Add(emojiData);
|
||||
@ -669,13 +662,16 @@ namespace DesignTools.Collections
|
||||
var iconNames = iconNamesList.ToArray();
|
||||
|
||||
int currentIndex = 0;
|
||||
var currentItem = iconItems.Find(x => x.Id == data.IconId);
|
||||
if (currentItem != null)
|
||||
if (!string.IsNullOrEmpty(data.IconName))
|
||||
{
|
||||
int itemIndex = iconItems.IndexOf(currentItem);
|
||||
if (itemIndex >= 0)
|
||||
var currentItem = iconItems.Find(x => x.Name == data.IconName);
|
||||
if (currentItem != null)
|
||||
{
|
||||
currentIndex = itemIndex + 1;
|
||||
int itemIndex = iconItems.IndexOf(currentItem);
|
||||
if (itemIndex >= 0)
|
||||
{
|
||||
currentIndex = itemIndex + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -685,18 +681,18 @@ namespace DesignTools.Collections
|
||||
{
|
||||
if (newIndex == 0)
|
||||
{
|
||||
data.IconId = -1;
|
||||
data.IconName = "";
|
||||
}
|
||||
else if (newIndex > 0 && newIndex <= iconItems.Count)
|
||||
{
|
||||
data.IconId = iconItems[newIndex - 1].Id;
|
||||
data.IconName = iconItems[newIndex - 1].Name;
|
||||
}
|
||||
}
|
||||
|
||||
// 预览
|
||||
if (data.IconId >= 0)
|
||||
if (!string.IsNullOrEmpty(data.IconName))
|
||||
{
|
||||
var item = iconItems.Find(x => x.Id == data.IconId);
|
||||
var item = iconItems.Find(x => x.Name == data.IconName);
|
||||
if (item != null)
|
||||
{
|
||||
Sprite sprite = item.Sprite;
|
||||
@ -792,7 +788,7 @@ namespace DesignTools.Collections
|
||||
Id = newId,
|
||||
NameKey = "",
|
||||
Init = 0,
|
||||
IconId = -1
|
||||
IconName = ""
|
||||
};
|
||||
|
||||
emojiDataList.Add(newEmoji);
|
||||
@ -865,14 +861,7 @@ namespace DesignTools.Collections
|
||||
worksheet.Cells[row, nameKeyCol].Value = emojiData.NameKey;
|
||||
worksheet.Cells[row, initCol].Value = emojiData.Init;
|
||||
|
||||
if (emojiData.IconId < 0)
|
||||
{
|
||||
worksheet.Cells[row, iconCol].Value = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
worksheet.Cells[row, iconCol].Value = emojiData.IconId;
|
||||
}
|
||||
worksheet.Cells[row, iconCol].Value = emojiData.IconName ?? "";
|
||||
|
||||
processedIds.Add(id);
|
||||
}
|
||||
@ -893,14 +882,7 @@ namespace DesignTools.Collections
|
||||
worksheet.Cells[currentRow, nameKeyCol].Value = emojiData.NameKey;
|
||||
worksheet.Cells[currentRow, initCol].Value = emojiData.Init;
|
||||
|
||||
if (emojiData.IconId < 0)
|
||||
{
|
||||
worksheet.Cells[currentRow, iconCol].Value = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
worksheet.Cells[currentRow, iconCol].Value = emojiData.IconId;
|
||||
}
|
||||
worksheet.Cells[currentRow, iconCol].Value = emojiData.IconName ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
@ -931,7 +913,7 @@ namespace DesignTools.Collections
|
||||
public int Id;
|
||||
public string NameKey;
|
||||
public int Init;
|
||||
public int IconId;
|
||||
public string IconName = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -525,21 +525,14 @@ namespace DesignTools.Collections
|
||||
int.TryParse(initText, out init);
|
||||
}
|
||||
|
||||
int iconId = -1; // 默认为未选择
|
||||
if (!string.IsNullOrEmpty(iconText))
|
||||
{
|
||||
if (!int.TryParse(iconText, out iconId))
|
||||
{
|
||||
iconId = -1; // 解析失败设为未选择
|
||||
}
|
||||
}
|
||||
string iconName = iconText ?? "";
|
||||
|
||||
var faceData = new FaceData
|
||||
{
|
||||
Id = id,
|
||||
NameKey = nameKey,
|
||||
Init = init,
|
||||
IconId = iconId
|
||||
IconName = iconName
|
||||
};
|
||||
|
||||
faceDataList.Add(faceData);
|
||||
@ -700,14 +693,17 @@ namespace DesignTools.Collections
|
||||
|
||||
// 查找当前选中的索引
|
||||
int currentIndex = 0; // 默认为"未选择"
|
||||
var currentItem = iconItems.Find(x => x.Id == data.IconId);
|
||||
if (currentItem != null)
|
||||
if (!string.IsNullOrEmpty(data.IconName))
|
||||
{
|
||||
// 找到了对应的Item,索引需要+1(因为第0项是"未选择")
|
||||
int itemIndex = iconItems.IndexOf(currentItem);
|
||||
if (itemIndex >= 0)
|
||||
var currentItem = iconItems.Find(x => x.Name == data.IconName);
|
||||
if (currentItem != null)
|
||||
{
|
||||
currentIndex = itemIndex + 1;
|
||||
// 找到了对应的Item,索引需要+1(因为第0项是"未选择")
|
||||
int itemIndex = iconItems.IndexOf(currentItem);
|
||||
if (itemIndex >= 0)
|
||||
{
|
||||
currentIndex = itemIndex + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -718,19 +714,19 @@ namespace DesignTools.Collections
|
||||
if (newIndex == 0)
|
||||
{
|
||||
// 选择了"未选择"
|
||||
data.IconId = -1;
|
||||
data.IconName = "";
|
||||
}
|
||||
else if (newIndex > 0 && newIndex <= iconItems.Count)
|
||||
{
|
||||
// 选择了具体的Icon(索引需要-1)
|
||||
data.IconId = iconItems[newIndex - 1].Id;
|
||||
data.IconName = iconItems[newIndex - 1].Name;
|
||||
}
|
||||
}
|
||||
|
||||
// 预览和Desc提示
|
||||
if (data.IconId >= 0)
|
||||
if (!string.IsNullOrEmpty(data.IconName))
|
||||
{
|
||||
var item = iconItems.Find(x => x.Id == data.IconId);
|
||||
var item = iconItems.Find(x => x.Name == data.IconName);
|
||||
if (item != null)
|
||||
{
|
||||
// 直接使用Sprite引用
|
||||
@ -796,7 +792,7 @@ namespace DesignTools.Collections
|
||||
}
|
||||
else
|
||||
{
|
||||
// IconId < 0,未选择状态
|
||||
// IconName为空,未选择状态
|
||||
GUILayoutUtility.GetRect(50, 50, GUILayout.Width(50), GUILayout.Height(50));
|
||||
GUILayout.Space(-50);
|
||||
EditorGUILayout.LabelField("未选择", GUILayout.Width(50));
|
||||
@ -835,7 +831,7 @@ namespace DesignTools.Collections
|
||||
Id = newId,
|
||||
NameKey = "",
|
||||
Init = 0,
|
||||
IconId = -1
|
||||
IconName = ""
|
||||
};
|
||||
|
||||
faceDataList.Add(newFace);
|
||||
@ -911,15 +907,7 @@ namespace DesignTools.Collections
|
||||
worksheet.Cells[row, nameKeyCol].Value = faceData.NameKey;
|
||||
worksheet.Cells[row, initCol].Value = faceData.Init;
|
||||
|
||||
// IconId为-1时写入空字符串,否则写入实际值
|
||||
if (faceData.IconId < 0)
|
||||
{
|
||||
worksheet.Cells[row, iconCol].Value = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
worksheet.Cells[row, iconCol].Value = faceData.IconId;
|
||||
}
|
||||
worksheet.Cells[row, iconCol].Value = faceData.IconName ?? "";
|
||||
|
||||
processedIds.Add(id);
|
||||
}
|
||||
@ -942,14 +930,7 @@ namespace DesignTools.Collections
|
||||
worksheet.Cells[currentRow, nameKeyCol].Value = faceData.NameKey;
|
||||
worksheet.Cells[currentRow, initCol].Value = faceData.Init;
|
||||
|
||||
if (faceData.IconId < 0)
|
||||
{
|
||||
worksheet.Cells[currentRow, iconCol].Value = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
worksheet.Cells[currentRow, iconCol].Value = faceData.IconId;
|
||||
}
|
||||
worksheet.Cells[currentRow, iconCol].Value = faceData.IconName ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
@ -981,7 +962,7 @@ namespace DesignTools.Collections
|
||||
public int Id;
|
||||
public string NameKey;
|
||||
public int Init;
|
||||
public int IconId;
|
||||
public string IconName = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -502,14 +502,7 @@ namespace DesignTools.Collections
|
||||
int.TryParse(initText, out init);
|
||||
}
|
||||
|
||||
int iconId = -1;
|
||||
if (!string.IsNullOrEmpty(iconText))
|
||||
{
|
||||
if (!int.TryParse(iconText, out iconId))
|
||||
{
|
||||
iconId = -1;
|
||||
}
|
||||
}
|
||||
string iconName = iconText ?? "";
|
||||
|
||||
float frameImageScale = 1.0f;
|
||||
if (!string.IsNullOrEmpty(frameImageScaleText))
|
||||
@ -525,7 +518,7 @@ namespace DesignTools.Collections
|
||||
Id = id,
|
||||
NameKey = nameKey,
|
||||
Init = init,
|
||||
IconId = iconId,
|
||||
IconName = iconName,
|
||||
FrameImageScale = frameImageScale
|
||||
};
|
||||
|
||||
@ -685,13 +678,16 @@ namespace DesignTools.Collections
|
||||
var iconNames = iconNamesList.ToArray();
|
||||
|
||||
int currentIndex = 0;
|
||||
var currentItem = iconItems.Find(x => x.Id == data.IconId);
|
||||
if (currentItem != null)
|
||||
if (!string.IsNullOrEmpty(data.IconName))
|
||||
{
|
||||
int itemIndex = iconItems.IndexOf(currentItem);
|
||||
if (itemIndex >= 0)
|
||||
var currentItem = iconItems.Find(x => x.Name == data.IconName);
|
||||
if (currentItem != null)
|
||||
{
|
||||
currentIndex = itemIndex + 1;
|
||||
int itemIndex = iconItems.IndexOf(currentItem);
|
||||
if (itemIndex >= 0)
|
||||
{
|
||||
currentIndex = itemIndex + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -701,18 +697,18 @@ namespace DesignTools.Collections
|
||||
{
|
||||
if (newIndex == 0)
|
||||
{
|
||||
data.IconId = -1;
|
||||
data.IconName = "";
|
||||
}
|
||||
else if (newIndex > 0 && newIndex <= iconItems.Count)
|
||||
{
|
||||
data.IconId = iconItems[newIndex - 1].Id;
|
||||
data.IconName = iconItems[newIndex - 1].Name;
|
||||
}
|
||||
}
|
||||
|
||||
// 预览
|
||||
if (data.IconId >= 0)
|
||||
if (!string.IsNullOrEmpty(data.IconName))
|
||||
{
|
||||
var item = iconItems.Find(x => x.Id == data.IconId);
|
||||
var item = iconItems.Find(x => x.Name == data.IconName);
|
||||
if (item != null)
|
||||
{
|
||||
Sprite sprite = item.Sprite;
|
||||
@ -811,7 +807,7 @@ namespace DesignTools.Collections
|
||||
Id = newId,
|
||||
NameKey = "",
|
||||
Init = 0,
|
||||
IconId = -1,
|
||||
IconName = "",
|
||||
FrameImageScale = 1.0f
|
||||
};
|
||||
|
||||
@ -888,14 +884,7 @@ namespace DesignTools.Collections
|
||||
worksheet.Cells[row, nameKeyCol].Value = frameData.NameKey;
|
||||
worksheet.Cells[row, initCol].Value = frameData.Init;
|
||||
|
||||
if (frameData.IconId < 0)
|
||||
{
|
||||
worksheet.Cells[row, iconCol].Value = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
worksheet.Cells[row, iconCol].Value = frameData.IconId;
|
||||
}
|
||||
worksheet.Cells[row, iconCol].Value = frameData.IconName ?? "";
|
||||
|
||||
worksheet.Cells[row, frameImageScaleCol].Value = frameData.FrameImageScale;
|
||||
|
||||
@ -918,14 +907,7 @@ namespace DesignTools.Collections
|
||||
worksheet.Cells[currentRow, nameKeyCol].Value = frameData.NameKey;
|
||||
worksheet.Cells[currentRow, initCol].Value = frameData.Init;
|
||||
|
||||
if (frameData.IconId < 0)
|
||||
{
|
||||
worksheet.Cells[currentRow, iconCol].Value = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
worksheet.Cells[currentRow, iconCol].Value = frameData.IconId;
|
||||
}
|
||||
worksheet.Cells[currentRow, iconCol].Value = frameData.IconName ?? "";
|
||||
|
||||
worksheet.Cells[currentRow, frameImageScaleCol].Value = frameData.FrameImageScale;
|
||||
}
|
||||
@ -958,7 +940,7 @@ namespace DesignTools.Collections
|
||||
public int Id;
|
||||
public string NameKey;
|
||||
public int Init;
|
||||
public int IconId;
|
||||
public string IconName = "";
|
||||
public float FrameImageScale;
|
||||
}
|
||||
}
|
||||
|
||||
326
Scripts/Editor/Design_Tools/DESIGN_TOOLS_SKILL.md
Normal file
326
Scripts/Editor/Design_Tools/DESIGN_TOOLS_SKILL.md
Normal file
@ -0,0 +1,326 @@
|
||||
# 策划工具系统 (Design Tools) - AI Skill 文档
|
||||
|
||||
## 概述
|
||||
|
||||
策划工具系统是基于Unity Editor的配置数据管理工具集,主要用于编辑xlsx配置表数据,并与美术资源(ArtTableSO)联动显示预览。所有工具通过Unity菜单栏「策划工具」菜单访问。
|
||||
|
||||
---
|
||||
|
||||
## 项目结构
|
||||
|
||||
### 代码目录
|
||||
```
|
||||
Assets/Design_SubModule/Scripts/Editor/Design_Tools/
|
||||
├── BaseDesignToolEditor.cs # 编辑器基类(Git管理、路径管理、UI框架)
|
||||
├── ItemConfigEditor.cs # 道具配置编辑器 (Item.xlsx)
|
||||
├── Common/
|
||||
│ └── DesignToolItemPickerWindow.cs # 通用道具选择弹窗(搜索+分页+筛选)
|
||||
├── Collections/
|
||||
│ ├── HeadFrameConfigEditor.cs # 头像框配置 (Avatar.xlsx)
|
||||
│ ├── HeadConfigEditor.cs # 头像配置 (Face.xlsx)
|
||||
│ └── EmojiConfigEditor.cs # 表情配置 (Emoji.xlsx)
|
||||
├── DesignSoundEditorWindow/
|
||||
│ ├── AmbientSoundConfigEditor.cs # 环境音配置 (Sound.xlsx - AmbientSound)
|
||||
│ └── SoundItemConfigEditor.cs # 音效配置 (Sound.xlsx - Sound)
|
||||
├── Friends/
|
||||
│ ├── FriendTreasureChestEditor.cs # 宝箱奖励及概率 (FriendTreasure.xlsx - Chest)
|
||||
│ └── InviteRewardEditor.cs # 邀请奖励 (Invite.xlsx - Reward)
|
||||
├── LimitedTimeEvent/
|
||||
│ └── CatTrickConstEditor.cs # 小猫戏法常量 (LimitedTimeEvent.xlsx - Const)
|
||||
└── Scene/
|
||||
├── DecorateCostPlannerEditor.cs # 装饰消耗规划 (DecorateCost.xlsx)
|
||||
├── IndoorProgressEditor.cs # 场景进度奖励 (IndoorProgress.xlsx + Item.xlsx)
|
||||
└── MaxAreaIdEditor.cs # 最大场景ID (Constant.xlsx - ConstantInt)
|
||||
```
|
||||
|
||||
### 菜单路径
|
||||
| 菜单路径 | 编辑器类 | 操作的xlsx文件 |
|
||||
|---------|---------|--------------|
|
||||
| 策划工具/Item配置 | ItemConfigEditor | Item.xlsx (Sheet: Item) |
|
||||
| 策划工具/收藏品/头像框 | HeadFrameConfigEditor | Avatar.xlsx (Sheet: Avatar) |
|
||||
| 策划工具/收藏品/头像 | HeadConfigEditor | Face.xlsx (Sheet: Face) |
|
||||
| 策划工具/收藏品/表情 | EmojiConfigEditor | Emoji.xlsx (Sheet: Emoji) |
|
||||
| 策划工具/好友/好友馈赠/宝箱奖励及概率 | FriendTreasureChestEditor | FriendTreasure.xlsx (Sheet: Chest) |
|
||||
| 策划工具/好友/邀请奖励 | InviteRewardEditor | Invite.xlsx (Sheet: Reward) |
|
||||
| 策划工具/限时活动/小猫戏法常量 | CatTrickConstEditor | LimitedTimeEvent.xlsx (Sheet: Const) |
|
||||
| 策划工具/场景/装饰消耗规划 | DecorateCostPlannerEditor | DecorateCost.xlsx (Sheet: DecorateCost) |
|
||||
| 策划工具/场景/场景进度奖励 | IndoorProgressEditor | IndoorProgress.xlsx + Item.xlsx |
|
||||
| 策划工具/场景/最大场景ID | MaxAreaIdEditor | Constant.xlsx (Sheet: ConstantInt) |
|
||||
| 策划工具/音效配置/环境音 | AmbientSoundConfigEditor | Sound.xlsx (Sheet: AmbientSound + Sound) |
|
||||
| 策划工具/音效配置/音效 | SoundItemConfigEditor | Sound.xlsx (Sheet: Sound) |
|
||||
|
||||
---
|
||||
|
||||
## Excel 表格约定
|
||||
|
||||
### 通用格式规则
|
||||
- **第1行**:Key行(列名/字段名,如 Id, Name, IType, Res 等)
|
||||
- **第2行**:备注行(中文说明,程序不读取)
|
||||
- **第3行起**:实际数据行
|
||||
|
||||
### 数据起始行常量
|
||||
所有编辑器中 `DATA_START_ROW = 3`(即从第3行开始读写数据)。
|
||||
|
||||
### xlsx文件位置
|
||||
所有xlsx文件存放在Docs项目(外部Git仓库)的 `config/` 目录下。
|
||||
|
||||
### 主要xlsx结构
|
||||
|
||||
#### Item.xlsx (Sheet: Item)
|
||||
| 列名 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| Id | int | 道具唯一ID |
|
||||
| Name | string | 道具名称 |
|
||||
| IType | int | 道具类型(见IType类型表) |
|
||||
| Effect | string | 效果数据(格式因IType而异) |
|
||||
| Res | string | 美术资源引用,格式: `"TableName,ItemName"` → 对应ArtTableSO的TableName和ArtItemData的Name |
|
||||
|
||||
#### IndoorProgress.xlsx (Sheet: IndoorProgress)
|
||||
| 列名 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| Id | int | 唯一ID |
|
||||
| Scene | int | 场景编号 |
|
||||
| Lv | int | 等级/步骤 |
|
||||
| Item | string | 道具奖励JSON: `[{"Id":83,"Num":1}]` |
|
||||
| Emit | string | 发射的道具ID: `83` |
|
||||
| Reward | string | 奖励: `83=1` |
|
||||
| BigReward | string | 大奖励: `Energy=25` 或 `Energy=50,PurplePig=1` |
|
||||
| AreaReward | string | 区域奖励JSON: `[{"Id":100001,"Num":25}]` |
|
||||
| Part | int | 零件数量 |
|
||||
|
||||
#### FriendTreasure.xlsx (Sheet: Chest)
|
||||
| 列名 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| 第1列 | int | Id |
|
||||
| 第2列 | string | Items JSON: `[{"Id":83,"Num":1}]` |
|
||||
| 第3列 | int | Prob 概率(总和=1000,即千分之) |
|
||||
|
||||
#### Avatar.xlsx (Sheet: Avatar)
|
||||
| 列名 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| Id | int | 头像框ID |
|
||||
| NameKey | string | 多语言key |
|
||||
| Init | int | 是否初始拥有 (0/1) |
|
||||
| Icon | int | 图标资源ID(对应HeadFrameResource SO中的ArtItemData.Id) |
|
||||
| FrameImageScale | float | 缩放比例 |
|
||||
|
||||
#### Face.xlsx (Sheet: Face)
|
||||
| 列名 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| Id | int | 头像ID |
|
||||
| NameKey | string | 多语言key |
|
||||
| Init | int | 是否初始拥有 |
|
||||
| Icon | int | 图标资源ID(对应HeadResource SO中的ArtItemData.Id) |
|
||||
|
||||
#### Emoji.xlsx (Sheet: Emoji)
|
||||
类似Face.xlsx结构,Icon对应EmojiResource SO。
|
||||
|
||||
---
|
||||
|
||||
## IType 道具类型表
|
||||
|
||||
| IType | 名称 | 说明 |
|
||||
|-------|------|------|
|
||||
| 1 | 能量 | Energy |
|
||||
| 2 | 星星 | Star |
|
||||
| 3 | 钻石 | Diamond |
|
||||
| 97 | Playroom宠物道具 | |
|
||||
| 98 | 卡牌 | |
|
||||
| 99 | 背包道具 | |
|
||||
| 100 | 棋子 | 数量最多的类型 |
|
||||
| 101 | 卡包 | |
|
||||
| 102 | 限时事件 | Effect格式: `eventId,seconds` |
|
||||
| 103 | 小猪存钱罐 | |
|
||||
| 104 | 万能卡 | |
|
||||
| 105 | 头像框 | Effect关联Avatar.xlsx |
|
||||
| 106 | 活动代币 | |
|
||||
| 107 | 竞赛游戏代币 | |
|
||||
| 108 | Pet Playroom拜访道具 | |
|
||||
| 109 | 表情 | Effect关联Emoji.xlsx |
|
||||
| 110 | 头像 | Effect关联Face.xlsx |
|
||||
| 111 | Playroom装饰 | |
|
||||
| 112 | Playroom服装 | |
|
||||
| 113 | Playroom装饰套装 | |
|
||||
| 114 | Playroom服装套装 | |
|
||||
| 115 | Playroom道具宝箱 | |
|
||||
| 116 | 活动通行证代币道具 | |
|
||||
|
||||
---
|
||||
|
||||
## 美术资源系统 (Art Resource)
|
||||
|
||||
### 核心类
|
||||
|
||||
#### ArtTableSO (ScriptableObject)
|
||||
```
|
||||
路径: Assets/Art_SubModule/Art_Scripts/ArtTableSO.cs
|
||||
```
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| TableId | int | 表ID(全局唯一,系统自动分配) |
|
||||
| TableName | string | 表名称(全局唯一标识符,如 "HeadFrameResource") |
|
||||
| Items | List\<ArtItemData\> | 资源项列表 |
|
||||
|
||||
#### ArtItemData (Serializable)
|
||||
```
|
||||
路径: Assets/Art_SubModule/Art_Scripts/ArtItemData.cs
|
||||
```
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| Id | int | 资源ID(表内唯一) |
|
||||
| Name | string | 资源名称(唯一,用于查询) |
|
||||
| Desc | string | 资源描述 |
|
||||
| Sprite | Sprite | 图片资源引用(Editor模式) |
|
||||
| SpritePath | string | 图片资源路径(Runtime模式) |
|
||||
| SpineAsset | SkeletonDataAsset | Spine骨骼资源(Editor模式) |
|
||||
| SpineAssetPath | string | Spine资源路径(Runtime模式) |
|
||||
| SpineAnimName | string | Spine动画名称 |
|
||||
|
||||
### Art_SO 目录结构
|
||||
```
|
||||
Assets/Art_SubModule/Art_SO/
|
||||
├── art_table_manifest.json # 资源表全局配置清单
|
||||
├── Collections/ # 收藏品资源
|
||||
│ ├── EmojiResource.asset # 表情(TableName="EmojiResource")
|
||||
│ ├── HeadFrameResource.asset # 头像框(TableName="HeadFrameResource")
|
||||
│ └── HeadResource.asset # 头像(TableName="HeadResource")
|
||||
├── DecorateIcon/ # 装饰图标资源(按场景区间分组)
|
||||
│ ├── DecorateIconResource_1_8.asset
|
||||
│ ├── DecorateIconResource_9_17.asset
|
||||
│ └── ...(共9个按区间的资源)
|
||||
├── DecorateScene/ # 装饰场景资源
|
||||
│ └── Scene1Resource.asset ~ Scene55Resource.asset
|
||||
├── Scene/
|
||||
│ └── SceneExpression.asset
|
||||
└── Shop/
|
||||
├── ShopBig.asset
|
||||
└── ShopOther.asset
|
||||
```
|
||||
|
||||
### Res字段格式
|
||||
Item.xlsx中的Res字段格式为 `"TableName,ItemName"`:
|
||||
- `TableName` → 对应 ArtTableSO.TableName(全局唯一标识符)
|
||||
- `ItemName` → 对应 ArtItemData.Name(表内唯一名称)
|
||||
- 例如: `"HeadFrameResource,frame_001"` 表示 TableName="HeadFrameResource" 的 ArtTableSO 中 Name="frame_001" 的 ArtItemData
|
||||
- 仅选了表但未选Item时: `"HeadFrameResource,"`(逗号后为空)
|
||||
|
||||
> **历史格式**: 旧版本使用 `"TableId,ItemId"`(数字格式如 `"3,15"`)。ItemConfigEditor中提供了「迁移Res格式(ID→Name)」按钮,可一键将旧格式转换为新格式。
|
||||
|
||||
### 资源查找流程
|
||||
```
|
||||
Res = "TableName,ItemName"
|
||||
→ 找到 ArtTableSO (where TableName == TableName)
|
||||
→ 在 Items 列表中找到 ArtItemData (where Name == ItemName)
|
||||
→ 获取 Sprite / SpinePath 等资源
|
||||
```
|
||||
|
||||
ItemConfigEditor中的DrawResField使用三级选择UI:
|
||||
1. **组(Folder)** → Art_SO下的子文件夹(Collections/DecorateIcon等)
|
||||
2. **表(Table)** → 该文件夹下的ArtTableSO
|
||||
3. **Item** → 该表中的ArtItemData
|
||||
|
||||
---
|
||||
|
||||
## 基类架构 (BaseDesignToolEditor)
|
||||
|
||||
### 继承关系
|
||||
```
|
||||
EditorWindow
|
||||
├── BaseDesignToolEditor (抽象基类)
|
||||
│ ├── FriendTreasureChestEditor
|
||||
│ ├── InviteRewardEditor
|
||||
│ ├── CatTrickConstEditor
|
||||
│ ├── AmbientSoundConfigEditor
|
||||
│ ├── SoundItemConfigEditor
|
||||
│ ├── DecorateCostPlannerEditor
|
||||
│ └── MaxAreaIdEditor
|
||||
│
|
||||
├── ItemConfigEditor (独立,未继承基类)
|
||||
├── HeadFrameConfigEditor (独立)
|
||||
├── HeadConfigEditor (独立)
|
||||
├── EmojiConfigEditor (独立)
|
||||
└── IndoorProgressEditor (独立)
|
||||
```
|
||||
|
||||
### 基类提供的能力
|
||||
- `docsRootPath` 管理和EditorPrefs持久化
|
||||
- `LoadData()` 统一加载流程:路径校验 → Git检查/更新 → 分支检查 → 子类LoadConfigData()
|
||||
- `ExecuteGitCommand()` Git命令执行
|
||||
- `CheckAndUpdateDocsRepository()` Docs仓库fetch/pull/冲突检测
|
||||
- `CheckAndSwitchDesignSubModuleBranch()` Design_SubModule分支切换
|
||||
- 统一UI框架:工具栏、路径选择、加载按钮、底部保存/重载按钮
|
||||
- `GetDocsConfigFilePath(fileName)` 拼接Docs配置文件路径
|
||||
|
||||
### 子类必须实现的抽象方法
|
||||
- `GetDocsPathPrefKey()` - 返回唯一的EditorPrefs key
|
||||
- `GetWindowTitle()` - 窗口标题
|
||||
- `LoadConfigData()` - 具体的数据加载逻辑
|
||||
- `DrawDataEditor()` - 具体的UI绘制逻辑
|
||||
|
||||
### 子类可选覆盖的虚方法
|
||||
- `GetMinWindowSize()` - 窗口最小尺寸(默认1000x600)
|
||||
- `NeedCheckDesignSubModuleBranch()` - 是否检查分支(默认true)
|
||||
- `SaveDataToExcel()` - 保存到Excel
|
||||
- `ShowBottomButtons()` - 是否显示底部按钮(默认true)
|
||||
|
||||
---
|
||||
|
||||
## 通用组件
|
||||
|
||||
### DesignToolItemPickerWindow(道具选择弹窗)
|
||||
```
|
||||
路径: Common/DesignToolItemPickerWindow.cs
|
||||
```
|
||||
**功能**:弹出模态窗口,供用户在大量道具中快速查找并选择
|
||||
**使用方式**:
|
||||
```csharp
|
||||
DesignToolItemPickerWindow.ShowWindow(
|
||||
items, // List<DesignToolItemPickerItem> 所有道具
|
||||
itemTypes, // List<int> 所有IType列表
|
||||
typeNames, // Dictionary<int,string> IType名称映射
|
||||
currentType, // 当前筛选的IType
|
||||
selectedItemId, // 当前选中的ItemId
|
||||
ownerRect, // 父窗口位置(弹窗居中用)
|
||||
onSelected // Action<int> 选中回调,返回ItemId
|
||||
);
|
||||
```
|
||||
**UI特性**:搜索框(按ID/Name匹配)、IType下拉筛选、分页(每页20条)、当前选中高亮
|
||||
|
||||
**使用者**:FriendTreasureChestEditor、IndoorProgressEditor(道具奖励和区域奖励均使用)
|
||||
|
||||
---
|
||||
|
||||
## 多语言支持
|
||||
|
||||
加载 `AllLanguage.xlsx` (Sheet: client):
|
||||
- 第1行: key行(key, zh_CN, en_US, pt_BR...)
|
||||
- 第2行: 备注行
|
||||
- 第3行起: 数据行
|
||||
- 用于Collections编辑器中显示名称tooltip
|
||||
|
||||
---
|
||||
|
||||
## Git工作流
|
||||
|
||||
所有工具在加载数据前会:
|
||||
1. 检查Docs目录是否为Git仓库(.git目录存在)
|
||||
2. `git fetch` 检查远程更新
|
||||
3. `git status -uno` 检查本地状态
|
||||
4. 如有远程更新则 `git pull`
|
||||
5. 检查Design_SubModule当前分支,必要时 `checkout main`
|
||||
|
||||
保存后会提醒用户手动commit和push。
|
||||
|
||||
---
|
||||
|
||||
## 关键数据格式
|
||||
|
||||
| 格式名 | 示例 | 使用场景 |
|
||||
|--------|------|---------|
|
||||
| Item JSON | `[{"Id":83,"Num":1}]` | IndoorProgress.Item / FriendTreasure.Items |
|
||||
| 多Item JSON | `[{"Id":100001,"Num":50},{"Id":100021,"Num":1}]` | IndoorProgress.AreaReward |
|
||||
| Reward字符串 | `83=1` | IndoorProgress.Reward |
|
||||
| BigReward字符串 | `Energy=25,PurplePig=1` | IndoorProgress.BigReward(使用MY_ITEM_DICT映射名称) |
|
||||
| Res引用 | `"TableName,ItemName"` 如 `"HeadFrameResource,frame_001"` | Item.xlsx.Res字段 |
|
||||
| Effect引用 | `"id,param"` 如 `"5,0"` | Item.xlsx.Effect字段(收藏品/限时事件) |
|
||||
| 限时事件 | `"eventId,seconds"` | IType=102的Effect |
|
||||
7
Scripts/Editor/Design_Tools/DESIGN_TOOLS_SKILL.md.meta
Normal file
7
Scripts/Editor/Design_Tools/DESIGN_TOOLS_SKILL.md.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8bb8a810cff55204488cd3d1998b56e1
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -645,20 +645,23 @@ namespace DesignTools.Friends
|
||||
return;
|
||||
}
|
||||
|
||||
if (!int.TryParse(parts[0], out int tableId) || !int.TryParse(parts[1], out int artItemId))
|
||||
string tableName = parts[0];
|
||||
string artItemName = parts[1];
|
||||
|
||||
if (string.IsNullOrEmpty(tableName) || string.IsNullOrEmpty(artItemName))
|
||||
{
|
||||
EditorGUILayout.LabelField("无图", GUILayout.Width(70));
|
||||
return;
|
||||
}
|
||||
|
||||
ArtTableSO artTable = allArtTables.FirstOrDefault(x => x.TableId == tableId);
|
||||
ArtTableSO artTable = allArtTables.FirstOrDefault(x => x.TableName == tableName);
|
||||
if (artTable == null)
|
||||
{
|
||||
EditorGUILayout.LabelField("无图", GUILayout.Width(70));
|
||||
return;
|
||||
}
|
||||
|
||||
var artItem = artTable.Items.FirstOrDefault(x => x.Id == artItemId);
|
||||
var artItem = artTable.Items.FirstOrDefault(x => x.Name == artItemName);
|
||||
if (artItem == null || artItem.Sprite == null)
|
||||
{
|
||||
EditorGUILayout.LabelField("无图", GUILayout.Width(70));
|
||||
|
||||
@ -616,13 +616,12 @@ namespace DesignTools
|
||||
{
|
||||
string nameKey = worksheet.Cells[row, nameKeyCol].Text;
|
||||
string iconText = worksheet.Cells[row, iconCol].Text;
|
||||
int.TryParse(iconText, out int iconId);
|
||||
|
||||
avatarDict[id] = new AvatarData
|
||||
{
|
||||
Id = id,
|
||||
NameKey = nameKey,
|
||||
IconId = iconId
|
||||
IconName = iconText ?? ""
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -681,13 +680,12 @@ namespace DesignTools
|
||||
{
|
||||
string nameKey = worksheet.Cells[row, nameKeyCol].Text;
|
||||
string iconText = worksheet.Cells[row, iconCol].Text;
|
||||
int.TryParse(iconText, out int iconId);
|
||||
|
||||
faceDict[id] = new FaceData
|
||||
{
|
||||
Id = id,
|
||||
NameKey = nameKey,
|
||||
IconId = iconId
|
||||
IconName = iconText ?? ""
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -746,13 +744,12 @@ namespace DesignTools
|
||||
{
|
||||
string nameKey = worksheet.Cells[row, nameKeyCol].Text;
|
||||
string iconText = worksheet.Cells[row, iconCol].Text;
|
||||
int.TryParse(iconText, out int iconId);
|
||||
|
||||
emojiDict[id] = new EmojiData
|
||||
{
|
||||
Id = id,
|
||||
NameKey = nameKey,
|
||||
IconId = iconId
|
||||
IconName = iconText ?? ""
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1306,13 +1303,10 @@ namespace DesignTools
|
||||
{
|
||||
EditorGUILayout.BeginVertical(GUILayout.Width(200));
|
||||
|
||||
// Res格式: "TableId,ItemId"
|
||||
// Res格式: "TableName,ItemName"
|
||||
string[] parts = string.IsNullOrEmpty(data.Res) ? new string[0] : data.Res.Split(',');
|
||||
int tableId = -1;
|
||||
int itemId = -1;
|
||||
|
||||
if (parts.Length > 0) int.TryParse(parts[0], out tableId);
|
||||
if (parts.Length > 1) int.TryParse(parts[1], out itemId);
|
||||
string tableName = parts.Length > 0 ? parts[0] : "";
|
||||
string itemName = parts.Length > 1 ? parts[1] : "";
|
||||
|
||||
// 查找当前表格和所属文件夹
|
||||
ArtTableSO currentTable = null;
|
||||
@ -1325,9 +1319,9 @@ namespace DesignTools
|
||||
}
|
||||
|
||||
// 如果有Res数据,从Res推导组和表
|
||||
if (tableId >= 0)
|
||||
if (!string.IsNullOrEmpty(tableName))
|
||||
{
|
||||
currentTable = allArtTables.FirstOrDefault(x => x.TableId == tableId);
|
||||
currentTable = allArtTables.FirstOrDefault(x => x.TableName == tableName);
|
||||
if (currentTable != null)
|
||||
{
|
||||
string tablePath = AssetDatabase.GetAssetPath(currentTable);
|
||||
@ -1365,8 +1359,8 @@ namespace DesignTools
|
||||
resSelectedFolderCache[data.Id] = currentFolder;
|
||||
// 组改变时,清空表和Item选择,并清空Res数据
|
||||
currentTable = null;
|
||||
tableId = -1;
|
||||
itemId = -1;
|
||||
tableName = "";
|
||||
itemName = "";
|
||||
data.Res = ""; // 清空Res,避免旧数据干扰
|
||||
}
|
||||
else if (newFolderIndex == 0)
|
||||
@ -1405,7 +1399,7 @@ namespace DesignTools
|
||||
int currentTableIndex = 0;
|
||||
if (currentTable != null && !folderChanged)
|
||||
{
|
||||
int foundIndex = tablesInFolder.FindIndex(t => t.TableId == currentTable.TableId);
|
||||
int foundIndex = tablesInFolder.FindIndex(t => t.TableName == currentTable.TableName);
|
||||
if (foundIndex >= 0)
|
||||
{
|
||||
currentTableIndex = foundIndex + 1; // +1 因为有"未选择"选项
|
||||
@ -1422,8 +1416,8 @@ namespace DesignTools
|
||||
{
|
||||
currentTable = tablesInFolder[newTableIndex - 1]; // -1 因为有"未选择"选项
|
||||
// 表格改变时,清空Item选择
|
||||
data.Res = $"{currentTable.TableId},-1";
|
||||
itemId = -1;
|
||||
data.Res = $"{currentTable.TableName},";
|
||||
itemName = "";
|
||||
}
|
||||
else if (newTableIndex == 0)
|
||||
{
|
||||
@ -1452,9 +1446,9 @@ namespace DesignTools
|
||||
itemOptions.AddRange(currentTable.Items.Select(item => $"{item.Name}(ID:{item.Id})"));
|
||||
|
||||
int currentItemIndex = 0;
|
||||
if (itemId >= 0 && !tableSelectionChanged)
|
||||
if (!string.IsNullOrEmpty(itemName) && !tableSelectionChanged)
|
||||
{
|
||||
int foundIndex = currentTable.Items.FindIndex(item => item.Id == itemId);
|
||||
int foundIndex = currentTable.Items.FindIndex(item => item.Name == itemName);
|
||||
if (foundIndex >= 0)
|
||||
{
|
||||
currentItemIndex = foundIndex + 1; // +1 因为有"未选择"选项
|
||||
@ -1467,12 +1461,12 @@ namespace DesignTools
|
||||
{
|
||||
if (newItemIndex > 0 && newItemIndex <= currentTable.Items.Count)
|
||||
{
|
||||
data.Res = $"{currentTable.TableId},{currentTable.Items[newItemIndex - 1].Id}"; // -1 因为有"未选择"选项
|
||||
data.Res = $"{currentTable.TableName},{currentTable.Items[newItemIndex - 1].Name}";
|
||||
}
|
||||
else if (newItemIndex == 0)
|
||||
{
|
||||
// 选择"未选择"
|
||||
data.Res = $"{currentTable.TableId},-1";
|
||||
data.Res = $"{currentTable.TableName},";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1523,8 +1517,9 @@ namespace DesignTools
|
||||
|
||||
if (!avatarDict.ContainsKey(avatarId)) return null;
|
||||
|
||||
int iconId = avatarDict[avatarId].IconId;
|
||||
var item = headFrameResourceSO.Items.FirstOrDefault(x => x.Id == iconId);
|
||||
string iconName = avatarDict[avatarId].IconName;
|
||||
if (string.IsNullOrEmpty(iconName)) return null;
|
||||
var item = headFrameResourceSO.Items.FirstOrDefault(x => x.Name == iconName);
|
||||
|
||||
return item?.Sprite;
|
||||
}
|
||||
@ -1543,8 +1538,9 @@ namespace DesignTools
|
||||
|
||||
if (!faceDict.ContainsKey(faceId)) return null;
|
||||
|
||||
int iconId = faceDict[faceId].IconId;
|
||||
var item = headResourceSO.Items.FirstOrDefault(x => x.Id == iconId);
|
||||
string iconName = faceDict[faceId].IconName;
|
||||
if (string.IsNullOrEmpty(iconName)) return null;
|
||||
var item = headResourceSO.Items.FirstOrDefault(x => x.Name == iconName);
|
||||
|
||||
return item?.Sprite;
|
||||
}
|
||||
@ -1563,8 +1559,9 @@ namespace DesignTools
|
||||
|
||||
if (!emojiDict.ContainsKey(emojiId)) return null;
|
||||
|
||||
int iconId = emojiDict[emojiId].IconId;
|
||||
var item = emojiResourceSO.Items.FirstOrDefault(x => x.Id == iconId);
|
||||
string iconName = emojiDict[emojiId].IconName;
|
||||
if (string.IsNullOrEmpty(iconName)) return null;
|
||||
var item = emojiResourceSO.Items.FirstOrDefault(x => x.Name == iconName);
|
||||
|
||||
return item?.Sprite;
|
||||
}
|
||||
@ -1579,13 +1576,15 @@ namespace DesignTools
|
||||
string[] parts = data.Res.Split(',');
|
||||
if (parts.Length < 2) return null;
|
||||
|
||||
if (!int.TryParse(parts[0], out int tableId)) return null;
|
||||
if (!int.TryParse(parts[1], out int itemId)) return null;
|
||||
string tableName = parts[0];
|
||||
string artItemName = parts[1];
|
||||
|
||||
var table = allArtTables.FirstOrDefault(x => x.TableId == tableId);
|
||||
if (string.IsNullOrEmpty(tableName) || string.IsNullOrEmpty(artItemName)) return null;
|
||||
|
||||
var table = allArtTables.FirstOrDefault(x => x.TableName == tableName);
|
||||
if (table == null) return null;
|
||||
|
||||
var item = table.Items.FirstOrDefault(x => x.Id == itemId);
|
||||
var item = table.Items.FirstOrDefault(x => x.Name == artItemName);
|
||||
return item?.Sprite;
|
||||
}
|
||||
|
||||
@ -1634,15 +1633,15 @@ namespace DesignTools
|
||||
string[] parts = res.Split(',');
|
||||
if (parts.Length < 2) return "";
|
||||
|
||||
if (!int.TryParse(parts[0], out int tableId)) return "";
|
||||
if (!int.TryParse(parts[1], out int itemId)) return "";
|
||||
string tableName = parts[0];
|
||||
string artItemName = parts[1];
|
||||
|
||||
if (itemId < 0) return ""; // 未选择Item
|
||||
if (string.IsNullOrEmpty(tableName) || string.IsNullOrEmpty(artItemName)) return "";
|
||||
|
||||
var table = allArtTables.FirstOrDefault(x => x.TableId == tableId);
|
||||
var table = allArtTables.FirstOrDefault(x => x.TableName == tableName);
|
||||
if (table == null) return "";
|
||||
|
||||
var item = table.Items.FirstOrDefault(x => x.Id == itemId);
|
||||
var item = table.Items.FirstOrDefault(x => x.Name == artItemName);
|
||||
if (item == null) return "";
|
||||
if (item.Sprite == null) return "";
|
||||
|
||||
@ -1814,7 +1813,7 @@ namespace DesignTools
|
||||
{
|
||||
public int Id;
|
||||
public string NameKey;
|
||||
public int IconId;
|
||||
public string IconName = "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -1824,7 +1823,7 @@ namespace DesignTools
|
||||
{
|
||||
public int Id;
|
||||
public string NameKey;
|
||||
public int IconId;
|
||||
public string IconName = "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -1834,7 +1833,7 @@ namespace DesignTools
|
||||
{
|
||||
public int Id;
|
||||
public string NameKey;
|
||||
public int IconId;
|
||||
public string IconName = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
170
Scripts/Editor/Design_Tools/MigrateIconFormatTool.cs
Normal file
170
Scripts/Editor/Design_Tools/MigrateIconFormatTool.cs
Normal file
@ -0,0 +1,170 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using OfficeOpenXml;
|
||||
using ArtResource;
|
||||
|
||||
namespace DesignTools
|
||||
{
|
||||
/// <summary>
|
||||
/// 一次性工具:将 Emoji.xlsx / Face.xlsx / Avatar.xlsx 的 Icon 字段从 ArtItemData.Id 迁移为 ArtItemData.Name
|
||||
/// 使用完毕后可删除此文件
|
||||
/// </summary>
|
||||
public static class MigrateIconFormatTool
|
||||
{
|
||||
private static readonly string[] SO_SEARCH_PATHS = new[]
|
||||
{
|
||||
"Assets/Art_SubModule/Art_SO",
|
||||
"Assets/Art_SubModule/Art_SO/Collections"
|
||||
};
|
||||
|
||||
private struct MigrationTarget
|
||||
{
|
||||
public string ExcelName;
|
||||
public string SheetName;
|
||||
public string SOTableName;
|
||||
}
|
||||
|
||||
private static readonly MigrationTarget[] Targets = new[]
|
||||
{
|
||||
new MigrationTarget { ExcelName = "Emoji.xlsx", SheetName = "Emoji", SOTableName = "EmojiResource" },
|
||||
new MigrationTarget { ExcelName = "Face.xlsx", SheetName = "Face", SOTableName = "HeadResource" },
|
||||
new MigrationTarget { ExcelName = "Avatar.xlsx", SheetName = "Avatar", SOTableName = "HeadFrameResource" },
|
||||
};
|
||||
|
||||
[MenuItem("策划工具/一次性迁移/Icon格式 ID→Name (Emoji+Face+Avatar)")]
|
||||
public static void Execute()
|
||||
{
|
||||
// 1. 选择 Docs/config 目录
|
||||
string configDir = EditorUtility.OpenFolderPanel("选择 Docs/config 目录(包含 Emoji.xlsx、Face.xlsx、Avatar.xlsx)", "", "");
|
||||
if (string.IsNullOrEmpty(configDir)) return;
|
||||
|
||||
// 2. 加载所有 ArtTableSO
|
||||
var allArtTables = new List<ArtTableSO>();
|
||||
foreach (string searchPath in SO_SEARCH_PATHS)
|
||||
{
|
||||
string[] guids = AssetDatabase.FindAssets("t:ArtTableSO", new[] { searchPath });
|
||||
foreach (string guid in guids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var so = AssetDatabase.LoadAssetAtPath<ArtTableSO>(path);
|
||||
if (so != null && !allArtTables.Any(x => x.TableName == so.TableName))
|
||||
allArtTables.Add(so);
|
||||
}
|
||||
}
|
||||
|
||||
if (allArtTables.Count == 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", "未找到任何 ArtTableSO 资源", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
int totalMigrated = 0;
|
||||
int totalSkipped = 0;
|
||||
int totalErrors = 0;
|
||||
var allMessages = new List<string>();
|
||||
|
||||
foreach (var target in Targets)
|
||||
{
|
||||
string excelPath = Path.Combine(configDir, target.ExcelName);
|
||||
if (!File.Exists(excelPath))
|
||||
{
|
||||
allMessages.Add($"[{target.ExcelName}] 文件不存在,跳过");
|
||||
continue;
|
||||
}
|
||||
|
||||
var tableSO = allArtTables.FirstOrDefault(x => x.TableName == target.SOTableName);
|
||||
if (tableSO == null)
|
||||
{
|
||||
allMessages.Add($"[{target.ExcelName}] 未找到 ArtTableSO: {target.SOTableName},跳过");
|
||||
totalErrors++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 备份
|
||||
string backupPath = excelPath + ".bak";
|
||||
File.Copy(excelPath, backupPath, true);
|
||||
|
||||
int migrated = 0, skipped = 0, errors = 0;
|
||||
|
||||
using (var package = new ExcelPackage(new FileInfo(excelPath)))
|
||||
{
|
||||
var ws = package.Workbook.Worksheets[target.SheetName];
|
||||
if (ws == null)
|
||||
{
|
||||
allMessages.Add($"[{target.ExcelName}] 未找到 Sheet: {target.SheetName},跳过");
|
||||
totalErrors++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 找 Icon 列和 Id 列
|
||||
int iconCol = -1, idCol = -1;
|
||||
int colCount = ws.Dimension?.Columns ?? 0;
|
||||
for (int c = 1; c <= colCount; c++)
|
||||
{
|
||||
string h = ws.Cells[1, c].Text;
|
||||
if (h == "Icon") iconCol = c;
|
||||
else if (h == "Id") idCol = c;
|
||||
}
|
||||
|
||||
if (iconCol < 0)
|
||||
{
|
||||
allMessages.Add($"[{target.ExcelName}] 未找到 Icon 列,跳过");
|
||||
totalErrors++;
|
||||
continue;
|
||||
}
|
||||
|
||||
int rowCount = ws.Dimension?.Rows ?? 0;
|
||||
for (int row = 3; row <= rowCount; row++)
|
||||
{
|
||||
string iconText = ws.Cells[row, iconCol].Text.Trim();
|
||||
if (string.IsNullOrEmpty(iconText))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 判断是否是旧格式(纯数字)
|
||||
if (!int.TryParse(iconText, out int artItemId))
|
||||
{
|
||||
// 已经是Name格式
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
string rowId = idCol > 0 ? ws.Cells[row, idCol].Text : row.ToString();
|
||||
|
||||
var artItem = tableSO.Items.FirstOrDefault(x => x.Id == artItemId);
|
||||
if (artItem == null)
|
||||
{
|
||||
errors++;
|
||||
allMessages.Add($"[{target.ExcelName}] Row {row} Id={rowId}: {target.SOTableName} 中无 ArtItemId={artItemId}");
|
||||
continue;
|
||||
}
|
||||
|
||||
ws.Cells[row, iconCol].Value = artItem.Name;
|
||||
migrated++;
|
||||
}
|
||||
|
||||
package.Save();
|
||||
}
|
||||
|
||||
totalMigrated += migrated;
|
||||
totalSkipped += skipped;
|
||||
totalErrors += errors;
|
||||
allMessages.Add($"[{target.ExcelName}] 成功: {migrated}, 跳过: {skipped}, 错误: {errors} (备份: {backupPath})");
|
||||
}
|
||||
|
||||
// 报告
|
||||
string msg = $"Icon 迁移完成!\n\n" +
|
||||
$"总计成功: {totalMigrated} 条\n" +
|
||||
$"已是新格式(跳过): {totalSkipped} 条\n" +
|
||||
$"错误: {totalErrors} 条\n\n" +
|
||||
string.Join("\n", allMessages);
|
||||
|
||||
EditorUtility.DisplayDialog("迁移结果", msg, "确定");
|
||||
Debug.Log(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Scripts/Editor/Design_Tools/MigrateIconFormatTool.cs.meta
Normal file
11
Scripts/Editor/Design_Tools/MigrateIconFormatTool.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6514ff4e26ea0ff429517847383cd800
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
155
Scripts/Editor/Design_Tools/MigrateResFormatTool.cs
Normal file
155
Scripts/Editor/Design_Tools/MigrateResFormatTool.cs
Normal file
@ -0,0 +1,155 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using OfficeOpenXml;
|
||||
using ArtResource;
|
||||
|
||||
namespace DesignTools
|
||||
{
|
||||
/// <summary>
|
||||
/// 一次性工具:将 Item.xlsx 的 Res 字段从 "TableId,ItemId" 迁移为 "TableName,ItemName"
|
||||
/// 使用完毕后可删除此文件
|
||||
/// </summary>
|
||||
public static class MigrateResFormatTool
|
||||
{
|
||||
private const string ART_SO_PATH = "Assets/Art_SubModule/Art_SO";
|
||||
private const string ITEM_SHEET_NAME = "Item";
|
||||
|
||||
[MenuItem("策划工具/一次性迁移/Res格式 ID→Name")]
|
||||
public static void Execute()
|
||||
{
|
||||
// 1. 选择 Item.xlsx
|
||||
string excelPath = EditorUtility.OpenFilePanel("选择 Item.xlsx", "", "xlsx");
|
||||
if (string.IsNullOrEmpty(excelPath)) return;
|
||||
|
||||
if (!File.Exists(excelPath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", "文件不存在", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 备份
|
||||
string backupPath = excelPath + ".bak";
|
||||
File.Copy(excelPath, backupPath, true);
|
||||
Debug.Log($"已备份到: {backupPath}");
|
||||
|
||||
// 3. 加载所有 ArtTableSO
|
||||
var allArtTables = new List<ArtTableSO>();
|
||||
string[] guids = AssetDatabase.FindAssets("t:ArtTableSO", new[] { ART_SO_PATH });
|
||||
foreach (string guid in guids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var so = AssetDatabase.LoadAssetAtPath<ArtTableSO>(path);
|
||||
if (so != null) allArtTables.Add(so);
|
||||
}
|
||||
|
||||
if (allArtTables.Count == 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", "未找到任何 ArtTableSO 资源", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 读取并修改 Excel
|
||||
int migratedCount = 0;
|
||||
int skippedCount = 0;
|
||||
int errorCount = 0;
|
||||
var errors = new List<string>();
|
||||
|
||||
using (var package = new ExcelPackage(new FileInfo(excelPath)))
|
||||
{
|
||||
var ws = package.Workbook.Worksheets[ITEM_SHEET_NAME];
|
||||
if (ws == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", $"未找到 Sheet: {ITEM_SHEET_NAME}", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
// 找 Res 列
|
||||
int resCol = -1;
|
||||
int idCol = -1;
|
||||
int nameCol = -1;
|
||||
int colCount = ws.Dimension?.Columns ?? 0;
|
||||
for (int c = 1; c <= colCount; c++)
|
||||
{
|
||||
string h = ws.Cells[1, c].Text;
|
||||
if (h == "Res") resCol = c;
|
||||
else if (h == "Id") idCol = c;
|
||||
else if (h == "Name") nameCol = c;
|
||||
}
|
||||
|
||||
if (resCol < 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", "未找到 Res 列", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
int rowCount = ws.Dimension?.Rows ?? 0;
|
||||
for (int row = 3; row <= rowCount; row++)
|
||||
{
|
||||
string res = ws.Cells[row, resCol].Text.Trim();
|
||||
if (string.IsNullOrEmpty(res)) continue;
|
||||
|
||||
string[] parts = res.Split(',');
|
||||
if (parts.Length < 2) continue;
|
||||
|
||||
// 判断是否是旧格式(首段为数字)
|
||||
if (!int.TryParse(parts[0], out int tableId))
|
||||
{
|
||||
skippedCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
int.TryParse(parts[1], out int artItemId);
|
||||
|
||||
string rowId = idCol > 0 ? ws.Cells[row, idCol].Text : row.ToString();
|
||||
string rowName = nameCol > 0 ? ws.Cells[row, nameCol].Text : "";
|
||||
|
||||
var table = allArtTables.FirstOrDefault(x => x.TableId == tableId);
|
||||
if (table == null)
|
||||
{
|
||||
errorCount++;
|
||||
errors.Add($"Row {row} Id={rowId}({rowName}): TableId={tableId} 不存在");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (artItemId < 0)
|
||||
{
|
||||
ws.Cells[row, resCol].Value = $"{table.TableName},";
|
||||
migratedCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
var artItem = table.Items.FirstOrDefault(x => x.Id == artItemId);
|
||||
if (artItem == null)
|
||||
{
|
||||
errorCount++;
|
||||
errors.Add($"Row {row} Id={rowId}({rowName}): Table={table.TableName} 中无 ItemId={artItemId}");
|
||||
continue;
|
||||
}
|
||||
|
||||
ws.Cells[row, resCol].Value = $"{table.TableName},{artItem.Name}";
|
||||
migratedCount++;
|
||||
}
|
||||
|
||||
package.Save();
|
||||
}
|
||||
|
||||
// 5. 报告结果
|
||||
string msg = $"迁移完成!\n\n" +
|
||||
$"成功: {migratedCount} 条\n" +
|
||||
$"已是新格式(跳过): {skippedCount} 条\n" +
|
||||
$"错误: {errorCount} 条\n\n" +
|
||||
$"备份文件: {backupPath}";
|
||||
|
||||
if (errorCount > 0)
|
||||
{
|
||||
msg += $"\n\n错误详情:\n{string.Join("\n", errors)}";
|
||||
}
|
||||
|
||||
EditorUtility.DisplayDialog("Res迁移结果", msg, "确定");
|
||||
Debug.Log(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Scripts/Editor/Design_Tools/MigrateResFormatTool.cs.meta
Normal file
11
Scripts/Editor/Design_Tools/MigrateResFormatTool.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 268a5a15fc9d3254db43243915bef064
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -8,6 +8,7 @@ using UnityEditor;
|
||||
using UnityEngine;
|
||||
using OfficeOpenXml;
|
||||
using ArtResource;
|
||||
using DesignTools.Common;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace DesignTools.Scene
|
||||
@ -101,11 +102,15 @@ namespace DesignTools.Scene
|
||||
|
||||
// 道具奖励临时编辑状态(按数据ID存储)
|
||||
private Dictionary<int, int> tempItemRewardTypeIndex = new Dictionary<int, int>();
|
||||
private Dictionary<int, int> tempItemRewardInputId = new Dictionary<int, int>();
|
||||
|
||||
// 区域奖励临时编辑状态(按数据ID存储)
|
||||
private Dictionary<int, int> tempAreaRewardTypeIndex = new Dictionary<int, int>();
|
||||
private Dictionary<int, int> tempAreaRewardItemId = new Dictionary<int, int>();
|
||||
private Dictionary<int, int> tempAreaRewardNum = new Dictionary<int, int>();
|
||||
|
||||
// 所有IType列表(动态生成)
|
||||
private List<int> allItemTypes = new List<int>();
|
||||
|
||||
[MenuItem("策划工具/场景/场景进度奖励")]
|
||||
public static void ShowWindow()
|
||||
@ -484,6 +489,9 @@ namespace DesignTools.Scene
|
||||
}
|
||||
|
||||
Debug.Log($"加载了 {itemDict.Count} 个Item数据");
|
||||
|
||||
// 生成所有IType列表
|
||||
allItemTypes = itemDict.Values.Select(x => x.IType).Distinct().OrderBy(x => x).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -752,12 +760,14 @@ namespace DesignTools.Scene
|
||||
|
||||
/// <summary>
|
||||
/// 绘制道具奖励编辑器(Item/Emit/Reward)
|
||||
/// 使用输入框+查找按钮+弹窗选择的方式,支持所有类型
|
||||
/// </summary>
|
||||
private void DrawItemRewardEditor(IndoorProgressData data)
|
||||
{
|
||||
// 解析当前数据
|
||||
var itemReward = ParseItemReward(data.Item);
|
||||
|
||||
EditorGUILayout.BeginVertical();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
// 选择IType
|
||||
@ -770,7 +780,7 @@ namespace DesignTools.Scene
|
||||
typeValues.Add(kvp.Key);
|
||||
}
|
||||
|
||||
// 初始化临时状态:如果有数据就用数据的类型,如果没数据就用临时保存的选择
|
||||
// 初始化临时状态
|
||||
if (!tempItemRewardTypeIndex.ContainsKey(data.Id))
|
||||
{
|
||||
if (itemReward.HasValue)
|
||||
@ -780,12 +790,11 @@ namespace DesignTools.Scene
|
||||
}
|
||||
else
|
||||
{
|
||||
tempItemRewardTypeIndex[data.Id] = 0; // 默认"无"
|
||||
tempItemRewardTypeIndex[data.Id] = 0;
|
||||
}
|
||||
}
|
||||
else if (itemReward.HasValue)
|
||||
{
|
||||
// 如果有数据,同步临时状态
|
||||
int itemType = GetItemType(itemReward.Value.Id);
|
||||
int typeIndex = typeValues.IndexOf(itemType);
|
||||
if (typeIndex >= 0)
|
||||
@ -794,6 +803,12 @@ namespace DesignTools.Scene
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化输入ID缓存
|
||||
if (!tempItemRewardInputId.ContainsKey(data.Id))
|
||||
{
|
||||
tempItemRewardInputId[data.Id] = itemReward.HasValue ? itemReward.Value.Id : 0;
|
||||
}
|
||||
|
||||
int currentTypeIndex = tempItemRewardTypeIndex[data.Id];
|
||||
if (currentTypeIndex < 0) currentTypeIndex = 0;
|
||||
|
||||
@ -814,6 +829,7 @@ namespace DesignTools.Scene
|
||||
data.Item = "";
|
||||
data.Emit = "";
|
||||
data.Reward = "";
|
||||
tempItemRewardInputId[data.Id] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -827,110 +843,115 @@ namespace DesignTools.Scene
|
||||
data.Emit = "";
|
||||
data.Reward = "";
|
||||
itemReward = null;
|
||||
tempItemRewardInputId[data.Id] = 0;
|
||||
}
|
||||
|
||||
// 根据类型筛选Item
|
||||
var itemsOfType = itemDict.Values.Where(x => x.IType == selectedType).ToList();
|
||||
// 统一使用输入框+查找按钮方式
|
||||
int inputId = tempItemRewardInputId[data.Id];
|
||||
|
||||
if (itemsOfType.Count > 0)
|
||||
EditorGUILayout.LabelField("ID:", GUILayout.Width(25));
|
||||
int newInputId = EditorGUILayout.IntField(inputId, GUILayout.Width(70));
|
||||
if (newInputId != inputId)
|
||||
{
|
||||
// IType=100(棋子)使用输入框+刷新按钮,其他使用下拉菜单
|
||||
if (selectedType == 100)
|
||||
tempItemRewardInputId[data.Id] = newInputId;
|
||||
if (itemDict.ContainsKey(newInputId))
|
||||
{
|
||||
// 输入ID
|
||||
EditorGUILayout.LabelField("ID:", GUILayout.Width(25));
|
||||
|
||||
// 当前ID:如果类型改变,重置为0;否则使用现有值
|
||||
int inputId = 0;
|
||||
if (!typeChanged && itemReward.HasValue && GetItemType(itemReward.Value.Id) == 100)
|
||||
{
|
||||
inputId = itemReward.Value.Id;
|
||||
}
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
inputId = EditorGUILayout.IntField(inputId, GUILayout.Width(60));
|
||||
bool idChanged = EditorGUI.EndChangeCheck();
|
||||
|
||||
// 数量
|
||||
EditorGUILayout.LabelField("数量:", GUILayout.Width(35));
|
||||
int num = itemReward.HasValue ? itemReward.Value.Num : 1;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
num = EditorGUILayout.IntField(num, GUILayout.Width(40));
|
||||
bool numChanged = EditorGUI.EndChangeCheck();
|
||||
|
||||
// 刷新按钮
|
||||
if (GUILayout.Button("刷新", GUILayout.Width(50)))
|
||||
{
|
||||
if (inputId > 0 && itemDict.ContainsKey(inputId) && itemDict[inputId].IType == 100)
|
||||
{
|
||||
UpdateItemReward(data, inputId, num);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", $"ID {inputId} 不存在或不是棋子类型", "确定");
|
||||
}
|
||||
}
|
||||
|
||||
// 如果ID或数量改变,自动更新
|
||||
if (idChanged || numChanged)
|
||||
{
|
||||
if (inputId > 0 && itemDict.ContainsKey(inputId) && itemDict[inputId].IType == 100)
|
||||
{
|
||||
UpdateItemReward(data, inputId, num);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示预览图
|
||||
if (inputId > 0 && itemDict.TryGetValue(inputId, out var item) && item.IType == 100)
|
||||
{
|
||||
DrawItemPreview(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField("暂无预览", GUILayout.Width(60));
|
||||
}
|
||||
UpdateItemReward(data, newInputId, num);
|
||||
// 同步类型选择
|
||||
int newItemType = GetItemType(newInputId);
|
||||
int newTypeIdx = typeValues.IndexOf(newItemType);
|
||||
if (newTypeIdx >= 0) tempItemRewardTypeIndex[data.Id] = newTypeIdx;
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
// 查找按钮
|
||||
GUI.backgroundColor = new Color(0.75f, 0.9f, 1f);
|
||||
if (GUILayout.Button("查找", GUILayout.Width(45), GUILayout.Height(18)))
|
||||
{
|
||||
int capturedDataId = data.Id;
|
||||
int currentType = selectedType;
|
||||
EditorApplication.delayCall += () => OpenItemPickerForReward(capturedDataId, currentType);
|
||||
GUI.backgroundColor = Color.white;
|
||||
GUIUtility.ExitGUI();
|
||||
}
|
||||
GUI.backgroundColor = Color.white;
|
||||
|
||||
// 数量
|
||||
EditorGUILayout.LabelField("x", GUILayout.Width(10));
|
||||
int currentNum = itemReward.HasValue ? itemReward.Value.Num : 1;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
currentNum = EditorGUILayout.IntField(currentNum, GUILayout.Width(40));
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
int currentId = tempItemRewardInputId[data.Id];
|
||||
if (currentId > 0 && itemDict.ContainsKey(currentId))
|
||||
{
|
||||
// 其他类型使用下拉菜单
|
||||
var itemOptions = itemsOfType.Select(x => $"{x.Name} ({x.Id})").ToList();
|
||||
var itemIds = itemsOfType.Select(x => x.Id).ToList();
|
||||
|
||||
int currentItemIndex = 0;
|
||||
if (!typeChanged && itemReward.HasValue)
|
||||
{
|
||||
currentItemIndex = itemIds.IndexOf(itemReward.Value.Id);
|
||||
if (currentItemIndex < 0) currentItemIndex = 0;
|
||||
}
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
int newItemIndex = EditorGUILayout.Popup(currentItemIndex, itemOptions.ToArray(), GUILayout.Width(180));
|
||||
int selectedItemId = itemIds[newItemIndex];
|
||||
|
||||
// 数量
|
||||
int num = itemReward.HasValue ? itemReward.Value.Num : 1;
|
||||
num = EditorGUILayout.IntField(num, GUILayout.Width(40));
|
||||
|
||||
// 更新数据
|
||||
if (EditorGUI.EndChangeCheck() || typeChanged || !itemReward.HasValue)
|
||||
{
|
||||
UpdateItemReward(data, selectedItemId, num);
|
||||
}
|
||||
|
||||
// 显示预览图
|
||||
if (itemDict.TryGetValue(selectedItemId, out var item2))
|
||||
{
|
||||
DrawItemPreview(item2);
|
||||
}
|
||||
UpdateItemReward(data, currentId, currentNum);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示预览图
|
||||
int previewId = tempItemRewardInputId[data.Id];
|
||||
if (previewId > 0 && itemDict.TryGetValue(previewId, out var previewItem))
|
||||
{
|
||||
DrawItemPreview(previewItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField("该类型无可用Item", GUILayout.Width(150));
|
||||
EditorGUILayout.LabelField("无", GUILayout.Width(50));
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
// 显示当前道具名称
|
||||
if (itemReward.HasValue && itemDict.TryGetValue(itemReward.Value.Id, out var currentItem))
|
||||
{
|
||||
EditorGUILayout.LabelField($" 当前: {currentItem.Name} ({currentItem.Id})", EditorStyles.miniLabel);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打开Item选择弹窗(道具奖励用)
|
||||
/// </summary>
|
||||
private void OpenItemPickerForReward(int dataId, int currentType)
|
||||
{
|
||||
var data = allProgressDataList.FirstOrDefault(x => x.Id == dataId);
|
||||
if (data == null) data = filteredProgressDataList.FirstOrDefault(x => x.Id == dataId);
|
||||
if (data == null) return;
|
||||
|
||||
var itemReward = ParseItemReward(data.Item);
|
||||
int selectedItemId = itemReward.HasValue ? itemReward.Value.Id : 0;
|
||||
|
||||
DesignToolItemPickerWindow.ShowWindow(
|
||||
itemDict.Values
|
||||
.Select(x => new DesignToolItemPickerItem { Id = x.Id, Name = x.Name, IType = x.IType })
|
||||
.ToList(),
|
||||
allItemTypes,
|
||||
ITEM_TYPES,
|
||||
currentType,
|
||||
selectedItemId,
|
||||
position,
|
||||
pickedId =>
|
||||
{
|
||||
if (itemDict.ContainsKey(pickedId))
|
||||
{
|
||||
int num = itemReward.HasValue ? itemReward.Value.Num : 1;
|
||||
UpdateItemReward(data, pickedId, num);
|
||||
tempItemRewardInputId[dataId] = pickedId;
|
||||
// 同步类型
|
||||
int pickedType = GetItemType(pickedId);
|
||||
var typeValues2 = new List<int> { -1 };
|
||||
foreach (var kvp in ITEM_TYPES.OrderBy(x => x.Key))
|
||||
typeValues2.Add(kvp.Key);
|
||||
int typeIdx = typeValues2.IndexOf(pickedType);
|
||||
if (typeIdx >= 0) tempItemRewardTypeIndex[dataId] = typeIdx;
|
||||
Repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -1024,26 +1045,13 @@ namespace DesignTools.Scene
|
||||
|
||||
/// <summary>
|
||||
/// 绘制添加区域奖励UI
|
||||
/// 使用输入框+查找按钮+弹窗选择的方式
|
||||
/// </summary>
|
||||
private void DrawAddAreaRewardUI(IndoorProgressData data, List<ItemReward> areaRewards)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
// 选择IType
|
||||
var typeOptions = new List<string> { "选择类型..." };
|
||||
var typeValues = new List<int> { -1 };
|
||||
|
||||
foreach (var kvp in ITEM_TYPES.OrderBy(x => x.Key))
|
||||
{
|
||||
typeOptions.Add($"{kvp.Value} ({kvp.Key})");
|
||||
typeValues.Add(kvp.Key);
|
||||
}
|
||||
|
||||
// 使用临时变量存储当前选择
|
||||
if (!tempAreaRewardTypeIndex.ContainsKey(data.Id))
|
||||
{
|
||||
tempAreaRewardTypeIndex[data.Id] = 0;
|
||||
}
|
||||
if (!tempAreaRewardItemId.ContainsKey(data.Id))
|
||||
{
|
||||
tempAreaRewardItemId[data.Id] = 0;
|
||||
@ -1053,116 +1061,92 @@ namespace DesignTools.Scene
|
||||
tempAreaRewardNum[data.Id] = 1;
|
||||
}
|
||||
|
||||
int currentTypeIndex = tempAreaRewardTypeIndex[data.Id];
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
int newTypeIndex = EditorGUILayout.Popup(currentTypeIndex, typeOptions.ToArray(), GUILayout.Width(120));
|
||||
bool typeChanged = EditorGUI.EndChangeCheck();
|
||||
|
||||
if (typeChanged)
|
||||
// 输入ID
|
||||
EditorGUILayout.LabelField("ID:", GUILayout.Width(25));
|
||||
int inputId = tempAreaRewardItemId[data.Id];
|
||||
int newInputId = EditorGUILayout.IntField(inputId, GUILayout.Width(70));
|
||||
if (newInputId != inputId)
|
||||
{
|
||||
tempAreaRewardTypeIndex[data.Id] = newTypeIndex;
|
||||
tempAreaRewardItemId[data.Id] = 0; // 重置ItemId
|
||||
tempAreaRewardItemId[data.Id] = newInputId;
|
||||
}
|
||||
|
||||
if (newTypeIndex > 0)
|
||||
// 查找按钮
|
||||
GUI.backgroundColor = new Color(0.75f, 0.9f, 1f);
|
||||
if (GUILayout.Button("查找", GUILayout.Width(45), GUILayout.Height(18)))
|
||||
{
|
||||
int selectedType = typeValues[newTypeIndex];
|
||||
|
||||
// 根据类型筛选Item
|
||||
var itemsOfType = itemDict.Values.Where(x => x.IType == selectedType).ToList();
|
||||
|
||||
if (itemsOfType.Count > 0)
|
||||
int capturedDataId = data.Id;
|
||||
EditorApplication.delayCall += () => OpenItemPickerForAreaReward(capturedDataId);
|
||||
GUI.backgroundColor = Color.white;
|
||||
GUIUtility.ExitGUI();
|
||||
}
|
||||
GUI.backgroundColor = Color.white;
|
||||
|
||||
// 显示道具名称
|
||||
int currentInputId = tempAreaRewardItemId[data.Id];
|
||||
if (currentInputId > 0 && itemDict.TryGetValue(currentInputId, out var inputItem))
|
||||
{
|
||||
EditorGUILayout.LabelField($"{inputItem.Name}", EditorStyles.miniLabel, GUILayout.Width(100));
|
||||
}
|
||||
else if (currentInputId > 0)
|
||||
{
|
||||
Color oldColor = GUI.color;
|
||||
GUI.color = new Color(1f, 0.35f, 0.35f);
|
||||
EditorGUILayout.LabelField("未找到", EditorStyles.miniLabel, GUILayout.Width(100));
|
||||
GUI.color = oldColor;
|
||||
}
|
||||
|
||||
// 数量
|
||||
EditorGUILayout.LabelField("x", GUILayout.Width(10));
|
||||
int num = tempAreaRewardNum[data.Id];
|
||||
num = EditorGUILayout.IntField(num, GUILayout.Width(40));
|
||||
tempAreaRewardNum[data.Id] = num;
|
||||
|
||||
// 添加按钮
|
||||
GUI.backgroundColor = Color.green;
|
||||
if (GUILayout.Button("+添加", GUILayout.Width(50)))
|
||||
{
|
||||
int addId = tempAreaRewardItemId[data.Id];
|
||||
if (addId > 0 && itemDict.ContainsKey(addId))
|
||||
{
|
||||
// IType=100(棋子)使用输入框+添加按钮
|
||||
if (selectedType == 100)
|
||||
{
|
||||
EditorGUILayout.LabelField("ID:", GUILayout.Width(25));
|
||||
int inputId = tempAreaRewardItemId[data.Id];
|
||||
inputId = EditorGUILayout.IntField(inputId, GUILayout.Width(60));
|
||||
tempAreaRewardItemId[data.Id] = inputId;
|
||||
|
||||
EditorGUILayout.LabelField("数量:", GUILayout.Width(35));
|
||||
int num = tempAreaRewardNum[data.Id];
|
||||
num = EditorGUILayout.IntField(num, GUILayout.Width(40));
|
||||
tempAreaRewardNum[data.Id] = num;
|
||||
|
||||
if (GUILayout.Button("添加", GUILayout.Width(50)))
|
||||
{
|
||||
if (itemDict.ContainsKey(inputId) && itemDict[inputId].IType == 100)
|
||||
{
|
||||
areaRewards.Add(new ItemReward { Id = inputId, Num = num });
|
||||
UpdateAreaReward(data, areaRewards);
|
||||
// 重置
|
||||
tempAreaRewardTypeIndex[data.Id] = 0;
|
||||
tempAreaRewardItemId[data.Id] = 0;
|
||||
tempAreaRewardNum[data.Id] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", $"ID {inputId} 不存在或不是棋子类型", "确定");
|
||||
}
|
||||
}
|
||||
|
||||
// 显示预览图
|
||||
if (itemDict.TryGetValue(inputId, out var previewItem) && previewItem.IType == 100)
|
||||
{
|
||||
DrawItemPreview(previewItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField("暂无预览", GUILayout.Width(60));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 其他类型使用下拉菜单
|
||||
var itemOptions = itemsOfType.Select(x => $"{x.Name} ({x.Id})").ToList();
|
||||
var itemIds = itemsOfType.Select(x => x.Id).ToList();
|
||||
|
||||
int savedItemId = tempAreaRewardItemId[data.Id];
|
||||
int currentItemIndex = itemIds.IndexOf(savedItemId);
|
||||
if (currentItemIndex < 0) currentItemIndex = 0;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
int newItemIndex = EditorGUILayout.Popup(currentItemIndex, itemOptions.ToArray(), GUILayout.Width(150));
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
tempAreaRewardItemId[data.Id] = itemIds[newItemIndex];
|
||||
}
|
||||
|
||||
int selectedItemId = itemIds[newItemIndex];
|
||||
|
||||
// 数量
|
||||
int num = tempAreaRewardNum[data.Id];
|
||||
num = EditorGUILayout.IntField(num, GUILayout.Width(40));
|
||||
tempAreaRewardNum[data.Id] = num;
|
||||
|
||||
if (GUILayout.Button("添加", GUILayout.Width(50)))
|
||||
{
|
||||
areaRewards.Add(new ItemReward { Id = selectedItemId, Num = num });
|
||||
UpdateAreaReward(data, areaRewards);
|
||||
// 重置
|
||||
tempAreaRewardTypeIndex[data.Id] = 0;
|
||||
tempAreaRewardItemId[data.Id] = 0;
|
||||
tempAreaRewardNum[data.Id] = 1;
|
||||
}
|
||||
|
||||
// 显示预览图
|
||||
if (itemDict.TryGetValue(selectedItemId, out var previewItem2))
|
||||
{
|
||||
DrawItemPreview(previewItem2);
|
||||
}
|
||||
}
|
||||
areaRewards.Add(new ItemReward { Id = addId, Num = tempAreaRewardNum[data.Id] });
|
||||
UpdateAreaReward(data, areaRewards);
|
||||
// 重置
|
||||
tempAreaRewardItemId[data.Id] = 0;
|
||||
tempAreaRewardNum[data.Id] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField("该类型无可用Item", GUILayout.Width(120));
|
||||
EditorUtility.DisplayDialog("错误", $"ID {addId} 不存在于Item表中", "确定");
|
||||
}
|
||||
}
|
||||
GUI.backgroundColor = Color.white;
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打开Item选择弹窗(区域奖励用)
|
||||
/// </summary>
|
||||
private void OpenItemPickerForAreaReward(int dataId)
|
||||
{
|
||||
int currentItemId = tempAreaRewardItemId.ContainsKey(dataId) ? tempAreaRewardItemId[dataId] : 0;
|
||||
int currentType = currentItemId > 0 && itemDict.ContainsKey(currentItemId) ? itemDict[currentItemId].IType : -1;
|
||||
|
||||
DesignToolItemPickerWindow.ShowWindow(
|
||||
itemDict.Values
|
||||
.Select(x => new DesignToolItemPickerItem { Id = x.Id, Name = x.Name, IType = x.IType })
|
||||
.ToList(),
|
||||
allItemTypes,
|
||||
ITEM_TYPES,
|
||||
currentType,
|
||||
currentItemId,
|
||||
position,
|
||||
pickedId =>
|
||||
{
|
||||
tempAreaRewardItemId[dataId] = pickedId;
|
||||
Repaint();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析Item奖励
|
||||
@ -1349,7 +1333,7 @@ namespace DesignTools.Scene
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析Res格式: "tableId,itemId"
|
||||
// 解析Res格式: "TableName,ItemName"
|
||||
var parts = item.Res.Split(',');
|
||||
if (parts.Length != 2)
|
||||
{
|
||||
@ -1357,18 +1341,24 @@ namespace DesignTools.Scene
|
||||
return;
|
||||
}
|
||||
|
||||
if (!int.TryParse(parts[0], out int tableId)) return;
|
||||
if (!int.TryParse(parts[1], out int itemId)) return;
|
||||
string tableName = parts[0];
|
||||
string artItemName = parts[1];
|
||||
|
||||
if (string.IsNullOrEmpty(tableName) || string.IsNullOrEmpty(artItemName))
|
||||
{
|
||||
EditorGUILayout.LabelField("无图", GUILayout.Width(50));
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找对应的ArtTableSO
|
||||
var artTable = allArtTables.FirstOrDefault(x => x.TableId == tableId);
|
||||
var artTable = allArtTables.FirstOrDefault(x => x.TableName == tableName);
|
||||
if (artTable == null)
|
||||
{
|
||||
EditorGUILayout.LabelField("无图", GUILayout.Width(50));
|
||||
return;
|
||||
}
|
||||
|
||||
var artItem = artTable.Items.FirstOrDefault(x => x.Id == itemId);
|
||||
var artItem = artTable.Items.FirstOrDefault(x => x.Name == artItemName);
|
||||
if (artItem == null || artItem.Sprite == null)
|
||||
{
|
||||
EditorGUILayout.LabelField("无图", GUILayout.Width(50));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user