Unity使用Odin制作角色换装系统

效果图

制作流程

换装其实是easy part,只是想用这个当做一个引子.总结一些Odin的使用方法.

ValueDropdown 与 ValueDropdownList

项目中的 性别,肤色,胖瘦等信息 内部存储是用的int,但是希望在编辑器中使用汉字显示出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

public static class AvatarFaceMetaConfig
{
public const int SEX_TYPE_COMMON = 0;
public const int SEX_TYPE_FEMALE = 1;
public const int SEX_TYPE_MALE = 2;
...
}

private static ValueDropdownList<int> sexTypeList = new ValueDropdownList<int>
{
{"男", AvatarFaceMetaConfig.SEX_TYPE_MALE},
{"女", AvatarFaceMetaConfig.SEX_TYPE_FEMALE}
};

[ValueDropdown("sexTypeList"), OnValueChanged("OnBaseInfoChanged")]
[LabelText("性别")]
[HorizontalGroup("BaseInfo", LabelWidth = 30)]
public int sexType = AvatarFaceMetaConfig.SEX_TYPE_FEMALE;

HorizontalGroup

将 性别,肤色,脸型 信息横向排列, 同时 减小 Label和下拉框之间的间距

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[ValueDropdown("sexTypeList"), OnValueChanged("OnBaseInfoChanged")]
[LabelText("性别")]
[HorizontalGroup("BaseInfo", LabelWidth = 30)]
public int sexType = AvatarFaceMetaConfig.SEX_TYPE_FEMALE;

[ValueDropdown("skinColorList"), OnValueChanged("OnBaseInfoChanged")]
[LabelText("肤色")]
[HorizontalGroup("BaseInfo", LabelWidth = 30)]
public int faceColorType = AvatarFaceMetaConfig.FACE_COLOR_LIGHT;

[ValueDropdown("faceSizeList"), OnValueChanged("OnBaseInfoChanged")]
[LabelText("脸型")]
[HorizontalGroup("BaseInfo", LabelWidth = 30)]
public int faceSizeType = AvatarFaceMetaConfig.FACE_SIZE_NORMAL;

ValueDropdown中绑定动态List

头发,眉毛等素材都是根据目录中PNG图片的数量动态变化的,不像 性别,胖瘦等信息 可以直接在代码中将List填充好.不过逻辑类似

1
2
3
4
5
6
7
private List<string> allEyebrowList = new List<string>();

[ValueDropdown("allEyebrowList"), OnValueChanged("OnDetailInfoChanged"), LabelWidthAttribute(30)]
[InlineButton("RightEyebrow", "->")]
[InlineButton("LeftEyebrow", "<-")]
[LabelText("眉毛")]
public string eyebrowRes;

InlineButton

如果需要横向放置的只是按钮,可以直接使用InlineButton,而无需外面嵌套Group了.

因为数据源是List,所以不能使用Enum Paging,如果是Enum的话 使用Enum Paging 可以直接实现 左右选择的逻辑. 这里需要手动实现相关选择逻辑

ShowIf

男性角色有胡子,女性没有,所以 相关属性 在性别变化时候 动态的 显示/隐藏.

使用一个private的bool值mIsShowBeard动态控制


1
2
3
4
5
6
7
8
9
10
11
12
13
14
[ValueDropdown("allBeardList"), OnValueChanged("OnDetailInfoChanged"), LabelWidthAttribute(30)]
[InlineButton("RightBeard", "->")]
[InlineButton("LeftBeard", "<-")]
[LabelText("胡子")]
[ShowIf("mIsShowBeard")]
public string beardRes;

private bool mIsShowBeard;

private void OnBaseInfoChanged()
{
mIsShowBeard = sexType == AvatarFaceMetaConfig.SEX_TYPE_MALE;
...
}

ReadOnly

脸型和肩膀只有一个,所以可以标记为只读属性

1
2
3
4
5
6
7
[ReadOnly]
[LabelText("脸型")]
public string faceRes;

[ReadOnly]
[LabelText("肩膀")]
public string shouldRes;

DisplayAsString & HideLabel

为了美观, 将GUI分成了两部分,上面加上了Title标记. 下面的Functions只需要在 Button上面加入[Title]标记即可.
但是上面的BaseInfo 不可以在性别属性上面加入Title标记. 因为 性别 肤色 脸型 在一个 水平的Grou里面

绕过这个逻辑的方式,就是加一行看不见的隐形属性,对该属性加入Title标记

1
2
3
[Title("Base Info", titleAlignment: TitleAlignments.Centered)]
[DisplayAsString, HideLabel]
public string s = "";

EditorUtility.SetDirty

当点击某些按钮 Odin内部会自动执行了 对当前gameobject setDirty, 这样会触发场景的重新绘制. 但是因为 头发等素材的左右选择

是间接的改变了属性值,并未触发setDirty. 所以当发现

点击按钮后 屏幕不刷新, 点击其他地方切回来时候就刷新了

就需要手动的对 相应按钮点击后 添加

1
EditorUtility.SetDirty(xxx);
坚持原创技术分享,您的支持将鼓励我继续创作!