2016-01-06 18:13:34 a44860589 阅读数 1459

Unity3d Mecanim动画如何应用3dmax中的动画位移

大家好!我是小周,这是我的第一篇博客,自己喜欢游戏,所以大学报了计算机专业,毕业以后也如愿以偿的进入了游戏开发者的队列,现在用Unity3d做MMORPG游戏快一年了,尝试着分享一下自己的经验。
每博加油:不计成本地努力!

程序动画流程

游戏当中角色动画前后制作过程:2D形象设计图->3D模型制作->为模型搭建骨骼制作动画->导出FBX->导入FBX->程序控制动画展现(主要讲述 3dmax 到 unity3d的过程)。
为了达到应用3dmax中的动画位移的目的,在上诉环节应该注意的一些地方,如果没有注意到,则多半需要返工啦,经验就是这么被实践出来的。。。

3D模型制作注意事项

在3dmax中制作好模型以后,一定要记得用ResetXform重置一下,否则在后续环节中很大可能会出错(比如大小比例、旋转不对等等),至于重置的作用,这里就不多讲了,可以百度一下ResetXform。

骨骼搭建和制作动画注意事项

如果要应用3dmax中的动画位移作为程序位移,则unity3d不支持3dmax中的虚拟体和约束关系,所以在搭建骨骼的时候不要使用虚拟体和约束关系。
为了应用3dmax动画中的位移,则需要一个根“根骨骼”,来用做程序中的Root Node。“根骨骼”起着至关重要的作用,在讲“根骨骼”之前,先来讲讲这两种软件的参考坐标系。
也许你会问参考坐标系有什么好讲的,我也认为没什么好讲的,但是两种软件的参考坐标系不一样,如果不注意这点,可能会带来一些不必要的麻烦。
在unity3d中是左手坐标系(Y轴朝上、Z轴朝前、X轴向右)、而在3dmax中是右手坐标系(Z轴朝上、Y轴朝前、X轴向右),而且不同的轴向分量在导出导入后对应关系也不一样,下面是自己试验得出的对应关系:
3dmax.z = unity3d.y
3dmax.x = -unity3d.x
3dmax.y = -unity3d.z
( - 为负号)
如果没考虑到对应关系,直接在3dmax做一个向前运动的动画(即向着3dmax的Y轴正向移动),在导入到unity3d以后就不是向前移动,按照上面的对应关系则是向后移动,所以要在unity3d中得到正确的结果,则在3dmax中制作动画时应该把Y轴负方向做为向前移动的方向,把X轴负方向作为向右移动的方向,竖直轴还是Z轴。
回过头来讲“根骨骼”,为了满足上诉移动方向的需求,则搭建骨骼的时候模型朝向Y轴负方向,“根骨骼”也应是朝向Y轴负方向,后续搭建的骨骼,都必须是“根骨骼”的子节点,“根骨骼”的位置是模型中心位置在水平面XY上的投映点,动作有标准起始动作,这一帧的“根骨骼”应该在世界坐标的原点,由于导出导入、坐标系对应关系和unity3d参数设置的原因,“根骨骼”在动画制作过程中,不能有旋转,而且只能在Y轴上运动,大致保持着模型中心映射到水平面XY上的位置(根据程序导入后的效果有微调)。

3dmax导出注意事项

做好以后,可以整体导出,也可以分段导出多个,格式为FBX。
导出要注意单位,一般为米,一个项目应该有统一的标准。
由于上述的参考坐标系的不同,在导出要设置“Y-up”(Advanced Options—>Axis Conversion中),unity3d是Y轴向上。
3dmax2011是比较稳定的版本,但是导出的FBX会比2014等高版本导出的资源大小更大,所以在保证效果的前提的下,可把做好的动作用高版本的3dmax导出,这样会减少不少的资源量。

导入unity3d注意事项

FBX导入unity3d以后,参数的设置:
在Model面板中,Scale Factor 大小比例,根据项目实际情况而定,一个项目应该有统一的标准,Import BlendShapes一般用于做面部精细的表情,所以一般不勾选。
在Rig面板中,Animation Type选择Generic(Mecanim在Humanoid的基础上增强了对Bone的骨骼支持),Root Node则选择上述的“根骨骼”。如果是多个FBX文件,设置好了一条标准骨骼,则Copy From Other Avatar即可。
在Animations面板中,Anim.Compression一般不需要,选择Off,因为动作已经做得比较完善了,如果选择了其他,会二次调整动画的表现,增加一些微调,与预期效果不一样。Clips中分割好每一个动作,这里需要注意的是每一个片段的开头都必须是标准动作(如果是连击动作去接上一击的动作除外),“根骨骼”在世界坐标原点(即便是连击动作,要进行这样的分段,则在每段开始时,在制作时“根骨骼”也要托回到世界坐标原点,在继续移动)。然后设置每一段动画的参数,要应用3dmax动画中的位移则设置参数如下图:
这里写图片描述

程序应用3dmax动画位移作为程序位移

