开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

蛮牛译馆

关注:578

当前位置:游戏蛮牛 技术专区 蛮牛译馆

查看: 1081|回复: 18

[Unity教程] 如何在Unity中优化敌军死亡动画

[复制链接]  [移动端链接]
排名
7923
昨日变化
11

260

主题

298

帖子

1509

积分

Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15

UID
159865
好友
9
蛮牛币
3908
威望
0
注册时间
2016-8-1
在线时间
623 小时
最后登录
2017-10-15

蛮牛译员

发表于 2017-8-11 12:14:12 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
本帖最后由 manew_JR 于 2017-8-11 12:15 编辑

如何在Unity中优化敌军死亡动画
002.png 001.png
你好,我们是一个小型的独立游戏工作室,被称为“醉酒星期一”。
我们最近在上做了一个游戏,你在一个竞技场里跑来跑去,用巨大的斧头绕着你自己旋转,并试图攻击其他玩家。Good smash - good kill.
我们使用基于物理的ragdoll动画来让死亡看起来更逼真。开始的时候一切都很好。

然而,当人物和计算的数量开始增长时,游戏开始缓慢而落后于旧手机。禁用所有物理计算给了我们50 - 60个fps和绝对平滑度,但我们不想放弃角色的酷炫死亡画面。

其中一个解决方案是强迫animator制作一组死亡动画。但是我们有一个好主意,把一些布娃娃的死亡直接记录在Unity中,然后显示出想要的动画?死亡将会变得多样化,不需要占据animator,最重要的是——一切都会很快,美丽和现实。

我们最终的效果

003.gif


实现

Unity中的动画剪辑由Clip类提供,该类包含一个AnimationCurve数组。AnimationCurve为特定对象的某一特定属性的更改定义了曲线,例如,localPosition.x。时间轴上的值变化由一些关键帧结构描述。
004.png

这个想法很简单:对于每个字符对象的每个属性,我们创建一个AnimationCurve并将该属性的值存储在每个框架的曲线上。生成的AnimationClip通过AssetDatabase导出。最后生成CreateAsset。

让我们创建类AnimationRecorderItem来跟踪每个字符对象。监视对象的所有属性都将通过字典来描述,其中键是属性的名称,值是动画曲线。


