Sunday, October 19, 2014

Create Asset Loader in Unity

I beleave that Unity community is quite large but I've feeling that SHARED RESOURCE & SOLUTIONS about any topic are quite few. When you google for something for Flash you can find almost everything if not everything AND Its FREE. When you google for something for Unity you mainly finish on forums/answers/references of Unity web site and at the end you finish to ASSET Store with the song Money,Money....Of course in Flash you have polished paid components but you have tons free. Game design or knowledge shouldn't be monopoly only to companies with fat purse.. I need simple compact asset loader. What I found was terrible example:
using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour {
    public string url = "http://images.earthcam.com/ec_metros/ourcams/fridays.jpg";
    IEnumerator Start() {
        WWW www = new WWW(url);
        yield return www;
        renderer.material.mainTexture = www.texture;
    }
}
and ofcourse their UnityPro paid contra part AssetBundle.LoadAsync..., some quzzy solution on answers forum and ofcourse paid Assets in AssetStore. In Flash you have for example BulkLoader or many other which are high quality and again free.(COMMUNITY?). And ofcourse their paid loaders with prof support,documents,thought tested.... I could remember MSDN in past as bad documentation reference but today is much much better with more examples but that's not the case with Unity docs. Overall against 2 line examples teaching beginners how to do unusable(would you define all you urls manually as public members and you will yield(wait) in sequence one by one....!?) and crap code. While building it I tried to adapt some good practices of Flash Loaders thinking that if you can do it in AS3 with 10x stronger language as C# you can do anything.
Define what need to be loaded and listen for loading status.
Loader request = new Loader();
request.Add("http://images.earthcam.com/ec_metros/ourcams/fridays.jpg");
request.Add("some.xml");
 request.LoadComplete += new EventHandler>>(onLoadComplete);
                request.Error += new EventHandler>(onLoadError);
                request.LoadItemComplete += new EventHandler>(onLoadItemComplete);
                request.load();
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using System.Collections;


namespace ws.winx.unity
{
    public class LoaderEvtArgs : EventArgs
    {


        public readonly T data;
        

        public LoaderEvtArgs(T data)
        {
            this.data = data;

        }
    }


 public class Loader:IDisposable
 {
        public event EventHandler>> LoadComplete;
        public event EventHandler> Error;
        public event EventHandler> LoadItemComplete;


        protected bool _isRunning=false;

        private static List _wwwList;
        private static List _queueList;

        protected static List queueList
        {
            get { if (_queueList == null) _queueList = new List(); return _queueList; }
        }

        protected static List wwwList{
            get{ if(_wwwList==null) _wwwList=new List(); return _wwwList; }
        }

        private static MonoBehaviour _behaviour;


        protected static MonoBehaviour behaviour
        {
            get { if (_behaviour == null) { _behaviour = (new GameObject("WWWRequest")).AddComponent(); } return _behaviour; }
        }

        public void load()
        {
            if (!_isRunning)
            {
                _isRunning = true;
                behaviour.StartCoroutine(check());
                if (wwwList.Count != queueList.Count)
                {
                     foreach (WWW www in queueList)
                     {
                        queueList.Add(www);
                     }
                }
            }
        }

      

        public void Add(string url){
            if (Application.isEditor)
                url = "file:///" + url;

            WWW w = new WWW(url);
            Add(w);

        }

        public void Add(WWW www)
        {
           
            wwwList.Add(www);
            queueList.Add(www);

        }


       


       
        

        IEnumerator check()
        { 
            int i;
   WWW www;
            while (true)
            {
               i=0;

               while (queueList.Count>i)
    {

     www=queueList.ElementAt(i);
                    if (www.isDone)
                    {
                        if (!String.IsNullOrEmpty(www.error))
                        {


                            if (Error != null)
                            {
                                //Error(this,new LoaderEvtArgs(www));
                                Dispatch(Error, new LoaderEvtArgs(www.error));
                            }
                            else
                            {
                                Debug.LogError(www.error);
                            }

       
                           

                        }else 
       if (LoadItemComplete != null)
                                Dispatch(LoadItemComplete, new LoaderEvtArgs(www));
        //LoadItemComplete(this, new LoaderEvtArgs(www));
                       
                        queueList.RemoveAt(i);
                    }

                    i++;


                }

               if (queueList.Count == 0)
               {
                   _isRunning = false;

                   if (LoadComplete != null)
                       //Dispatch(LoadComplete, new LoaderEvtArgs(wwwList));
                        LoadComplete(this, new LoaderEvtArgs>(wwwList));

                    yield break; 
               }


                yield return new WaitForSeconds(0.5f);
            }
        }


        private void EndAsyncEvent(IAsyncResult iar)
        {
            var ar = (System.Runtime.Remoting.Messaging.AsyncResult)iar;
            var invokedMethod = (EventHandler>)ar.AsyncDelegate;

            try
            {
                invokedMethod.EndInvoke(iar);
            }
            catch
            {
                // Handle any exceptions that were thrown by the invoked method
                Console.WriteLine("An event listener went kaboom!");
            }
        }


        protected void Dispatch(Delegate del,LoaderEvtArgs args)
        {
            Delegate[] delegates = del.GetInvocationList();
            foreach (Delegate d in delegates)
                ((EventHandler>)d).BeginInvoke(this, args, EndAsyncEvent, null);
        }
 
public void  Dispose()
{
    if(_queueList!=null) _queueList.Clear();
    if(_wwwList!=null) _wwwList.Clear();

  
}
}
}


If you use BeginInvoke so you not block main thread while handling instead Invoke, callback would be executed on the another thread so you need to use some sync mechanism with Unity main thread as WWW can't be used outside. You could use QUEUE of Action in main thread, fill in Invoking thread and execute in main thread again.
   void onLoadComplete(object sender, LoaderEvtArgs> args)
        {
           

            if (System.Threading.Thread.CurrentThread.ManagedThreadId != 1) return;

// Debug.Log(((List)args.data).ElementAt(0).text);
       
        }

        void onLoadItemComplete(object sender, LoaderEvtArgs args)
        {
           // Debug.Log(args.data.text);
 // Debug.Log(args.data.texture);
        }


        void onLoadError(object sender, LoaderEvtArgs args)
        {
             Debug.Log(args.data);
        }

No comments:

Post a Comment