1. 程式人生 > >關於unity5中應用Animator控制精靈動畫的經驗

關於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屬性。

 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();


    }
(3)IO進行載入,在協程中使用IO方法載入Sprite,設定Sprite Render元件中的Sprite屬性。
 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);
        }
}


以上是自己三天的經驗小結,希望對大家有幫助,並希望大家留言指導。