系列目錄
【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偏偏沒有封裝定時器,所以就需要我們深刻了解其的特性,然后選最優的方式(雖然前人已經栽樹了),萬里長征第一步繼續吧。


