Unity3D实现2D人物动画① UGUI&Native2D序列帧动画

原理

看过前篇的朋友,一定能猜到这篇的内容了,2D人物动画,这是一个老生常谈的话题,很多人都写过或者提供过类似的代码,本文还是遵守着重原理,代码次之的原则。下面是根据以前自己学习的时候学习“深蓝色右手”WPF游戏教程的“WPF/Silverlight动画及游戏系列教程”,先结合Unity3d技术改编的原理文字

动态实现2D人物角色动画目前有两种主流方法,下面我会分别进行介绍。
      第一种方法我称之为图片切换法,准备工作:首先通过3DMAX等工具3D渲染2D的方法制作出角色,然后将角色每个动作均导出8个方向每方向若干帧的系列图片(如果是有方向的魔法图片,很多2D-MMORPG往往会导出16个方向的系列帧图片以求更为逼真),即将每个人物每个动作的各方向的每帧均存成一张图片,如下图仅以从破天一剑游戏中提取的素材为例:

(特别申明:本系列教程所使用的如有注明归属权的图片素材均来源于网络,请勿用于商业用途,否则造成的一切后果均与本人无关。)

1

从上图可以看到,我将人物向右方跑步共8帧图片通过Photoshop分别将画布等比例扩大成150*150象素图片(因为是提取的素材,初始宽和高是不均衡值,所以必须扩大成自己的需求,这样人物会在图片中居中,并且为后期加入武器或坐骑留好余地。稍微的偏离也可以在后期进行微调),并将他们从开始到结束分别命名为0.png,1.png,2.png,3.png,4.png,5.png,6.png,7.png,然后将这8张图片保存到相关目录下,到此准备工作终于结束了

这里在WPF中有一个UI线程级别的定时器DispatcherTimer,而Unity中没有提供类似的机制(或许是我不知道),Unity主要是心跳来控制的也就是Update函数了,但是这里的原理就是帧动画,每个多少帧变化一下player的动作图片即可,但我们知道帧就是和时间相关的。

简单的说:就是定义一个图片数组,然后实现一个定时器,时间到了就获取数组里的一张图,替换精灵的背景图片。

1(1)

实现

这里我们把问题分解主要是两个子问题,一、定时获取图片替换精灵背景,简称定时器;二、数组的图片循环获取,简称数组顺序遍历

先从软柿子开始,二比较简单,一个数组,加一个全局基数器变量 搞定

private int currentTexture = 0;
public Sprite[] textureArray;
private SpriteRenderer spriteRenderer;

//遍历数组 到数组未重新回到0索引

void NextTexture()
{
    currentTexture++;
    if (currentTexture >= textureArray.Length)
    {
        currentTexture = 0;
    }

    spriteRenderer.sprite = textureArray[currentTexture];
}

一、定时器,稍微麻烦点,Unity3d并没有提供像样的UI定时器封装,这里为了验证 这种定帧动画的原理,我用几种Unity3d中定时器机制分别实现了动画功能,实际开发中用的A和D方法比较多,至少我查了不少教程基本是A和D

首先是变量

private float animationDeltaTime;
private float animationDelay = 5 / 60f;

A、Update 心跳延时定时器

void Update()
{
    animationDeltaTime += Time.deltaTime;
    // Debug.Log(animationDeltaTime);
    if (animationDeltaTime >= animationDelay)
    {
        animationDeltaTime = 0;

        NextTexture();
    }
}

B、协程递归定时器

void Start()
{
    spriteRenderer = GetComponent<SpriteRenderer>() as SpriteRenderer;
    StartCoroutine(TextureChanger());  
}

IEnumerator TextureChanger()
{
    yield return new WaitForSeconds(animationDelay);
    if (true)
    {
        //Debug.Log(animationDeltaTime);
        NextTexture();
        StartCoroutine(TextureChanger());
    }
}

C、InvokeRepeating定时器

void Start()
{
    spriteRenderer = GetComponent<SpriteRenderer>() as SpriteRenderer;
    InvokeRepeating("NextTexture", 1, 0.1f);//1秒后调用LaunchProjectile () 函数,之后每5秒调用一次      
}

D、时长求余法(我自己起的名字,比较巧妙可能也是用的比较多的方法)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using  UnityEngine;
using  System.Collections;
 
public  class  PlayerAnimator : MonoBehaviour {
 
     public  Sprite[] sprites;
     public  float  framesPerSecond;
 
     private  SpriteRenderer spriteRenderer;
     // Use this for initialization
     void  Start () {
         spriteRenderer = GetComponent<Renderer>()  as  SpriteRenderer;
     }
     
