Unity实现简单换装系统
作者:langresser 发布时间:2022-06-15 16:30:07
关于Unity的换装,网上有几篇文章,我之前也简单的描述过实现。不过那个时候只是粗略的试验了下。今天好好梳理了下代码。
先上代码(自己的游戏项目,不是公司的,所以放心的贴上项目代码了,部分引用到其他的功能文件,但是核心代码无影响,这里主要看一下细节和思路)
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public enum AvatarPart
{
helmet,
chest,
shoulders,
gloves,
boots,
}
// 人物换装
public class ActorAvatar : MonoBehaviour
{
// 换装的部件信息
public class AvatarInfo
{
public string partName;
public GameObject defaultPart;
public GameObject avatarPart;
}
protected int _bodyModelId;
protected GameObject _body; // 基础模型动画
protected Dictionary<string, AvatarInfo> _avatarInfo = new Dictionary<string, AvatarInfo>(); // 换装信息
private List<int> _avatarLoadQueue = new List<int>();
void Start()
{
}
void Update()
{
}
// 创建模型
public void LoadModel(int modelId)
{
_bodyModelId = modelId;
ResourceMgr.Instance.LoadModel(modelId, (GameObject obj) =>
{
_body = obj;
// 换装请求
if (_avatarLoadQueue.Count > 0) {
foreach (var avatar in _avatarLoadQueue) {
LoadAvatar(avatar);
}
_avatarLoadQueue.Clear();
}
}, true);
}
// 给人物换装
public void LoadAvatar(int avatarId)
{
// 如果还没有加载完基础模型,则等待
if (_body == null) {
_avatarLoadQueue.Add(avatarId);
return;
}
AvatarData adata = DataMgr.Instance.GetAvatarData(avatarId);
ResourceMgr.Instance.LoadModel(adata.model, (GameObject obj) => {
ChangeAvatar(obj, adata.addpart);
});
}
// 替换部件
public void ChangeAvatar(GameObject avatarModel, string partName)
{
// 先卸载当前部件
AvatarInfo currentInfo;
if (_avatarInfo.TryGetValue(partName, out currentInfo)) {
if (currentInfo.avatarPart != null) {
Destroy(currentInfo.avatarPart);
currentInfo.avatarPart = null;
}
if (currentInfo.defaultPart != null) {
currentInfo.defaultPart.SetActive(true);
}
}
// avatarModel是一个resource,并没有实例化
if (avatarModel == null) {
return;
}
// 需要替换的部件
Transform avatarPart = GetPart(avatarModel.transform, partName);
if (avatarPart == null) {
Debug.LogError(string.Format("Avatar Part Not Found: ", partName));
return;
}
// 将原始部件隐藏
Transform bodyPart = GetPart(_body.transform, partName);
if (bodyPart != null) {
bodyPart.gameObject.SetActive(false);
}
// 设置到body上的新物件
GameObject newPart = new GameObject(partName);
newPart.transform.parent = _body.transform;
SkinnedMeshRenderer newPartRender = newPart.AddComponent<SkinnedMeshRenderer>();
SkinnedMeshRenderer avatarRender = avatarPart.GetComponent<SkinnedMeshRenderer>();
// 刷新骨骼模型数据
SetBones(newPart, avatarPart.gameObject, _body);
newPartRender.sharedMesh = avatarRender.sharedMesh;
newPartRender.sharedMaterials = avatarRender.sharedMaterials;
// 记录换装信息
AvatarInfo info = new AvatarInfo();
info.partName = partName;
if (bodyPart != null) {
info.defaultPart = bodyPart.gameObject;
} else {
info.defaultPart = null;
}
info.avatarPart = newPart;
_avatarInfo[partName] = info;
}
// 递归遍历子物体
public static Transform GetPart(Transform t, string searchName)
{
foreach (Transform c in t) {
string partName = c.name.ToLower();
if (partName.IndexOf(searchName) != -1) {
return c;
} else {
Transform r = GetPart(c, searchName);
if (r != null) {
return r;
}
}
}
return null;
}
public static Transform FindChild(Transform t, string searchName)
{
foreach (Transform c in t) {
string partName = c.name;
if (partName == searchName) {
return c;
} else {
Transform r = FindChild(c, searchName);
if (r != null) {
return r;
}
}
}
return null;
}
// 刷新骨骼数据 将root物体的bodyPart骨骼更新为avatarPart
public static void SetBones(GameObject goBodyPart, GameObject goAvatarPart, GameObject root)
{
var bodyRender = goBodyPart.GetComponent<SkinnedMeshRenderer>();
var avatarRender = goAvatarPart.GetComponent<SkinnedMeshRenderer>();
var myBones = new Transform[avatarRender.bones.Length];
for (var i = 0; i < avatarRender.bones.Length; i++) {
myBones[i] = FindChild(root.transform, avatarRender.bones[i].name);
}
bodyRender.bones = myBones;
}
}
1、Unity换装有三种需求:
添加武器的挂载式换装,这个只要创建对应的模型,并且设置好transform.parent就可以了。
替换纹理,这个取到对应的material,然后设置texture就可以了。
模型部件的替换,这个是此处处理的,也是相对最复杂的换装。
2、最核心的部分是ChangeAvatar,它完成了模型换装的功能。模型部件的替换其实就是替换SkinnedMeshRender中的sharedMesh和sharedMaterials。
(这里稍微插一下sharedMaterials sharedMaterial Materials Material这几个变量的区别。sharedMaterials是共享和引用的关系,只要修改这个,所有使用到这个material的模型都会受到影响。如果是在编辑器模式下,它还会修改实际material文件的属性。Materials是sharedMaterials的一份拷贝,只有当前模型使用。materia是materials数组中的第一个对象,这个仅仅是为了方便书写而存在的。)
仅仅替换了sharedMesh还不够,模型会变成一坨麻花。 还应该修改SkinnedMeshRender中的bones属性,它记录了模型的骨骼信息(其实就是一大堆Transform)。 SetBones函数完成了骨骼替换的操作。它查找avatar部件中的所有骨骼名称,然后查找当前模型中的对应骨骼名字,并存储起来。这个数组就是新部件的骨骼信息。
3、一个逻辑上的处理细节。保留了原始模型的对应部件,并没有销毁这个部件,仅仅是隐藏起来。这样卸载装备的时候,只需要删掉装备部件,然后把默认部件设为可见就可以了。
4、可以考虑使用Unity的CombineInstance把模型合并,这样的好处是可以提高运行性能。但是只有材质共用一个的时候才能真正起到优化效果。有个MeshBaker的插件很酷。如果要进行千人战,就必须考虑这方面的优化。
来源:https://blog.csdn.net/langresser_king/article/details/44179901
猜你喜欢
- 日志是非常重要的,虽然他不会以需求功能提来,但也不会体现在产品方案中。但是,它在系统项目中却占有巨大的地位。为了保证服务的高可用,发现问题一
- 写在前面: 线程堆栈应该是多线程类应用程序非功能问题定位的最有效手段,可以说是杀手锏。线程堆栈最擅长与分析如下类型问题:系统无缘无故CPU过
- Android的应用被限制为最多占用16m的内存,至少在T-Mobile G1上是这样的(当然现在已经有几百兆的内存可以用了——译者注)。它
- 准备工作HALCON示例程序的描述部分一直是英文的,看起来很不方便。我决定汉化一下HALCON示例程序的描述,准备工作如下:拿到HALCON
- 项目中需要webview重定向,但是由于一个webveiw里面有许多加载操作,因此在调用webview。goback()方法时,往往达不到我
- 在c#中可以遍历指定驱动器或指定目录下嵌套目录中的所有文件或者任意深度的文件。通过遍历可以检索string形式的目录名和文件名,也可以检索
- 1、直接使用getWindow().getDecorView().getRootView()直接使用getWindow().getDecor
- Android Spinner 组件Spinner: 下拉组件使用事项:布局在XML 中实现,具体的数据在JAVA 代码中实现;所用知识点:
- 前言Jetpack Compose(简称 Compose )是 Google 官方推出的基于 Kotlin 语言的 Android 新一代
- 1、Dom4j概述dom4j is an easy to use, open source library for working with
- 前言:最近对接了一个第三方的项目,该项目的数据传输格式是XML。由于工作多年只有之前在医疗行业的时候有接触过少量数据格式是XML的接口,之后
- 目录1 Exchanger 介绍2 Exchanger 实例exchange等待超时3 实现原理1 Exchanger 介绍前面分别介绍了C
- 前言本文主要给大家介绍了关于C#连接FTP时路径问题的相关内容,分享出来供大家参考学习,话不多说,来一起看看详细的介绍:今天在开发项目时,需
- 目录一、背景二、推荐方式2.1 自定义的枚举2.2 外部枚举三、总结一、背景平时工作开发过程中,难免会用到状态机(状态的流转)。如奖学金审批
- 日期和时间,在我们开发中非常重要。DateTime在C#中,专门用来表达和处理日期和时间。本文算是多年使用DateTime的一个总结,包括D
- 前言这篇文章的内容基于对Spring Security 认证流程的理解,如果你不了解,可以读一下这篇文章:Spring Security 认
- 本文主要介绍了关于c#和java base64不一致的解决方法,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧不一致的问题不
- Jmeter是纯Java开发的, 能够运行Java程序的系统一般都可以运行Jmeter, 如:Windows、 Linux、 mac等。由于
- 方法重载概述方法重载指同一个类中定义的多个方法之间的关系,满足下列条件的多个方法互相构成重载* 多个方法在同一个类中* 多个放方法具有相同方
- 故事背景故事发生在几个星期前,自动化平台代码开放给整个测试团队以后,越来越多的同事开始探索平台代码。为了保障自动化测试相关的数据和沉淀能不被