系列目錄
【Unity3D基礎】讓物體動起來①--基於UGUI的鼠標點擊移動
【Unity3D基礎】讓物體動起來②--UGUI鼠標點擊逐幀移動
時光煮雨 Unity3D讓物體動起來③—UGUI DoTween&Unity Native2D實現
時光煮雨 Unity3D實現2D人物動畫① UGUI&Native2D序列幀動畫
時光煮雨 Unity3D實現2D人物動畫② Unity2D 動畫系統&資源效率
原理
看過前篇的朋友,一定能猜到這篇的內容了,2D人物動畫,這是一個老生常談的話題,很多人都寫過或者提供過類似的代碼,本文還是遵守着重原理,代碼次之的原則。下面是根據以前自己學習的時候學習“深藍色右手”WPF游戲教程的“WPF/Silverlight動畫及游戲系列教程”,先結合Unity3d技術改編的原理文字
動態實現2D人物角色動畫目前有兩種主流方法,下面我會分別進行介紹。
第一種方法我稱之為圖片切換法,准備工作:首先通過3DMAX等工具3D渲染2D的方法制作出角色,然后將角色每個動作均導出8個方向每方向若干幀的系列圖片(如果是有方向的魔法圖片,很多2D-MMORPG往往會導出16個方向的系列幀圖片以求更為逼真),即將每個人物每個動作的各方向的每幀均存成一張圖片,如下圖僅以從破天一劍游戲中提取的素材為例:(特別申明:本系列教程所使用的如有注明歸屬權的圖片素材均來源於網絡,請勿用於商業用途,否則造成的一切后果均與本人無關。)
從上圖可以看到,我將人物向右方跑步共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的動作圖片即可,但我們知道幀就是和時間相關的。
簡單的說:就是定義一個圖片數組,然后實現一個定時器,時間到了就獲取數組里的一張圖,替換精靈的背景圖片。
實現
這里我們把問題分解主要是兩個子問題,一、定時獲取圖片替換精靈背景,簡稱定時器;二、數組的圖片循環獲取,簡稱數組順序遍歷
先從軟柿子開始,二比較簡單,一個數組,加一個全局基數器變量 搞定
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、時長求余法(我自己起的名字,比較巧妙可能也是用的比較多的方法)
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)
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偏偏沒有封裝定時器,所以就需要我們深刻了解其的特性,然后選最優的方式(雖然前人已經栽樹了),萬里長征第一步繼續吧。