最后就是在程序中实现,实例一个对应FBX模型对象,然后加上Animator组件(一般会自动加上),新建一个Animaotr Controller将上述分好的动画片段引入,Avatar用对应FBX统一用的标准骨骼。控制播放哪段动画,这里就不细讲了,可以上网搜索Mecanim动画系统。
这里讲一下注意事项:
1.要应用动画中的位移作为程序位移,Animator组件上有一个总开关Apply Root Motion,如果要应用,则需勾选。
2.如果对象上还存在Rigidbody刚体组件,则Constraints中Freeze Position中的 X 与 Z 不能勾选,若果勾选了,即是锁定水平上的移动,应用动画位移就体现不出来。

上述经验可能不全面,如果遇到新的问题,可以一起讨论讨论,如果错误,还望指正,回复或者联系我的QQ489289839都可以,感谢大家阅读我的博客。

2019-10-28 15:00:39 songhuanfei2017 阅读数 41

静态对象是Unity提供的一个属性,它可以附加在游戏对象或者Prefab上。

它的原理是限制物体在运行中不能发生位移变化,预先生成一些辅助的数据,从而达成一种用内存换时间的优化方式。

静态元素的种类很多,选择任意游戏对象,Insoector面板单击右上角Static下拉框,即可选择设置。

Lightmap Static :用来表示接受烘培光照计算,可烘培光照贴图。

Occluder Static :表示自身是否可以遮挡其他元素。

Batching Static :表示支持静态合批。

Navigation Static :表示可烘培寻路网格。

Occludee Static :表示自身可以被遮挡剔除掉。

Off Mesh Link Generation :寻路连接不同区域的点。

Reflection probe Static :反射探头。

2015-12-30 18:51:20 chrisfxs 阅读数 2047

更漂亮的位移指的是先慢再快再慢的这种位移,更像现实中的位移。也叫平滑阻尼。


这里有两种实现方式:

1.通过计算值:调的函数是Mathf.SmoothStep

例:

    float t = 0;
	// Update is called once per frame
    void Update()
    {
        if(sprite.fillAmount != 1 && transform.gameObject.activeSelf)
        {
            t += Time.deltaTime/5;
            if (m_startAction)
            {
                sprite.fillAmount = Mathf.SmoothStep(sprite.fillAmount, 1, t);
            }
            else
            {
                sprite.fillAmount = 1;
            }
        }


    }

    public void PerformAction()
    {
        m_startAction = true;
    }

2.通过向量Vector3:调的函数是Vector3.SmoothDamp

例:

        if (_buttonUp)
        {
            AddingFriendButton.transform.localPosition = Vector3.SmoothDamp(AddingFriendButton.transform.localPosition, _tabOriginalPosition, ref _velocity, 0.2f);
            if (Vector3.Distance(AddingFriendButton.transform.localPosition, _tabOriginalPosition) < 0.01)
                _buttonUp = false;
        }


3.通过向量Vector3,设定持续时间,

    Vector3 org = new Vector3();
    Vector3 vec = new Vector3();
    Vector3 _velocity = new Vector3();
    public void RotateCameraFromGesture(Vector3 delta)
    {
        org = SceneCameraGameObject.transform.localEulerAngles;
        vec = org;

        vec.y += delta.x * 5;
        vec.x += delta.y * -5;
        vec.z = 0;

        vec = Vector3.SmoothDamp(org, vec, ref _velocity, 1f);
        if (vec.x < 0)
            vec.x = 0;
        if (vec.x > 45)
            vec.x = 45;
        SceneCameraGameObject.transform.localEulerAngles = vec;

        //SceneCamera.transform.Rotate(new Vector3(-delta.y * 0.1f, 0, delta.x));//这个根据世界坐标转,上下左右转就出问题了
    }



2015-01-22 14:46:43 book_longssl 阅读数 2644
效果图

分析
    什么是视差滚动?度娘的解释:让多层背景以不同的速度移动,形成立体的运动效果。从效果图可以看出,主场景背景大致分为3层,草地、山河还有云彩,每一层的速度都不一样。接着分析,虽然度娘的解释是以速度来阐述,但用速度来计算并不合适,因为主层(即草地层)的滚动是跟随我们手指的移动,所以应该把速度转换为位移来计算。既然用位移来计算,每一层的位移不同,怎么样才能把多个层同步起来,我使用了归一化方法,把整个场景的滚动看作是0~1之间的归一化位移,每个层的滚动只需乘以各自层的最大位移。有了归一化位移来实现视差滚动,接下来就是让场景的滚动跟随手指移动,现在其实很好实现,只需要根据手指移动的距离(X方向)和主层的最大位移计算。最后要分析的就是惯性,在手指离开屏幕后场景仍将滚动一段时间,其实就是一个减速运动。(终于分析完了,人家不会分析,憋到现在已经内伤了
 

实现
第一步,实现SetPosition()方法,通过这个方法设置归一化位置,然后将所有层移动到正确位置。
  1. public Transform[] Layers;//每个层的Transform。
  2.      public float[] Offsets;//对应每个层的最大位移。
  3.      int count;
  4.      float location;//定义归一化位置。
  5.      void Start ()
  6.      {
  7.                count = Layers.Length;
  8.      }
  9. public void SetPosition(float position)
  10.      {
  11.                location = Mathf.Clamp01(position);
  12.                for (int i = 0; i < count; i++)
  13.                {
  14.                         Layers[i].localPosition = new Vector3(Offsets[i] * location ,0, 0);
  15.                }
  16.      }
复制代码
 
 

    当location0时显示各个层最左边的内容,为1时显示各个层最右边的内容。将各个层的根节点赋给Layers属性。如下图所示:


整体效果如下:


    然后将层往左边移动直到显示最右边的内容,这个时候根节点的X坐标就是我们要的最大位移,将这个值赋给对应的Offsets(这个值为负数)。
第二步,实现手指跟随的滚动。
  1. bool dragged;
  2. float lastTouch;
  3. float dragOffset;
  4. float touchToPos;
  5. void Start ()
  6. {
  7.           count = Layers.Length;

  8.           dragged = false;
  9.           touchToPos = 1f / Screen.width * camera.orthographicSize * 2f * camera.aspect / Mathf.Abs(Offsets[0]);
  10. }
  11. void Update ()
  12. {
  13.           if (Input.GetMouseButtonDown(0))
  14.           {
  15.                    dragged = true;
  16.                    lastTouch = Input.mousePosition.x;
  17.           }
  18.           if (Input.GetMouseButtonUp(0))
  19.           {
  20.                    dragged = false;
  21.           }
  22.           if (dragged)
  23.           {
  24.                    float currTouch = Input.mousePosition.x;
  25.                    dragOffset = lastTouch - currTouch;
  26.                    lastTouch = currTouch;
  27.                    dragOffset *= touchToPos;
  28.                    location += dragOffset;
  29.                    SetPosition(location);
  30.           }
  31. }
复制代码
 

    这段代码就是计算每次手指移动时,场景归一化位置改变量——dragOffset,并将该量与当前location相加后使用SetPosition来更新各个层。
第三步,实现惯性。

  1. bool tweened;
  2. float tweenTime;
  3. const float MaxTweenTime = 0.5f;
  4. void Start ()
  5. {
  6.           count = Layers.Length;
  7.           dragged = false;
  8.           tweened = false;
  9.           touchToPos = 1f / Screen.width * camera.orthographicSize * 2f * camera.aspect / Mathf.Abs(Offsets[0]);
  10. }
  11. void Update ()
  12. {
  13.           if (Input.GetMouseButtonDown(0))
  14.           {
  15.                    dragged = true;
  16.                    tweened = false;
  17.                    lastTouch = Input.mousePosition.x;
  18.           }
  19.           if (Input.GetMouseButtonUp(0))
  20.           {
  21.                    dragged = false;
  22.                    tweened = true;
  23.                    tweenTime = 0f;
  24.           }
  25.           if (dragged)
  26.           {
  27.                    ……
  28.           } else if (tweened)
  29.           {
  30.                    tweenTime += Time.deltaTime;
  31.                    if (tweenTime > MaxTweenTime)
  32.                    {
  33.                             tweened = false;
  34.                    } else
  35.                    {
  36.                             float offset = dragOffset * (1 - tweenTime / MaxTweenTime);
  37.                             location += offset;
  38.                             SetPosition(location);
  39.                    }
  40.           }
  41. }
复制代码


    这里利用到第二步中所计算的dragOffset,将最后一次计算(即手指离开屏幕前)得出的dragOffset在最大惯性时间MaxTweenTime内减为0并累加到location上,以达到减速运动的效果,而且移动的距离和手指移动的速度相关。
终于写完了,还是代码爽 

2019-10-28 15:28:33 songhuanfei2017 阅读数 38

Lightmap 技术的原理是将场景中的灯光与物体产生的光照与阴影信息烘培在一张或者多张Lightmap 贴图中,这些物体将不再参与实时光照计算,从而减少了大量的性能开销。

它的缺点就是参与烘培计算的对象在游戏过程中不能发生移动。

所以游戏中通常会将物体分成两类:一类是可发生位移变化的对象,他们使用实时光照计算;另一类是不可发生位移变化的,他们采取预先烘培Lightmap。

//--设置烘培贴图

首先需要在场景中选中参与烘培计算的游戏对象,可以单独调整某个对象。

关于其中各个属性的介绍

打开Window → Rendering → Lighting Settings。

Lighting属性以及烘培详解

Auto Generate 选择后会自动烘培,如果场景中元素比较多,不建议开启。

//--实时光和烘培光共存

在游戏中少部分物体需要实时光,例如控制主角移动时,需要动态地产生光照和阴影信息。

如图,可以在Mode 中设置灯光的属性:

其中Realtime 表示实时光,Mixed 表示实时光和烘培光的混合模式,Baked 表示仅烘培光。

//--灯光管理

如果场景中灯光很多,如何管理。

打开Window → Rendering → Lighting Explorer。

我们可以快速设置灯光开关状态 灯光类型和模式等,并且点击其中一个光源,即可在Scene视图中找到它。

//--

更换烘培贴图实际上就是设置正确的lightmapIndex 和 lightmapScaleOffset。

//--

Unity3D 中旋转和变换

阅读数 10033

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