IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Unity换装系统(SkinnedMeshRenderer) -> 正文阅读

[游戏开发]Unity换装系统(SkinnedMeshRenderer)

【SiKi学院Unity】Unity换装系统【上】
【SiKi学院Unity】Unity换装系统【中】
【SiKi学院Unity】Unity换装系统【下】
课程资料.zip (71.93MB)

unity游戏换装系统【该全集有杂音】

原理

一个隐藏的,”穿着所有衣服“的模型
一个只有模型的骨骼的Target
给Target创建了6个部位,换装就是到模型那里取Transform,SkinnedMeshRenderer,加组件到部位节点上
在这里插入图片描述

04 游戏场景搭建

//Assets/Assets/Sources/Character/characters/prefabs
//删除其他部位,只保留根节点
在这里插入图片描述
//跟模型(拖一份新的)一起放Resources
在这里插入图片描述

05 换装资源以及换装骨骼的加载

动画循环

//是下不是上
在这里插入图片描述

代码

public class AvatarSys : MonoBehaviour
{

    [Tooltip("模型")] private GameObject model;
    [Tooltip("模型位置")] private Transform modelTrans;
    //
    [Tooltip("目标骨架")] private GameObject target;
    [Tooltip("骨骼信息")] private Transform[] hipArr;

    [Tooltip("多个部位,多件装")] private Dictionary<string, Dictionary<string, SkinnedMeshRenderer>> dictionary = new Dictionary<string, Dictionary<string, SkinnedMeshRenderer>>();
    // Start is called before the first frame update
    void Start()
    {
        InstantiateModel("FemaleModel");
        InstantiateTarget("FemaleTarget");
    }
    /// <summary>实例模型para /> </summary>
    void InstantiateModel(string name)
    {
        GameObject go = Instantiate(Resources.Load(name)) as GameObject;
        modelTrans = go.transform;
        go.SetActive(false);
    }
    /// <summary>实例骨骼<para /> </summary>    
    void InstantiateTarget(string name)
    { 
        target= Instantiate(Resources.Load(name)) as GameObject;
        hipArr = target.GetComponentsInChildren<Transform>();
    }
}

效果

//需要模型位置跟骨骼位置(在屋里),就赋值粘贴Transform
在这里插入图片描述

06 换装信息的数据存储

背景

//它的一个model是如下的命名
在这里插入图片描述

代码


public class AvatarSys : MonoBehaviour
{

    void Start()
    {
        SaveDictionary();
    }
  
    /// <summary>存储服装信息<para /></summary>
    void SaveDictionary()
    {
        SkinnedMeshRenderer[] clothes = modelTrans.GetComponentsInChildren<SkinnedMeshRenderer>();

        //比如eyes-1,取eyes(只取一次),拼成一套的部位部位     
        for (int i = 0; i < clothes.Length ; i++)
        {
            string[] nameArr=clothes[i].name.Split('-');
            string part = nameArr[0];
            string cloth = nameArr[1];

            if (fixedPartDictionary.ContainsKey(part) == false)//部位字典里没有该部位
            {
                GameObject go = new GameObject();
                go.name = part;
                go.transform.parent = target.transform;
                fixedPartDictionary.Add(part, go.AddComponent<SkinnedMeshRenderer>());
                partsDictionary.Add(part, new Dictionary<string, SkinnedMeshRenderer>());//声明一个部位可以穿衣服
            }
            partsDictionary[ part ].Add(cloth, clothes[i]);//衣服字典都Add
        }
    }
}

效果

在这里插入图片描述

07 换装代码的逻辑实现

骨骼的Transform

在这里插入图片描述

代码

	/// <summary>换衣服<para /></summary>
    void ChangeCloth(string part, string clothNum)
    {
        //选择一件衣服
        SkinnedMeshRenderer newCloth = partsDictionary[part][clothNum];

        //看衣服穿在哪里
        List<Transform> tmpList = new List<Transform>();
        foreach (var bone in newCloth.bones)//新衣服
        {
            foreach (var hip in hipArr)//目标骨骼。身上部位
            {
                if (bone.name == hip.name)
                {
                    tmpList.Add(bone);
                    break;
                }
            }
        }
        //穿上衣服。网格骨骼材质
        SkinnedMeshRenderer newClothed = fixedPartDictionary[part];

        newClothed.materials = newCloth.materials;       
        newClothed.bones = tmpList.ToArray();
        newClothed.sharedMesh = newCloth.sharedMesh;
    }

