zoukankan      html  css  js  c++  java
  • unity 角色换装

    unity角色换装的关键是更改角色部位上的物体的SkinnedMeshRenderer组件的属性:

    更改mesh:mesh决定了部位的物体的外形,是主要的数据。

    刷新骨骼:同一个部位下,不同的mesh受到的不同的骨骼的影响不同,因此更换mesh之后,还要更新SkinnedMeshRenderer下的骨骼列表的信息,也就是更换骨骼列表。

    替换材质:一个SkinnedMeshRenderer下由多个材质作用,因此还需要更换材质列表。

    操作过程为,从预制物体中获取的需要更换的相关部位的mesh,然后通过从预制物体的相关部位的SkinnedMeshRenderer下获取到影响该部位的骨骼列表,然后从场景角色的骨骼下获取到同名的骨骼列表,将该骨骼列表赋予到场景下角色的部位的SkinnedMeshRenderer下,并且获取到预制物体下该部位的材质列表,同样的将该列表赋予场景下角色的部位的SkinnedMeshRenderer下。

    为了获取到更换的信息,需要由预制物体存储物体的相关信息。预制物体如下,每个部位下所有的物体都呈现,便于程序提取信息。

    原模型如下:

    场景下角色如下:

    具体代码如下:

    该脚本可以放在任何地方

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class AvatarSysDemo00 : MonoBehaviour
    {
        public Transform role;//场景中的角色物体
        public GameObject rolePrefab;//预制物体
        public GameObject kuzi;//场景中角色物体下裤子物体
        public string[] kuziNames;//所有的用于替换的裤子的名字,用于再预制物体中找到相关的物体的信息
        public GameObject[] objs;//裤子相关的预制物体 
        int index = 0;//当前的装备索引
        public Transform[] hips;//角色的骨骼物体
    
        private void Awake()
        {
            hips = null;
            if (role)
                hips = role.GetComponentsInChildren<Transform>();//首先获取场景中角色下的骨骼列表
    
            for (int i = 0; i < kuziNames.Length; i++)//获取预制物体下的所有裤子物体
            {
                Transform kuziObj = rolePrefab.transform.Find(kuziNames[i]);
                objs[i] = kuziObj.gameObject;
            }
        }
    
        // Start is called before the first frame update
        void Start()
        {
                  
        }
    
        // Update is called once per frame
        void Update()
        {
            if (Input.GetKeyDown(KeyCode.Space))//设置为按下空格就切换一下裤子
            {
                Debug.Log(objs.Length);
                if (objs.Length == 0) return;
                index = (index + 1) % objs.Length;
    
                ChangeMesh(objs[index]);
            }
        }
    
        //换装
        public void ChangeMesh(GameObject part)
        {
            SkinnedMeshRenderer smr = part.GetComponent<SkinnedMeshRenderer>();//获取预制物体下相关部位的SkinnedMeshRenderer
    
            //获取角色物体下与预制物体相关更换的Mesh部位下作用于该Mesh,再场景中与其同名的骨骼列表
            List<Transform> bones = new List<Transform>();
            foreach (Transform bone in smr.bones)
            {
                foreach (Transform hip in hips)
                {
                    if (hip.name != bone.name)
                    {
                        continue;
                    }
                    bones.Add(hip);
                    break;
    
                }
            }
            kuzi.GetComponent<SkinnedMeshRenderer>().sharedMesh = smr.sharedMesh;//更改mesh
            kuzi.GetComponent<SkinnedMeshRenderer>().bones = bones.ToArray();//更换(刷新)骨骼列表
            kuzi.GetComponent<SkinnedMeshRenderer>().materials = smr.sharedMaterials;//更换材质
        }
    }

     上面是具体原理,但是为了正确使用,这我们对功能进行封装,形成一个工具类,同时我们考虑到如果是MeshRendere组件,也是可以进行非骨骼绑定的换装,所以这里同时将这两个功能封装进工具类里面

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public static class AvatarTool
    {
        /// <summary>
        /// 应用装备
        /// </summary>
        /// <param name="targetRole">目标角色物体</param>
        /// <param name="targetPart">被改变的部位</param>
        /// <param name="partName">装备预制体名称</param>
        /// <param name="partSavorRolePrefab">预制体所在的对象预制物体</param>
        public static void ChangeAvatar(GameObject targetRole,GameObject targetPart,string partName,GameObject partSavorRolePrefab)
        {
            //Transform t = partSavorRolePrefab.transform.Find(partName);
            Transform t = FindTargetObj(partSavorRolePrefab, partName);
    
            if (t == null) return;
            ChangeAvatar(targetRole, targetPart, t.gameObject);
        }
    
        /// <summary>
        /// 寻找到目标物体
        /// </summary>
        public static Transform FindTargetObj(GameObject obj,string name)
        {
            Transform o=null;
            Transform[] transforms = obj.transform.GetComponentsInChildren<Transform>();
            foreach (Transform t in transforms)
            {
                if (t.name == name)
                {
                    o = t;
                    break;
                }
            }
            return o;
        }
    
        /// <summary>
        /// 根据是否带有SkinnedMeshRenderer决定改变外观的方式
        /// </summary>
        /// <param name="targetRole"></param>
        /// <param name="targetPart"></param>
        /// <param name="part"></param>
        public static void ChangeAvatar(GameObject targetRole, GameObject targetPart,GameObject part)
        {
            if (targetPart.GetComponent<SkinnedMeshRenderer>())
            {
                Transform[] hips = targetRole.GetComponentsInChildren<Transform>();//角色的骨骼物体
                ChangeMesh(targetPart, part, hips);
            }
            else
            {
                ChangeMesh(targetPart, part);
            }
            
        }
    
        /// <summary>
        /// 改变mesh,这里主要使用蒙皮骨骼换装
        /// </summary>
        /// <param name="targetPart">需要被改变的玩家身上的部位,比如:鞋子</param>
        /// <param name="part">用于改变的部位,比如:鞋子01</param>
        /// <param name="hips">玩家身上的骨骼,targetPart和part必须在相同的一套或者同名(模型不同,但是骨骼完全一样)的骨骼下</param>
        public static void ChangeMesh(GameObject targetPart,GameObject part,Transform[] hips)
        {
            SkinnedMeshRenderer smr = part.GetComponent<SkinnedMeshRenderer>();//获取预制物体下相关部位的SkinnedMeshRenderer
    
            //获取角色物体下与预制物体相关更换的Mesh部位下作用于该Mesh,再场景中与其同名的骨骼列表
            List<Transform> bones = new List<Transform>();
            foreach (Transform bone in smr.bones)
            {
                foreach (Transform hip in hips)
                {
                    if (hip.name != bone.name)
                    {
                        continue;
                    }
                    bones.Add(hip);
                    break;
                }
            }
            targetPart.GetComponent<SkinnedMeshRenderer>().sharedMesh = smr.sharedMesh;//更改mesh
            targetPart.GetComponent<SkinnedMeshRenderer>().bones = bones.ToArray();//更换(刷新)骨骼列表
            targetPart.GetComponent<SkinnedMeshRenderer>().materials = smr.sharedMaterials;//更换材质
        }
    
        /// <summary>
        /// 改变Mesh,这里主要使用MeshRenderer
        /// </summary>
        /// <param name="targetPart">玩家身上的部位</param>
        /// <param name="part"></param>
        public static void ChangeMesh(GameObject targetPart,GameObject part)
        {
            targetPart.GetComponent<MeshFilter>().mesh = part.GetComponent<MeshFilter>().sharedMesh;
            targetPart.GetComponent<MeshRenderer>().materials = part.GetComponent<MeshRenderer>().sharedMaterials;
        }
    }
  • 相关阅读:
    MyEclipse中的几种查找方法
    WebLogic初学笔记
    CountDownLatch源码分析
    linux--句柄相关
    linux命令--wc
    Spring源码解析(九)--再来说说三级缓存
    定位JVM内存泄漏常用命令和方法
    Mybatis整合Spring之MapperFactoryBean怎么拿到的SqlSessionFactory
    Mybatis3.3.0 Po类有LocalDateTime字段报错
    时间范围查询优化技巧
  • 原文地址:https://www.cnblogs.com/xiaoahui/p/unity.html
Copyright © 2011-2022 走看看