     // Update is called once per frame
     void  Update () {
         int  timeIndex = ( int )(Time.timeSinceLevelLoad * framesPerSecond);
         int  index = timeIndex % sprites.Length;
         spriteRenderer.sprite = sprites[index];
 
     }
}

  

原理的代码分析和代码展示完毕,下面是自己在网上找的前人分享的一些代码,自测可以运行,主要的问题还是一句老话,“原理很简单,现实很残酷”,实际一个简单的2d动画涉及的东西很多,比如性能效率,状态控制,封装合理性等等吧。

A、Unity3d UGUI序列帧动画 实现 (原文地址:http://www.cnblogs.com/mrblue/p/5191183.html)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
using  UnityEngine;
 
using  System.Collections;
 
using  System.Collections.Generic;
 
using  UnityEngine.UI;
 
using  System;
 
[RequireComponent( typeof (Image))]
 
public  class  UGUISpriteAnimation : MonoBehaviour
 
{
 
     private  Image ImageSource;
 
     private  int  mCurFrame = 0;
 
     private  float  mDelta = 0;
 
     public  float  FPS = 5;
 
     public  List<Sprite> SpriteFrames;
 
     public  bool  IsPlaying =  false ;
 
     public  bool  Foward =  true ;
 
     public  bool  AutoPlay =  false ;
 
     public  bool  Loop =  false ;
 
     public  int  FrameCount
 
     {
 
         get
 
         {
 
             return  SpriteFrames.Count;
 
         }
 
     }
 
     void  Awake()
 
     {
 
         ImageSource = GetComponent<Image>();
 
     }
 
     void  Start()
 
     {
 
         if  (AutoPlay)
 
         {
 
             Play();
 
         }
 
         else
 
         {
 
             IsPlaying =  false ;
 
         }
 
     }
 
     private  void  SetSprite( int  idx)
 
     {
 
         ImageSource.sprite = SpriteFrames[idx];
 
         ImageSource.SetNativeSize();
 
     }
 
     public  void  Play()
 
     {
 
         IsPlaying =  true ;
 
         Foward =  true ;
 
     }
 
     public  void  PlayReverse()
 
     {
 
         IsPlaying =  true ;
 
         Foward =  false ;
 
     }
 
     void  Update()
 
     {
 
         if  (!IsPlaying || 0 == FrameCount)
 
         {
 
             return ;
 
         }
 
         mDelta += Time.deltaTime;
 
         if  (mDelta > 1 / FPS)
 
         {
 
             mDelta = 0;
 
             if (Foward)
 
             {
 
                 mCurFrame++;
 
             }
 
             else
 
             {
 
                 mCurFrame--;
 
             }
 
             if  (mCurFrame >= FrameCount)
 
             {
 
                 if  (Loop)
 
                 {
 
                     mCurFrame = 0;
 
                 }
 
                 else
 
                 {
 
                     IsPlaying =  false ;
 
                     return ;
 
                 }
 
             }
 
             else  if  (mCurFrame<0)
 
             {
 
                 if  (Loop)
 
                 {
 
                     mCurFrame = FrameCount-1;
 
                 }
 
                 else
 
                 {
 
                     IsPlaying =  false ;
 
                     return ;
 
                 }       
 
             }
 
             SetSprite(mCurFrame);
 
         }
 
     }
 
     public  void  Pause()
 
     {
 
         IsPlaying =  false ;
 
     }
 
     public  void  Resume()
 
     {
 
         if  (!IsPlaying)
 
         {
 
             IsPlaying =  true ;
 
         }
 
     }
 
     public  void  Stop()
 
     {
 
         mCurFrame = 0;
 
         SetSprite(mCurFrame);
 
         IsPlaying =  false ;
 
     }
 
     public  void  Rewind()
 
     {
 
         mCurFrame = 0;
 
         SetSprite(mCurFrame);
 
         Play();
 
     }
 
}

  

B、Native2D 序列帧动画 实现

这部分代码已经在上文“D、时长求余法(我自己起的名字,比较巧妙可能也是用的比较多的方法)”中贴出,这里不再重复

总结

实际上“序列帧动画”的实现原理很简单,就是一个定时器,但是Unity3d偏偏没有封装定时器,所以就需要我们深刻了解其的特性,然后选最优的方式(虽然前人已经栽树了),万里长征第一步继续吧

转载地址:http://www.cnblogs.com/IlidanStormRage/p/6043642.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值