在这里插入图片描述

效果

网格骨骼材质,缺骨骼

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            ChangeCloth("hair", UnityEngine.Random.Range(1,4).ToString());
        }
    }

在这里插入图片描述

08 人物加载到场景中

代码

    [Tooltip("当前着装")] public string[,] clothedArr = { { "eyes", "1" } ,{ "hair", "1" },{ "top", "1" },
                                                          { "pants", "1" },{ "shoes", "1" },{ "face", "1" }};
    void Start()
    {
		......
        ChangeAvatar();
    }
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            ChangeCloth("hair", UnityEngine.Random.Range(1,4).ToString());
            ChangeCloth("pants", UnityEngine.Random.Range(1,7).ToString());
            ChangeCloth("hair", UnityEngine.Random.Range(1,7).ToString());
            ChangeCloth("top", UnityEngine.Random.Range(1,7).ToString());
            ChangeCloth("shoes", UnityEngine.Random.Range(1,7).ToString());
        }
    }
    /// <summary>初始骨骼<para /></summary>
    void ChangeAvatar()
    {
        int length = clothedArr.GetLength(0);//0行1列
        for (int i = 0; i < length; i++)
        {
            string part = clothedArr[i, 0];//第一列
            string clotht = clothedArr[i, 1];//第二列
            ChangeCloth(part, clotht);
        }
     }

效果(动画静止)

在这里插入图片描述

(问题) 衣服动画

//视频如下改,就可以动,但没起作用

在这里插入图片描述
//视频的骨骼是空的
在这里插入图片描述

.//发现ChangCloth时tmpList为为空
在这里插入图片描述
//给tmpList赋值的hipArr不为空
在这里插入图片描述
//tmpList.Add(hip);而不是tmpList.Add(bone);

    void ChangeCloth(string part, string clothNum)
    {
        //选择一件衣服
        SkinnedMeshRenderer newCloth = partsDictionary[part][clothNum];
         
        //看衣服穿在哪里
        List<Transform> tmpList = new List<Transform>();
       
        foreach (var bone in newCloth.bones)//新衣服
        {
            foreach (var hip in hipArr)//目标骨骼。身上部位
            {
                if (bone.name == hip.name)
                {
                    tmpList.Add(hip);
                    break;
                }
            }
        }

效果

在这里插入图片描述

10 加入男孩角色

一份脚本的话认后面的Male

    void Start()
    {
        InstantiateModel("FemaleModel");
        InstantiateTarget("FemaleTarget");
        InstantiateModel("MaleModel");
        InstantiateTarget("MaleTarget");
        SaveDictionary();
        ChangeAvatar();
    }

在这里插入图片描述

UI脚本位置

//UI代码自己做的
在这里插入图片描述

01 表头(部位)

(问题) toggle不能一起设SetActive,对象会被变化,认最后一个

在这里插入图片描述

Tab

挂表头

public class Tab : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
    /// <summary>根据索引和isOn调用显示内容的方法<para /></summary>
    public void SwitchTab()
    {
        bool isOn = GetComponent<Toggle>().isOn;
        int index = MyToggleGroup._instance.tabList.IndexOf(transform);
        print(index+","+isOn);
        MyToggleGroup._instance.SwitchTab( index ,isOn);
    }
}

MyToggleGroup

public class MyToggleGroup : MonoBehaviour
{

    [Tooltip("每个部位的列表")] public List<Transform> tabList;
    [Tooltip("(每个部位装衣服的节点的列表")] public List<Transform> contentList;

    public static MyToggleGroup _instance;

    void Awake()
    {
        _instance = this;
    }

    // Start is called before the first frame update
    void Start()
    {
        InitTab();
        InitContent();
    }

    /// <summary>初始化表头(每个部位)<para /></summary>
    void InitTab()
    {
        //tab命名
        string[] tabNameArr = { "发\n型", "眼\n睛", "上\n衣", "裤\n子", "鞋\n子","性\n别" };
        for (int i = 0; i < transform.childCount; i++)
        {
            transform.GetChild(i).GetChild(0).GetComponent<Text>().text = tabNameArr[i];
        }

        for (int i = 0; i < transform.childCount; i++)//不要性别
        {
            tabList.Add(transform.GetChild(i)) ;
        }
    }
    /// <summary>初始化内容(每个部位的衣服)<para /></summary>
    void InitContent()
    {
        //拿到content总节点
        for (int i = 0; i < tabList.Count; i++)
        {
            Transform contentTrans = tabList[i].GetChild(1);//0是文字
            contentList.Add(contentTrans);
        }


        //显示第一项,其余隐藏
        for (int i = 0; i < contentList.Count; i++)
        {
            contentList[i].gameObject.SetActive(false);                      
        }
        contentList[0].gameObject.SetActive(true);
        
    }

    /// <summary>根据参数显示具体部位的衣服<para /></summary>
    public void SwitchTab(int index,bool isOn)
    {
        contentList[index].gameObject.SetActive(isOn);      
    }
}

效果

在这里插入图片描述

02 表头里的item(部位里的衣服)

//hair-1是空节点
在这里插入图片描述

制作Item

填充中心

在这里插入图片描述

Item

public class Item : MonoBehaviour
{
    Transform ok;
    // Start is called before the first frame update
    void Start()
    {
        Init();
    }
    /// <summary>设置OK,ToggleGroup<para /></summary>
    private void Init()
    {
        GetComponent<Toggle>().group = transform.parent.parent.GetComponent<ToggleGroup>();
        ok = transform.GetChild(2);
        ok.gameObject.SetActive(false);
    }

    // Update is called once per frame
    void Update()
    {
    }
    /// <summary>点击item时Toggle会调用此方法<para /></summary>
    public void SwitchToggle()
    {
        //打钩
        ok.gameObject.SetActive(GetComponent<Toggle>().isOn);
        string[] names = gameObject.name.Split('-');
        string part = names[0];
        string clothNum = names[1];
        print(part + "," + clothNum);
        AvatarSys._instance.ChangeCloth(part,clothNum);
    }

效果

在这里插入图片描述

03 填充item的UI

效果

在这里插入图片描述

04 制作性别

Item_Sex

public class Item_Sex : MonoBehaviour
{
    Transform ok;
    // Start is called before the first frame update
    void Start()
    {
        Init();
    }
    /// <summary>设置OK,ToggleGroup<para /></summary>
    private void Init()
    {
        GetComponent<Toggle>().group = transform.parent.parent.GetComponent<ToggleGroup>();
        ok = transform.GetChild(2);
        ok.gameObject.SetActive(false);
    }

    // Update is called once per frame
    void Update()
    {
        
    }
    public void SwitchToggle()
    {
        //打钩
        ok.gameObject.SetActive(GetComponent<Toggle>().isOn);
        //
        string sex = gameObject.name;
        AvatarSys._instance.Init(sex);
    }
}

MyItemGroup

//根据Male、Female切换,切换前销毁存在的模型(根据标签)

   void Start()
    {
        Init("Female");
    }
    private void Destroy()
    {
        GameObject[] goArr = GameObject.FindGameObjectsWithTag("Player");
        for (int i = 0; i < goArr.Length; i++)
        {
            Destroy(goArr[i]);
        }
    }
    public void Init(string sex)
    {
        Destroy();

        hipArr = null;
        partsDictionary.Clear();
        fixedPartDictionary.Clear();
        if (sex == "Female")
        {
            InstantiateModel("FemaleModel");
            InstantiateTarget("FemaleTarget");
        }
        else
        {
            InstantiateModel("MaleModel");
            InstantiateTarget("MaleTarget");
        }


        SaveDictionary();
        ChangeAvatar();

        target.GetComponent<Animation>().cullingType = AnimationCullingType.AlwaysAnimate;
    }

效果

//可以看到,Male的UI面板我没做
在这里插入图片描述

(问题) 不清楚这样是不是销毁了

在这里插入图片描述

16 添加人物穿衣动画

AvatarSys

    public void ChangeCloth(string part, string clothNum)
    {
		......
        PlayAnimationByChangeCloth(part);
    }
   /// <summary>根据part播放相应动画<para /></summary>
   void PlayAnimationByChangeCloth(string part)
   {
        switch ( part )
        {
            case "top" : PlayAnimation("item_shirt"); break;
            case "pants" : PlayAnimation("item_pants"); break;
            case "shoes" : PlayAnimation("item_boots"); break;
            default: break;
        }

    }
    /// <summary>换衣动画后Idle<para /></summary>
    void PlayAnimation(string name)
    {
        GameObject go = GameObject.FindGameObjectWithTag("Female");
        Animation animation = go.GetComponent<Animation>();


        if (animation.IsPlaying(name)==false)//相同不切播
        {
            animation.Play(name);
            animation.PlayQueued("idle1");
        }
    }

效果

初始衣服时最后是鞋子,所以一开始播放换鞋动画
在这里插入图片描述

17 鼠标控制人物旋转

鼠标点击需要添加碰撞体

在这里插入图片描述

代码 RotateTarget

//觉得oldPos,newPos的赋值放里面更好点

public class RotateTarget : MonoBehaviour
{ 
    public bool isClick = false;
    public Vector3 newPos;
    public Vector3 oldPos;
    public float length = 5f;

    // Update is called once per frame
    void Update()
    {             
        if (isClick==true)
        {
            newPos = Input.mousePosition;
            Vector3 offset = newPos - oldPos;
            if (Mathf.Abs(offset.x) > Mathf.Abs(offset.y) && Mathf.Abs(offset.x)>length)
            {
                transform.Rotate(Vector3.up, offset.x);
                print(offset.x);
            }
            oldPos = Input.mousePosition;
        }      
    }

    private void OnMouseDown()
    {
        isClick = true;
    }
    private void OnMouseUp()
    {
        isClick = false;
    }   
}

效果

在这里插入图片描述

18 保存换装信息

二位数组

    [Tooltip("当前着装")]
    public string[,] clothedArr = { { "eyes", "1" } ,{ "hair", "1" },{ "top", "1" },
                                                          { "pants", "1" },{ "shoes", "1" },{ "face", "1" }};
    // Start is called before the first frame update
    void Start()
    {

        print("总长度:"+clothedArr.Length);//总长度
        print("第一维长度:"+clothedArr.GetLength(0));//第一维长度,部位
        print("第二维长度:"+clothedArr.GetLength(1));//第二维长度,每个部位比如"eyes", "1" 

        for (int i = 0; i < clothedArr.Length; i++)
        {
            print(clothedArr[i, 0] + ":" + clothedArr[i, 1]);
        }
    }

在这里插入图片描述

存储 AvatarSys【DontDestroyOnLoad】

//DontDestroyOnLoad(gameObject);
在这里插入图片描述

    void Start()
    {
        Init("Female");
        DontDestroyOnLoad(gameObject);
    }
    public void ChangeCloth(string part, string clothNum)
    {
		......
        //更新数据
        UpdateClothedArr(part, clothNum);

        PlayAnimationByChangeCloth(part);
    }
    /// <summary>更新数组<para /></summary>
    void UpdateClothedArr(string part, string clothNum)
    {
        for (int i = 0; i < clothedArr.GetLength(0); i++)
        {
            if (part == clothedArr[i, 0])
            {
                clothedArr[i, 1] = clothNum;
            }
        }
        //打印测试
        for (int i = 0; i < clothedArr.GetLength(0); i++)
        {
            print(clothedArr[i, 0] + "," + clothedArr[i, 1]);
        }
    }

在这里插入图片描述

加载

public class SaveAndLoad : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        AvatarSys._instance.Init("Female");
    }
    ......

在这里插入图片描述

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-07-16 11:38:06  更:2021-07-16 11:39:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 13:41:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码