2013-10-14 19:49:57 u011838628 阅读数 9187
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

      最近在看Unity3D的人物模型和动画。所以今天先说下人物的换装吧。相信大家都玩过网游吧,没有玩过的也相信见过,就是网游或者单机游戏里的人物会有更换服装,更换武器的功能。如果外形(mesh)是一样的,那么把贴图换下就好,但是如果外形不一样甚至骨骼都变了的话,就需要我们今天讨论的技术了。

  一.Unity3D里的3D模型

Unity3D里的基础模型是从3D工具中导出来的fbx文件。把fbx放在Assets目录下会自动生成一个Materials文件夹,里面会生成一个材质球。在Unity3D项目里的显示是这样

注意:Textrue需要另外添加进去。

         模型的主要文件就是那个蓝色的方块,前面两个(WGKS_wugang,WGKS_yaodai)这两个文件是模型的Mesh信息,一个是人物本身的,一个是腰带的,因为比较简单的模型,所以只有两个。下面的一坨是动作信息,动作是可以自己设置的,这个以后文章会讲。而紧接着的

“Bip001”这个是模型的骨骼信息,你拖到Scene里就能层层打开看到了。而WGKS_yaodai则是蒙皮信息的一些玩意。选择到它会在Inspector属性栏里看到SkinnedMeshRenderer组件,这里会看到它牵扯到的mesh,material,bone信息。

  

  二.换装方案1组合模型的显示和隐藏

  这个题目可能说的不合适,但是想不到更合适的了。我们的第一种换装的方法很简单,就是让模型或者Mesh进行显示和隐藏。就比如让人物穿两套衣服,显示第一套的时候隐藏第二套,显示第二套的时候显示第一套。

         例如这个模型:

         

这里有TBJ_SR_01和TBJ_SR_03两个SkinnedMeshRenderer.那么我们可以分别设置Active来设置是否可见。代码如下:

public GameObject clothA = null;
public GameObject clothB = null;

if (GUI.Button(new Rect(5, 5, 100, 50), "ClothA"))
{
    clothA.SetActive(true);
    clothB.SetActive(false);
}

if (GUI.Button(new Rect(5, 60, 100, 50), "ClothB"))
{
    clothA.SetActive(false);
    clothB.SetActive(true);
}


运行起来的效果如下:

ClothA

ClothB

      如果角色是换武器而不是换装备的话,原理是一样的,只不过我们要找到附属的武器GameObject然后设置让它显示和隐藏就可以了。

  三.换装方案2 替换Skin信息

      方案1的方法用起来比较简单,比较适合简单的模型替换,比如一个模型来来回回就两把武器的话,就可以用。但是如果一个模型有十来套衣服或者十几件装备的话,这么做会比较悲剧的,因为太占资源了,创建一个模型,牵扯的不用的东西也整进内存了,暴殄天物。

      所以我想能不能游戏运行的时候替换模型的Skin信息达到我们的目的。一开始我觉得,其实我们只需要把sharedmesh,material替换掉就行了。实验证明不行的。大家可以自己尝试下,Unity3D会报错,即使不报错模型也会变形的。模型中还有一块是骨骼没有动,所以我想尝试下如果把骨骼去掉能不能拿到mesh信息等,然后再替换。结果是从3DMAX里去掉骨骼导出的话,蒙皮信息也没有了。所以我确定了一点是,被换装的模型必须有骨架,而源Mesh信息中必须包含骨架信息,按照我的理解就是Mesh中保存着这个点依附的是哪个骨头,权重是多少等。

 

  这里我准备了两个模型:

            

带有完整的信息(骨头,动作,蒙皮信息等,只不过模型只有一个mesh而已)。

放在Scene的样子:

            

  我的目的是想让左边的那个模型读取右边模型的SkinnedMeshRenderer信息,然后赋值给自己。首先我把右边的模型除了骨架的所以资源赋值给一个prefab,然后加载prefab,读取信息给左边的模型。

  代码如下:

// 声明两个SkinnedMeshRenderer
public SkinnedMeshRenderer MyskinMeshRender = null;
public SkinnedMeshRenderer SrcSkinMeshRender = null;

//加载资源
Object Src = Resources.Load("Res/Materials2");
objSrc = Instantiate(Src) as GameObject;

//找到目标模型的SkinnedMeshRenderer
MyskinMeshRender = GameObject.Find("TBJ_SR_01").GetComponent<SkinnedMeshRenderer>();
//获取加载的SkinnedMeshRenderer
SrcSkinMeshRender = objSrc.GetComponent<SkinnedMeshRenderer>();
//获取目标模型的骨骼
Transform[] bonesMy = gameObject.GetComponentsInChildren<Transform>();
Debug.Log(bonesMy.Length);
//Transform[] bonesSrc = GameObject.Find("TBJ_SR_03").GetComponentsInChildren<Transform>();
//获得源SkinMesh中依附的骨骼信息
Transform[] bonesSrc = SrcSkinMeshRender.bones;
Debug.Log(bonesSrc.Length);

//根据源SkinMesh依附的骨骼信息重新排列目标骨骼
List<Transform> bones = new List<Transform>();
foreach (Transform boneS in bonesSrc)
{
    foreach (Transform boneM in bonesMy)
    {
        if (boneS != null && boneM != null)
        {
            if (boneS.name != boneM.name)
            {
                continue;
            }
            bones.Add(boneM);
        }
    }
}
MyskinMeshRender.bones = bones.ToArray();
MyskinMeshRender.sharedMesh = SrcSkinMeshRender.sharedMesh;
MyskinMeshRender.sharedMaterials =SrcSkinMeshRender.sharedMaterials;     


运行之后发现皮肤换了,而且动画信息没有丢失掉,也就是说还能动,但是有个奇葩的现象,人物是倒着的,如图:

  这个我分析后觉得是因为骨骼的问题。两个模型的骨骼有点不一样。因为我是读取了右边的模型的蒙皮信息对应的骨骼信息而重组了左边的骨骼,但是左边的动画信息还是按照原来的。虽然不是很清楚动画数据里面数据结构,但是我觉得就是记录的骨骼或者mesh或者当前模型的世界坐标的某个点对应的Mesh或者骨骼移动到哪个位置。

 

  接着我换了一个思路,用一个人物模型,一套骨骼,一套动作,不过蒙皮信息有两个,导出fbx。

如图:

 

  然后在3DMax里面导出一个没有蒙皮信息只有骨骼和动作的fbx,如图:

  然后添加脚本,代码如下: 

//声明两个SkinMeshRender 声明为public是为了方便观察 真正需要private
public SkinnedMeshRenderer MyskinMeshRender = null;
public SkinnedMeshRenderer SrcSkinMeshRender = null;

//添加SkinnedMeshRenderer组件
MyskinMeshRender = gameObject.AddComponent<SkinnedMeshRenderer>();
	
if (Input.GetKeyDown(KeyCode.Z))
{
    GameObject obj = GameObject.Find("TBJ_SR_01") as GameObject;
    SrcSkinMeshRender = GameObject.Find("TBJ_SR_01").GetComponent<SkinnedMeshRenderer>();

    Transform[] bonesMy = gameObject.GetComponentsInChildren<Transform>();
    Transform[] bonesSrc = SrcSkinMeshRender.bones;

    List<Transform> bones = new List<Transform>();
    foreach (Transform boneM in bonesSrc)
    {
        foreach (Transform boneS in bonesMy)
        {
            if (boneM != null && boneS != null)
            {
                if (boneM.name != boneS.name)
                {
                    continue;
                }
                bones.Add(boneS);
            }
        }
    }
    MyskinMeshRender.bones = bones.ToArray();
    MyskinMeshRender.sharedMesh = SrcSkinMeshRender.sharedMesh;
    MyskinMeshRender.sharedMaterials = SrcSkinMeshRender.sharedMaterials;
}


 

  运行,结果没有问题了。如图:

  

  按下Z之后:

  而且动作保留。

 

  总结:

  虽然这个例子不是很经典,也存在些许问题,希望大家指出问题,共同进步。在添加skinnedMeshRenderer组件的时候有个问题,如果模型选择Generic的时候,Unity3D会挂掉。

这个例子可以运用到骨骼和动作基本一样,但是外形不同的模型中,减少游戏中的资源重复占用。网上还有个更经典的例子,运用的是Character Customization 包,运用的原理是一样的,这里给大家看下运行结果:

  

      

  这个模型是由各个部分的mesh组成,而我的例子是用的一个mesh。在复杂的模型中,我们可以查找子组件的SkinnedMeshRenderer,然后进行操作就可以了。想换哪里就替换哪里的SkinnedMeshRenderer的信息,更新对应的骨骼列表和组合一下网格就行了。

List<CombineInstance> combineInstances = new List<CombineInstance>();
CombineInstance ci = new CombineInstance();
ci.mesh = SrcSkinMeshRender.sharedMesh;
combineInstances.Add(ci);


         还有一个想法是这样的,源资源中是否需要骨头。我发现重组骨头结构的时候其实只是读取了骨头的列表信息,所以我觉得可以写成一个配置信息,直接读取配置信息来重组。这个想法还没有得到验证,等有时间的吧。

 

 

2018-08-28 14:51:00 gaoyu1253401563 阅读数 3429
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

将sketup中生成的fbx模型和贴图导入到unity3D中,导入时注意先导入贴图文件,再导入fbx文件,这两个文件名需要同名,这样可以保证模型导入unity3D中时,模型上的贴图可以贴到所在位置。

 

模型导入到unity3D中后,为了防止模型被穿透,进行如下设置:

1.在project中选中你的模型;

2.在inspector中勾选Generate Colliders;

3.Apply,即可生成模型的mesh collider。
 

 

 

 

 

2017-01-17 00:16:38 qq_26651375 阅读数 3451
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

导入模型、贴图和材质

导入资源包文件

1.新建Unity3d项目SpaceShoot。

2.从Asset Store下载SpaceShoot资源包。https://www.assetstore.unity3d.com/cn/#!/content/13866


3.在浏览器中点击【在Unity中打开】按钮,会跳转到Unity3d软件界面。


4.点击下载,同意协议,当下载完毕后,弹出导入资源包界面。选择【Import】。


5.导入完成,在Project栏出现多个目录,_Completed-Assets目录下为已经完成的Demo,其他文件夹为资源文件夹,双击Scenes文件夹下的Done_Main场景,即可打开该场景,点击运行,游戏Demo运行。


6.依次点击File->New Scene,创建一个新场景,然后再次点击File->Save Scene或者【Ctrl+S】保存场景。在弹出的对话窗中新建文件夹“_Scene_Self”,打开文件夹,文件名Main,单击【保存】按钮。


7.此时可看到Project视图中文件夹”_Scene_Self”和空场景文件“Main”。


9.依次单击菜单项File->Build Setting->PlayerSetting。取消Default Is Full Screen的勾选,然后依次设置Width为400,Height为600。


10.这时在Game窗口中,可以看到Standalone模式下运行窗口的尺寸为400 x 600。


创建飞船对象

1.从Project视图下Asset/Models拖动模型文件vehicle_playerShip到Hierarchy视图,重命名为Player,按图重置Transform组件。

2.添加Rigidbody(刚体)组件:在Hierarchy视图中选择Player,在右侧的Inspector视图中点击Add Component->Physics->Rigidbody。刚体的作用是提供作用力,受到力的作用。另外取消Use Gravity的勾选,否则开始游戏,飞船将受到重力的作用而下坠。


3.添加Mesh Collider组件:选择Player,在Inspector视图中点击Add Component->Physics->Mesh Collider(碰撞体)。作用是让飞船可以玉其他物体产生碰撞,并触发碰撞后的事件(比如销毁等)。还需勾选Convex和Is Trigger,从而将Mesh Collider设置为触发器。

4.添加飞船尾部的粒子效果:在Project/Prefads/VFX/Engine下的预制体engine_player拖动到Hierarchy视图下的Player上,使其成为Player的子对象,并重置Transform组件,其Position的Z为-0.8。


设置摄像机参数

1.在Hierarchy视图中选中Main Camera,将其Transform组件的Rotation设置为(90,0,0),使摄像机处于俯视视角。调整Position(0,10,5),此时飞船处于Game窗口下方。

2.设置投影方式(Projection)为正交投影(Orthographic),并设置size为10,Clear Flags改为SolidColor,Background设为黑色,此时飞船处于合适的位置。



添加背景图片

1.单击GameObject->3DObject->Quad,创建一个平面,重命名Background,重置Transform,移出Mesh Collider,背景不需要碰撞体。此时平面垂直飞船,看不到,须设置其Transform组件的Rotation为(90,0,0),绕X轴逆时针旋转90度。

2.为Background添加纹理图片。将Assets/Textures目录下的tile_nebla_green_dff拖动到Background上。注意图片宽高比是1:2,放大平面时须遵守该比例,不然会失真。

3.选中Background,将Shader改为Unlit/Texture。

4.改变Background大小,Transform组件的Scale为(15,30,0)时,基本充满窗口。此时,飞船与Background重叠,将Background的Transform组件的Position的Y设为-10,使飞船处于Background上方。

添加粒子效果

从Project视图中Assets/VFX/Starfield目录下,拖动预制体Starfield到Hierarchy视图中,Transform组件默认不便,运行游戏,繁星点点。

没有更多推荐了,返回首页