Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
2
Question by Fattie · Mar 13, 2014 at 11:45 AM · parse

Loom - initialise per app, per scene, or ?

Normally I "initialise" Loom thus:

 Loom.Current.GetComponent<Loom>(); // irrelevant call to kickoff Loom.

I then use it thus ... {by the way, this is an interesting example, actually you can't use Parse.com unless you use Loom. Parse-Unity, which is pretty new, has a blockbuster bug or flaw where it goes to the wrong thread!}

 public void _NaiveLoginLoomStyle()
     {
     ParseUser.LogInAsync("fattie", "1234").ContinueWith(t =>
         {
         // ( point 'P' - see below )
         
         if (t.IsFaulted || t.IsCanceled)
             { .. password wrong.. }
         else
             {
             // wrong thread, can't do it .... GameObject.Find("cube");
             
             Loom.QueueOnMainThread(()=>
                 {
                 Debug.Log("Back on the main thread! Win an iPad! :)");
                 GameObject cubey = GameObject.Find("cube");
                 cubey.transform.Translate(0,-2,0, Space.World);
                 });
             
             // handy note, you can also write
             // Loom.QueueOnMainThread(SomeFunctionName);
             }
         });
     return;
     }

This is all fine

BUT.............................

I've just realised, I assumed one could do this

 Loom.Current.GetComponent<Loom>(); // irrelevant call to kickoff Loom.

once, at application launch time .. but that does not work (unless I screwed-up something else).

in fact, when do you have to "initialise" Loom? Is it at "point P" (see code above). Or once, for the class above? Or once per scene?

Or indeed, should you just do it each time before the QueueOnMainThread() call, or?

Cheers!

Comment
Add comment · Show 2
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Huacanacha · May 22, 2014 at 10:35 PM 1
Share

As we both seem to have discovered (or maybe it's changed on the Parse side?) you can actually use Coroutines to handle Parse async API calls without worrying about threads.

For anyone interested in how to do this see Fatties answer here: http://answers.unity3d.com/questions/701013/whats-the-difference-between-facebook-sdk-unity-an.html

avatar image Fattie · May 24, 2014 at 06:42 AM 0
Share

Right, you're 1000% correct, Hua. After we worked on few more projects, I'd say the "only way to go" is to use the "yield..." form.

It's kind of insane on the Parse front that (a) they haven't fixed that bug yet and (b) the doco still, largely, explains it as if the other way works.

BTW on that link to HWQLearner's question ... you've got to love anyone who calls me "$$anonymous$$r. Fattie" :)

2 Replies

· Add your reply
  • Sort: 
avatar image
6
Best Answer

Answer by whydoidoit · Mar 13, 2014 at 11:56 AM

So it "depends" if you ask me. I put a Loom initialization in each scene to stop there being any extraneous calls that get processed from the previous scene. If you DontDestroyOnLoad the component it's attached to (and you actually bother to attach it yourself to a scene component, so you are fully in control) then you can have it run once for the entire application - bearing in mind that it's possible queued actions will attempt to access things that no longer exist.

However, you might want to use this version which is improved to enable clearing of actions between scenes anyway and better memory usage.

 using UnityEngine;
 using System.Collections.Generic;
 using System;
 using System.Threading;
 using WhyDoIDoIt.LinqLite;
 using WhyDoIDoIt;
 
 
 public class Loom : MonoBehaviour
 {
     void OnApplicationQuit()
     {
         _quitting = true;
     }
     static bool _quitting;
     private static Loom _current;
     private int _count;
     /// <summary>
     /// Return the current instance
     /// </summary>
     /// <value>
     /// 
     /// </value>
     public static Loom Current
     {
         get
         {
             Initialize();
             return _current;
         }
     }
 
     static bool _initialized;
     static int _threadId;
 
     void Awake()
     {
         //gameObject.hideFlags = HideFlags.HideAndDontSave;
     }
 
     public static void Initialize()
     {
         if (!Application.isPlaying || _quitting)
             return;
         var go = !_initialized;
         if (!go && _threadId == Thread.CurrentThread.ManagedThreadId && _current == null)
             go = true;
 
         if (go)
         {
             foreach (var loom in Resources.FindObjectsOfTypeAll(typeof(Loom)).Cast<Loom>())
                 DestroyImmediate(loom.gameObject);
             var g = new GameObject("Loom");
             //g.hideFlags = HideFlags.DontSave;
             //DontDestroyOnLoad(g);
             _current = g.AddComponent<Loom>();
             _initialized = true;
             _threadId = Thread.CurrentThread.ManagedThreadId;
         }
 
     }
 
 
 
     void OnDestroy()
     {
         _actions.Clear();
         _delayed.Clear();
         if (_current == this)
         {
             _initialized = false;
         }
     }
 
     private readonly List<Action> _actions = new List<Action>();
     public class DelayedQueueItem
     {
         public float time;
         public Action action;
         public string name;
     }
     private readonly List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
 
     public static void QueueOnMainThread(Action action, string name)
     {
         QueueOnMainThread(action, 0, name);
     }
 
     public static void QueueOnMainThread(Action action, float time, string name)
     {
         if (!Application.isPlaying)
             return;
         if (Math.Abs(time - 0) > float.Epsilon || !string.IsNullOrEmpty(name))
         {
             lock (Current._delayed)
             {
                 DelayedQueueItem existing = null;
                 if (!string.IsNullOrEmpty(name))
                     existing = Current._delayed.FirstOrDefault(d => d.name == name);
                 if (existing != null)
                 {
                     existing.time = Time.time + time;
                     return;
                 }
                 var queueItem = Recycler.New<DelayedQueueItem>(-1);
                 queueItem.name = name;
                 queueItem.time = Time.time + time;
                 queueItem.action = action;
                 Current._delayed.Add(queueItem);
             }
         }
         else
         {
             lock (Current._actions)
             {
                 Current._actions.Add(action);
             }
         }
 
     }
 
     /// <summary>
     /// Queues an action on the main thread
     /// </summary>
     /// <param name='action'>
     /// The action to execute
     /// </param>
     public static void QueueOnMainThread(Action action)
     {
         QueueOnMainThread(action, 0f);
     }
     /// <summary>
     /// Queues an action on the main thread after a delay
     /// </summary>
     /// <param name='action'>
     /// The action to run
     /// </param>
     /// <param name='time'>
     /// The amount of time to delay
     /// </param>
     public static void QueueOnMainThread(Action action, float time)
     {
         QueueOnMainThread(action, time, null);
     }
 
     /// <summary>
     /// Runs an action on another thread
     /// </summary>
     /// <param name='action'>
     /// The action to execute on another thread
     /// </param>
     public static void RunAsync(Action action)
     {
         var t = new Thread(RunAction)
         {
             Priority = System.Threading.ThreadPriority.Normal
         };
         t.Start(action);
     }
 
     private static void RunAction(object action)
     {
         ((Action)action)();
     }
 
 
     // Use this for initialization
     void Start()
     {
 
     }
 
     readonly Action[] _toRun = new Action[4000];
 
 
     // Update is called once per frame
     void Update()
     {
         if (Current != this)
         {
             if (Application.isPlaying)
                 DestroyImmediate(gameObject);
             return;
         }
         if (!Application.isPlaying)
         {
             _actions.Clear();
             _delayed.Clear();
             return;
         }
         var count = Mathf.Min(_actions.Count, 4000);
         lock (_actions)
         {
             _actions.CopyTo(0, _toRun, 0, count);
             if (count == _actions.Count)
                 _actions.Clear();
             else
                 _actions.RemoveRange(0, count);
         }
         for (var i = 0; i < count; i++)
         {
             try
             {
                 _toRun[i]();
             }
             catch (Exception e)
             {
                 Debug.LogException(e);
             }
         }
 
         lock (_delayed)
         {
             count = 0;
             for (var i = _delayed.Count - 1; i >= 0 && count < 3999; i--)
             {
                 if (!(_delayed[i].time <= Time.time))
                 {
                     continue;
                 }
                 _toRun[count++] = _delayed[i].action;
                 _delayed.RemoveAt(i);
             }
         }
 
         for (var i = 0; i < count; i++)
         {
             try
             {
                 _toRun[i]();
             }
             catch (Exception e)
             {
                 Debug.LogException(e);
             }
         }
 
 
     }
 
 
 
     void OnLevelWasLoaded()
     {
         _actions.Clear();
         _delayed.Clear();
     }
 }
Comment
Add comment · Show 4 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Fattie · Mar 13, 2014 at 12:19 PM 0
Share

i've gotta feeling you have to "initialise" on component X, to use it on, component X. You can't initialise "once for the whole app" on component A (and A is persistent), and then use on component X. I've got a feeling you have to "initialise" on component X, to use on component X !

avatar image whydoidoit · Mar 13, 2014 at 12:37 PM 0
Share

Shouldn't have to - the only thing initializing does is ensure that there's a component to receive the delayed actions and thread start requests.

avatar image Fattie · Mar 13, 2014 at 12:49 PM 0
Share

Got it. In that case I'll look in to the specific app further and see if there's a reason I couldn't simply " 'initiailse' once in the preLoad scene" and if I figure it out I'll report back to you as a curiosity!! CHEERS

avatar image whydoidoit · Mar 13, 2014 at 12:50 PM 0
Share

$$anonymous$$aybe an execution order thing? $$anonymous$$y Loom runs pretty early in the execution order.

avatar image
5

Answer by Huacanacha · May 15, 2014 at 12:06 AM

Update: if you just need to handle Parse async API calls you can simply use coroutines... see here for an example.


For those stumbling on this thread while tearing their hair out trying to get the Parse.com backend service API working in Unity... here's my solution! It’s a cut down and re-architected version of Loom purely for dispatching message to the main thread. It will auto-initialise per scene just by adding it to an object in the scene. In theory you can use DontDestroyOnLoad to make it persist between scenes (see discussion above). The original Loom class from UnityGems.com had been giving me get_isPlaying errors when calling QueueOnMainThread.

For me this makes the Parse.com backend API usable within Unity. Just call this within the ContinueWith closure after an API call like FindAsync() ;)