[C#] 纯文本查看 复制代码
Properties = new Dictionary<string, AnimationCurve> ();
 
Properties.Add ( "localPosition.x", new AnimationCurve () );
Properties.Add ( "localPosition.y", new AnimationCurve () );
Properties.Add ( "localPosition.z", new AnimationCurve () );
 
Properties.Add ( "localRotation.x", new AnimationCurve () );
Properties.Add ( "localRotation.y", new AnimationCurve () );
Properties.Add ( "localRotation.z", new AnimationCurve () );
Properties.Add ( "localRotation.w", new AnimationCurve () );
 
Properties.Add ( "localScale.x", new AnimationCurve () );
Properties.Add ( "localScale.y", new AnimationCurve () );
Properties.Add ( "localScale.z", new AnimationCurve () );


对于每个框架中的所有对象属性,都将设置它们的当前值:


[C#] 纯文本查看 复制代码
Properties["localPosition.x"].AddKey (new Keyframe (time, _animObj.localPosition.x, 0.0f, 0.0f));
Properties["localPosition.y"].AddKey (new Keyframe (time, _animObj.localPosition.y, 0.0f, 0.0f));
Properties["localPosition.z"].AddKey (new Keyframe (time, _animObj.localPosition.z, 0.0f, 0.0f));

Properties["localRotation.x"].AddKey (new Keyframe (time, _animObj.localRotation.x, 0.0f, 0.0f));
Properties["localRotation.y"].AddKey (new Keyframe (time, _animObj.localRotation.y, 0.0f, 0.0f));
Properties["localRotation.z"].AddKey (new Keyframe (time, _animObj.localRotation.z, 0.0f, 0.0f));
Properties["localRotation.w"].AddKey (new Keyframe (time, _animObj.localRotation.w, 0.0f, 0.0f));

Properties["localScale.x"].AddKey (new Keyframe (time, _animObj.localScale.x, 0.0f, 0.0f));
Properties["localScale.y"].AddKey (new Keyframe (time, _animObj.localScale.y, 0.0f, 0.0f));
Properties["localScale.z"].AddKey (new Keyframe (time, _animObj.localScale.z, 0.0f, 0.0f));



但是,如果您为每个对象的每个属性记录每个帧的所有值,那么动画的输出文件将会变得太大。让我们介绍一下限制最小变化的条件与前面的框架相比。如果对象移动了,增加和转动了一点,我们将不会记录这些变化。


另外,我们还需要创建一个manager类 —AnimationRecorder.。

这个脚本应该通过动画对象的所有子元素执行,并为每个对象创建一个animation记录器的实例。
同时,它还会立即生成并记住它将被保存在AnimationClip中的相对路径。


根据文档,相对路径生成如下:

Quote
这条曲线适用于游戏对象的路径。相对路径的格式类似于pathname。e.g. "root/spine/leftArm". 如果相对路径为空,则表示动画剪辑所附的游戏对象。


代码将会是:

[C#] 纯文本查看 复制代码
private List<AnimationRecorderItem> _recorders;
 
void Start ()
{
    Configurate ();
}
 
void Configurate ()
{
    _recorders = new List<AnimationRecorderItem> ();
 
    var allTransforms = gameObject.GetComponentsInChildren< Transform > ();
    for ( int i = 0; i < allTransforms.Length; ++i )
    {
        string path = CreateRelativePathForObject ( transform, allTransforms [ i ] );
        _recorders.Add( new AnimationRecorderItem ( path, allTransforms [ i ] ) );
    }
}
 
private string CreateRelativePathForObject ( Transform root, Transform target )
{
    if ( target == root )
    {
        return string.Empty;
    }
 
    string name = target.name;
    Transform bufferTransform = target;
 
    while ( bufferTransform.parent != root )
    {
        name = string.Format ( "{0}/{1}", bufferTransform.parent.name, name );
        bufferTransform = bufferTransform.parent;
    }
    return name;
}
 



计算当前动画时间并记录每个帧的属性值:

[C#] 纯文本查看 复制代码
private float _recordingTimer;
private bool _recording = false;
 
void Update ()
{
 
    if ( _recording )
    {
        for ( int i = 0; i < _recorders.Count; ++i )
        {
            _recorders [ i ].AddFrame ( _recordingTimer );
        }
        _recordingTimer += Time.deltaTime;
    }
}


但是更新函数经常被调用,并且记录动画的每一个帧都是冗余的,所以我们限制了记录。每个人都应该有30个fps。


我们将通过在空格键上敲打来开始记录。

[C#] 纯文本查看 复制代码
private const float CAPTURING_INTERVAL = 1.0f / 30.0f;
 
private float _lastCapturedTime;
private float _recordingTimer;
private bool _recording = false;
 
void Update ()
{
    if ( Input.GetKeyDown ( KeyCode.Space ) && !_recording )
    {
        StartRecording ();
        return;
    }
 
    if ( _recording )
    {
        if (_recordingTimer==0.0f||_recordingTimer-_lastCapturedTime>=CAPTURING_INTERVAL)
        {
            for ( int i = 0; i < _recorders.Count; ++i )
            {
                _recorders [ i ].AddFrame ( _recordingTimer );
            }
            _lastCapturedTime = _recordingTimer;
        }
        _recordingTimer += Time.deltaTime;
    }
}
 
public void StartRecording ()
{
    Debug.Log ( "AnimationRecorder recording started" );
    _recording = true;
}



让我们实现一个动画导出。我们将创建AnimationClip实例并将其填充为所收集的值。

[C#] 纯文本查看 复制代码
private void ExportAnimationClip ()
{
    AnimationClip clip = new AnimationClip ();
    for ( int i = 0; i < _recorders.Count; ++i )
    {
        Dictionary<string,AnimationCurve> propertiles = _recorders [ i ].Properties;
        for ( int j = 0; j < propertiles.Count; ++j )
        {
            string name = _recorders [ i ].PropertyName;
            string propery = propertiles.ElementAt ( j ).Key;
            var curve = propertiles.ElementAt ( j ).Value;
            clip.SetCurve ( name, typeof(Transform), propery, curve );
        }
    }
    clip.EnsureQuaternionContinuity ();
 
    string path = "Assets/" + gameObject.name + ".anim";
    AssetDatabase.CreateAsset ( clip, path );
    Debug.Log ( "AnimationRecorder saved to = " + path );
}




最后,我们将创建AnimationRecorderRagdollHelper助手类,该函数将停止动画的动画对象,打开所有的碰撞,给物体加速度,开始记录我们的动画。动画录制的结束将由我们自己完成。

为了避免场景加载和各种对象的初始化,脚本将开始处理指定的延迟。


这就是所有的教程,我们添加AnimationRecorderRagdollHelper性格,冲击力,然后开始现场,观察人物高高兴兴地飞来飞去。

当冰冷的尸体在地面上结冰时,按下空格键。
008.gif


脚本将我们的动画输出到项目的根。

009.png
010.gif




我们以这种方式为每个角色录制4 - 5个动画,并在角色死亡时随机选择。





原文标题:How We Optimized Ragdoll Death Animation in Unity









评分

参与人数 1鲜花 +2 收起 理由
634948676 + 2 赞一个!

查看全部评分

本帖被以下淘专辑推荐:


回复

使用道具 举报

7日久生情
1718/5000
排名
1780
昨日变化
4

30

主题

556

帖子

1718

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
69941
好友
1
蛮牛币
5357
威望
0
注册时间
2015-1-20
在线时间
535 小时
最后登录
2017-10-18
发表于 2017-8-11 12:31:52 | 显示全部楼层
很厉害的样子,马克一下。。。。。。。。。

回复 支持 反对

使用道具 举报

4四处流浪
491/500
排名
5615
昨日变化
8

5

主题

200

帖子

491

积分

Rank: 4

UID
176358
好友
0
蛮牛币
27
威望
0
注册时间
2016-11-4
在线时间
101 小时
最后登录
2017-10-18
发表于 2017-8-11 13:40:53 | 显示全部楼层
{:106:}{:106:}

回复

使用道具 举报

7日久生情
1983/5000
排名
990
昨日变化
5

3

主题

407

帖子

1983

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
46577
好友
1
蛮牛币
2407
威望
0
注册时间
2014-9-24
在线时间
699 小时
最后登录
2017-10-18
发表于 2017-8-11 14:10:09 | 显示全部楼层
厉害,这个很实用,这让我想到了骑砍里的布娃娃选项里就有限定数量和无限数量
[发帖际遇]: llupll 捡了钱没交公 蛮牛币 降了 2 . 幸运榜 / 衰神榜

回复 支持 反对

使用道具 举报

3偶尔光临
241/300
排名
7273
昨日变化
69

0

主题

42

帖子

241

积分

Rank: 3Rank: 3Rank: 3

UID
159593
好友
0
蛮牛币
565
威望
0
注册时间
2016-7-30
在线时间
71 小时
最后登录
2017-10-18
发表于 2017-8-11 14:42:21 | 显示全部楼层
那么腻害

回复

使用道具 举报

3偶尔光临
295/300
排名
8574
昨日变化
2

0

主题

124

帖子

295

积分

Rank: 3Rank: 3Rank: 3

UID
190613
好友
0
蛮牛币
11
威望
0
注册时间
2016-12-8
在线时间
72 小时
最后登录
2017-9-28
发表于 2017-8-11 15:36:52 | 显示全部楼层
很厉害的样子,马克一下。。。。。

回复 支持 反对

使用道具 举报

7日久生情
2097/5000
排名
570
昨日变化

1

主题

265

帖子

2097

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
2219
好友
1
蛮牛币
5385
威望
0
注册时间
2013-8-19
在线时间
675 小时
最后登录
2017-10-18
发表于 2017-8-11 19:34:50 | 显示全部楼层
很厉害的样子

回复

使用道具 举报

4四处流浪
324/500
排名
7436
昨日变化
5

2

主题

136

帖子

324

积分

Rank: 4

UID
85086
好友
2
蛮牛币
298
威望
0
注册时间
2015-3-26
在线时间
62 小时
最后登录
2017-9-28
发表于 2017-8-11 21:49:23 | 显示全部楼层
啊啊啊啊啊阿啊啊啊啊啊啊啊啊啊啊啊

回复 支持 反对

使用道具 举报

3偶尔光临
169/300
排名
7594
昨日变化
79

0

主题

4

帖子

169

积分

Rank: 3Rank: 3Rank: 3

UID
231194
好友
0
蛮牛币
624
威望
0
注册时间
2017-7-10
在线时间
43 小时
最后登录
2017-10-18
发表于 2017-8-12 08:26:15 | 显示全部楼层
[发帖际遇]: 简简单单happy 在论坛发帖时没有注意,被小偷偷去了 2 蛮牛币. 幸运榜 / 衰神榜

回复

使用道具 举报

6蛮牛粉丝
1403/1500
排名
2397
昨日变化
6

16

主题

469

帖子

1403

积分

Rank: 6Rank: 6Rank: 6

UID
182832
好友
7
蛮牛币
6075
威望
0
注册时间
2016-11-11
在线时间
496 小时
最后登录
2017-10-18
发表于 2017-8-12 09:22:40 | 显示全部楼层
感谢分享

回复

使用道具 举报

4四处流浪
372/500
排名
7045
昨日变化
65

1

主题

174

帖子

372

积分

Rank: 4

UID
233607
好友
1
蛮牛币
294
威望
0
注册时间
2017-7-22
在线时间
59 小时
最后登录
2017-10-19
发表于 2017-8-12 10:15:17 | 显示全部楼层
大神的诞生,小白表示很崇拜
[发帖际遇]: 一个袋子砸在了 2695648715 头上,2695648715 赚了 1 蛮牛币. 幸运榜 / 衰神榜

回复 支持 反对

使用道具 举报

5熟悉之中
718/1000
排名
2664
昨日变化
8

4

主题

78

帖子

718

积分

Rank: 5Rank: 5

UID
127539
好友
0
蛮牛币
3423
威望
0
注册时间
2015-11-1
在线时间
210 小时
最后登录
2017-10-18
QQ
发表于 2017-8-12 14:34:09 | 显示全部楼层
mark,666666666666666

回复 支持 反对

使用道具 举报

排名
17311
昨日变化
11

0

主题

29

帖子

75

积分

Rank: 2Rank: 2

UID
223971
好友
0
蛮牛币
68
威望
0
注册时间
2017-5-27
在线时间
22 小时
最后登录
2017-10-18
发表于 2017-8-14 08:53:29 | 显示全部楼层
animation?

回复

使用道具 举报

7日久生情
1871/5000
排名
6493
昨日变化
50

4

主题

1531

帖子

1871

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
209186
好友
0
蛮牛币
2462
威望
0
注册时间
2017-3-1
在线时间
184 小时
最后登录
2017-10-18
发表于 2017-8-14 09:29:31 | 显示全部楼层
这自己做的动画不必特效差啊

回复 支持 反对

使用道具 举报

排名
32147
昨日变化
91

1

主题

35

帖子

63

积分

Rank: 2Rank: 2

UID
165351
好友
0
蛮牛币
84
威望
0
注册时间
2016-9-1
在线时间
21 小时
最后登录
2017-8-24
发表于 2017-8-14 09:43:00 | 显示全部楼层
顶一个。。。。。。。。。。。。。。。。。。。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

快速回复 返回顶部 返回列表