【SiKi学院Unity】Unity换装系统【上】
【SiKi学院Unity】Unity换装系统【中】
【SiKi学院Unity】Unity换装系统【下】
课程资料.zip (71.93MB)
unity游戏换装系统【该全集有杂音】
一个隐藏的,”穿着所有衣服“的模型
一个只有模型的骨骼的Target
给Target创建了6个部位,换装就是到模型那里取Transform,SkinnedMeshRenderer,加组件到部位节点上
//Assets/Assets/Sources/Character/characters/prefabs
//删除其他部位,只保留根节点
//跟模型(拖一份新的)一起放Resources
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");
}
/// 实例模型para />
void InstantiateModel(string name)
{
GameObject go = Instantiate(Resources.Load(name)) as GameObject;
modelTrans = go.transform;
go.SetActive(false);
}
/// 实例骨骼
void InstantiateTarget(string name)
{
target= Instantiate(Resources.Load(name)) as GameObject;
hipArr = target.GetComponentsInChildren<Transform>();
}
}
//需要模型位置跟骨骼位置(在屋里),就赋值粘贴Transform
public class AvatarSys : MonoBehaviour
{
void Start()
{
SaveDictionary();
}
/// 存储服装信息
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
}
}
}
/// 换衣服
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());
}
}
[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());
}
}
/// 初始骨骼
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;
}
}
}
一份脚本的话认后面的Male
void Start()
{
InstantiateModel("FemaleModel");
InstantiateTarget("FemaleTarget");
InstantiateModel("MaleModel");
InstantiateTarget("MaleTarget");
SaveDictionary();
ChangeAvatar();
}
挂表头
public class Tab : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
/// 根据索引和isOn调用显示内容的方法
public void SwitchTab()
{
bool isOn = GetComponent<Toggle>().isOn;
int index = MyToggleGroup._instance.tabList.IndexOf(transform);
print(index+","+isOn);
MyToggleGroup._instance.SwitchTab( index ,isOn);
}
}
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();
}
/// 初始化表头(每个部位)
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)) ;
}
}
/// 初始化内容(每个部位的衣服)
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);
}
/// 根据参数显示具体部位的衣服
public void SwitchTab(int index,bool isOn)
{
contentList[index].gameObject.SetActive(isOn);
}
}
public class Item : MonoBehaviour
{
Transform ok;
// Start is called before the first frame update
void Start()
{
Init();
}
/// 设置OK,ToggleGroup
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()
{
}
/// 点击item时Toggle会调用此方法
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);
}
public class Item_Sex : MonoBehaviour
{
Transform ok;
// Start is called before the first frame update
void Start()
{
Init();
}
/// 设置OK,ToggleGroup
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);
}
}
//根据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面板我没做
public void ChangeCloth(string part, string clothNum)
{
......
PlayAnimationByChangeCloth(part);
}
/// 根据part播放相应动画
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;
}
}
/// 换衣动画后Idle
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");
}
}
初始衣服时最后是鞋子,所以一开始播放换鞋动画
//觉得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;
}
}
[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]);
}
}
//DontDestroyOnLoad(gameObject);
void Start()
{
Init("Female");
DontDestroyOnLoad(gameObject);
}
public void ChangeCloth(string part, string clothNum)
{
......
//更新数据
UpdateClothedArr(part, clothNum);
PlayAnimationByChangeCloth(part);
}
/// 更新数组
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");
}
......