關於unity5中應用Animator控制精靈動畫的經驗
在unity5.4.0中,進行場景製作時需要模擬一些植物的生長過程等,匯入植物各個階段的圖片設定為Sprite,利用精靈進行開發。基本方法是:在場景中的物體上新增Sprite Render元件,通過更換Sprite Render元件中的Sprite屬性模擬植物的生長過程。
在這過程中,如果涉及到的植物只有一個或幾個,且Sprite所佔記憶體空間不多,可以使用如下方法用Resoures匯入進行開發。
(1)把匯入的Sprite圖片放到Resoures檔案中,利用協程模擬製作SPrite動畫。
在指令碼的Start()函式中,使用Resources.Load()匯入Sprite,在指令碼的Update()中,在需要的時機啟動協程設定Sprite Render元件中的Sprite屬性。
private Sprite[] spriteFlowers1 = new Sprite[145]; private Sprite[] spriteFlowers2 = new Sprite[145]; private Dictionary <string ,int> dic = new Dictionary<string ,int> (); void Start () { for (int i = 0; i < 145; i++) { string str; str = "flower1/flower1_" + (i+1).ToString (); spriteFlowers1[i] = Resources.Load<Sprite> (str); Resources.UnloadAsset(spriteFlowers1[i]); str = "flower2/flower2_" + (i+1).ToString (); spriteFlowers2[i] = Resources.Load<Sprite> (str); Resources.UnloadAsset(spriteFlowers2[i]); } } public void EnableFlowersGrowing(GameObject obj) { if (!dic.ContainsKey (obj.name)) { dic.Add (obj.name, obj.GetInstanceID ()); StartCoroutine (ChangeSprite (obj)); } } IEnumerator ChangeSprite (GameObject obj) { string str = obj.name; Sprite[] newSprites = new Sprite[145]; if(str.StartsWith("flower1")) newSprites = spriteFlowers1; else if(str.StartsWith("flower2")) newSprites = spriteFlowers2; if(newSprites.Length>0) { for (int index=0; index < newSprites.Length; index++) { obj.GetComponent<SpriteRenderer> ().sprite = newSprites [index]; yield return new WaitForSeconds (0.02f); } } //等待一定時間後返回到最小 float timeToOriginal = Random.Range (10, 20); yield return new WaitForSeconds (timeToOriginal); if(newSprites.Length>0) { for (int index=newSprites.Length-1; index >=0; index--) { obj.GetComponent<SpriteRenderer> ().sprite = newSprites [index]; yield return new WaitForSeconds (0.02f); } } dic.Remove (str); }
但是當場景中用到的植物比較多十幾個到幾十個時,使用第一種方法後導致記憶體佔用非常高,場景幀率下降到了個位數,解決這個問題時,考慮到可能是一次性載入資源較多導致記憶體問題和幀率較低問題,使用了兩種方法進行嘗試(但是都沒有到達效果):
(2)WWW進行載入,在協程中使用WWW方法載入Sprite,設定Sprite Render元件中的Sprite屬性。
(3)IO進行載入,在協程中使用IO方法載入Sprite,設定Sprite Render元件中的Sprite屬性。public void LoadSpriteByWWW() { string[] substring = gameObject.name.Split('('); string strName = substring[0].Trim(); if (strName == "1") { ImagePath = System.Environment.CurrentDirectory + "/scene/"+strName; #if (UNITY_EDITOR) ImagePath = Application.dataPath + "/scene/"+strName; #endif DirectoryInfo raw = new DirectoryInfo(ImagePath); FileInfo[] images = raw.GetFiles("*.png"); ImagePath = ImagePath + "/" + strName + "_" + "*" + images.Length.ToString(); if (raw.Exists) StartCoroutine(EnablePlantToGrow(ImagePath)); } } IEnumerator EnablePlantToGrow(string imagePath) { string[] substring = imagePath.Split('*'); int ImageCounts = int.Parse(substring[1].Trim()); string subImagePath = substring[0].Trim(); for (int i = 1; i <= ImageCounts; i++) { string strPath = "file://" + subImagePath+ i.ToString()+".png"; strPath= strPath.Replace("\\","/"); WWW www = new WWW(strPath); yield return www; if (www!=null && string.IsNullOrEmpty(www.error)) { Texture2D textureMy = www.texture; Sprite sprite = Sprite.Create(textureMy, new Rect(0, 0, textureMy.width, textureMy.height), new Vector2(0.5f, 0.5f)); Sprite cacheSprite = gameObject.GetComponent<SpriteRenderer>().sprite; gameObject.GetComponent<SpriteRenderer>().sprite = sprite; Destroy(cacheSprite); yield return new WaitForSeconds(0.02f); } } Resources.UnloadUnusedAssets(); }
IEnumerator LoadByIO()
{
string[] substring = gameObject.name.Split('(');
string strName = substring[0].Trim();
// if (strName == "1")
{
ImagePath = System.Environment.CurrentDirectory + "/scene/";
#if (UNITY_EDITOR)
ImagePath = Application.dataPath + "/scene/";
#endif
DirectoryInfo raw = new DirectoryInfo(ImagePath);
if (raw.Exists)
{
FileInfo[] images = raw.GetFiles("*.png");
int ImageCounts = images.Length;
for (int i = 1; i <= ImageCounts; i++)
{
string strPath = ImagePath + strName + "/" + strName + "_" + i.ToString() + ".png";
strPath = strPath.Replace("\\", "/");
if (File.Exists(strPath))
{
//建立檔案讀取流
FileStream fileStream = new FileStream(strPath, FileMode.Open, FileAccess.Read);
fileStream.Seek(0, SeekOrigin.Begin);
//建立檔案長度緩衝區
byte[] bytes = new byte[fileStream.Length];
//讀取檔案
fileStream.Read(bytes, 0, (int)fileStream.Length);
//釋放檔案讀取流
fileStream.Close();
fileStream.Dispose();
fileStream = null;
//建立Texture
int width = bytes[16] * 16 * 16 * 16 * 16 + bytes[17] * 16 * 16 * 16 + bytes[18] * 16 * 16 + bytes[19];
int height = bytes[20] * 16 * 16 * 16 * 16 + bytes[21] * 16 * 16 * 16 + bytes[22] * 16 * 16 + bytes[23];
// Debug.Log(width+ "|"+ height+"|" + bytes[16] + "|" + bytes[17] + "|" + bytes[18] + "|" + bytes[19]);
Texture2D texture = new Texture2D(width, height);
texture.LoadImage(bytes);
////建立Sprite
// Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.one * 0.5f);
Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
Sprite cacheSprite = gameObject.GetComponent<SpriteRenderer>().sprite;
gameObject.GetComponent<SpriteRenderer>().sprite = sprite;
// gameObject.GetComponent<RawImage>().texture = texture;
DestroyImmediate(cacheSprite,true);
}
yield return new WaitForSeconds(0.02f);
}
}
}
}
實在抱歉,只能是我的水平低的可憐,無法把想法實現出來。君子善假於物也。
Unity中早就集成了Animator控制Sprite的Animation,實現動態設定Sprite Render元件中的Sprite屬性。可以參考unity 2D遊戲開發 製作幀動畫的兩種方法
我採用了第二種方法進行嘗試。
在每個Animator中掛載兩個Animation,中間建立Transition,同時設定第一個Animation的Loop Time為FALSE,第二個Animation的Loop Time為TRUE。
在初始時可以根據需要,設定物體是隱藏還是顯示為某一幀,具體的控制指令碼如下:
[HideInInspector]
public int ctrlFlag = 0;
private float timerInternal = 3f;
private float timeCount = 0f;
private bool isStartDown;
AnimationClip an,an1;
public bool isShow = false;
private Animator animator;
// Use this for initialization void Start() { animator = this.GetComponent<Animator>(); an = animator.runtimeAnimatorController.animationClips[0]; an1 = animator.runtimeAnimatorController.animationClips[1]; if (isShow) { animator.Play (an.name, 0, 0f); animator.Update (0f); animator.speed = 0; gameObject.SetActive(true); } else { gameObject.SetActive(false); } }
void Update()
{
//計時
if (2 == ctrlFlag && !isStartDown)
{
timeCount += Time.deltaTime;
if (timeCount > timerInternal)
{
timeCount = 0;
isStartDown = true;
}
}
//出現
if (1 == ctrlFlag)
{
isStartDown = false;
ctrlFlag = 0;
animator.speed = 1;
}
//出現一段時間後消失
if (2 == ctrlFlag && isStartDown)
{
ctrlFlag = 0;
isStartDown = false;
if (isShow)
{
animator.Play (an.name, 0, 0);
animator.Update (0f);//停留在第一幀
animator.speed = 0;
}
else
gameObject.SetActive(false);
}
}
以上是自己三天的經驗小結,希望對大家有幫助,並希望大家留言指導。