Note: I’m sure there are cases I haven’t thought of here and its not thoroughly tested, so use with caution.

My thanks to whydoidoit (I believe?) for the original!


=== Usage ===

 SceneLoom.Loom.QueueOnMainThread(() => {
     Debug.Log(“Hello main thread!”);
     // Your main thread code here
 });

=== SceneLoom.cs ===

 using UnityEngine;
 using System.Collections.Generic;
 using Action=System.Action;
 
 public class SceneLoom : MonoBehaviour
 {
     public interface ILoom {
         void QueueOnMainThread(Action action);
     }
 
     private static NullLoom _nullLoom = new NullLoom();
     private static LoomDispatcher _loom;
     public static ILoom Loom {
         get {
             if (_loom != null) {
                 return _loom as ILoom;
             }
             return _nullLoom as ILoom;
         }
     }
 
     void Awake() {
         _loom = new LoomDispatcher();
     }
     void OnDestroy() {
         _loom = null;
     }
 
     void Update() {
         if (Application.isPlaying) {
             _loom.Update();
         }
     }
 
     private class NullLoom : ILoom {
         public void QueueOnMainThread(Action action) {}
     }

     private class LoomDispatcher : ILoom {
         private readonly List<Action> queuedActions = new List<Action>();
         private readonly List<Action> actionsToRun = new List<Action>();

         public void QueueOnMainThread(Action action) {
             lock (queuedActions) {
                 queuedActions.Add(action);
             }
         }

         public void Update() {
             // Pop the actions from the synchronized list
             actionsToRun.Clear();
             lock (queuedActions) {
                 actionsToRun.AddRange(queuedActions);
                 queuedActions.Clear();
             }

             // Run each action
             for (int i = 0; i < actionsToRun.Count; ++i) {
                 actionsToRun[i]();
             }
         }
     }
 
 }


Comment
Add comment · Show 3 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image leegod · Nov 18, 2015 at 04:11 AM 0
Share

Thanks man! I met same thread problem and above Loom solution is insufficient because there is no more Loom and whydoidoit library.

avatar image Huacanacha leegod · Nov 18, 2015 at 08:06 PM 0
Share

@leggod You are welcome. Do note the update re using Coroutines with Parse.com API... if thats what you are doing that keeps things simpler and more in the Unity style. Loom and SceneLoom are definitely useful where you need to do your own threaded dispatching.

avatar image Fattie · Nov 18, 2015 at 01:22 PM 1
Share

no problem but Loom by @whydoidoit is like the most famous library in all of Unity3D. it's true that the link does not work anymore!

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

23 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Getting text from a website file and storing it in a variable 1 Answer

equation game question need help 1 Answer

Converting a .CSV string array to a float array 1 Answer

Is Parse's SDK compatible with WebGL builds ? 1 Answer

Tag Manager unable to parse 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges