Transfer from Http://blog.csdn.net/huutu http://www.thisisgame.com.cn
Because of the lag in loading a large amount of resources in a project, it is intended to be handled asynchronously, but with a problem that is difficult to understand.
Problem Description:
In an asynchronous function, the OnClick setting of the 9 buttons on the interface sets an anonymous function that uses log to print the Index of the current button. The code looks fine, but the test found
Clicking on all the buttons outputs 8, which means that although I re-created an int value in the code and assigned the value of index, it didn't actually take effect at all.
As follows: transfer from Http://blog.csdn.net/huutu http://www.thisisgame.com.cn
Transfer from Http://blog.csdn.net/huutu http://www.thisisgame.com.cn
Code:
Using unityengine;using system.collections;using system.collections.generic;using Unityengine.ui;public class Newbehaviourscript:monobehaviour {[serializefield]list<gameobject> m_heroobjlist;//Use Initializationvoid Start () {Startcoroutine (Testasync ()));D Ebug. Log ("End");} IEnumerator Testasync () {Debug.Log ("Testasync"); for (int index=0;index<9;index++) {Button Btn=m_heroobjlist[index ]. Getcomponent<button> (); int a=index;//from the result, every time you create a a there is no effect Btn.onClick.AddListener (Delegate {showbtn (a);});} Debug.Log ("Testasync end"); yield break;} void showbtn (int index) {Debug.Log (index). ToString ());}}
As a result, whichever button is clicked, it is output 8.
Transfer from Http://blog.csdn.net/huutu http://www.thisisgame.com.cn
Can not think of their async code where there is a problem, I each time I created a local variable A in C # to become a member variable, and then switch to the synchronous code test.
void Testsync () {Debug.Log ("Testsync"); for (int index=0;index<9;index++) {Button Btn=m_heroobjlist[index]. Getcomponent<button> (); int A=index;btn.onclick.addlistener (Delegate {showbtn (a);});} Debug.Log ("Testsync End");}
Test finds synchronization code everything is OK, each button outputs the correct index.
Transfer from Http://blog.csdn.net/huutu http://www.thisisgame.com.cn
I thought for two days without fruit, so I ask you colleagues of the great God. A colleague gives a reply, this is the unity bug, async cannot write anonymous functions like I do, it is recommended to use delegate to modify the replacement.
Instead, use delegate to replace the anonymous function to resolve the bug.
First add a class Eventtriggerlistener to listen for click events in Unity and then callback.
Using unityengine;using system.collections;using unityengine.eventsystems;public class Eventtriggerlistener: UnityEngine.EventSystems.EventTrigger {public delegate void Voiddelegate (gameobject go,object data);p ublic Voiddelegate onclick;private Object M_param=null;public static Eventtriggerlistener Get (Gameobject go) { Eventtriggerlistener Listener=go. Getcomponent<eventtriggerlistener> (); if (listener==null) {Listener=go. Addcomponent<eventtriggerlistener> ();} return listener;} public void SetParam (object data) {M_param=data;} public override void Onpointerclick (Pointereventdata eventData) {base. Onpointerclick (EventData); if (onclick!=null) {OnClick (Gameobject,m_param);}}}
The code then modifies the following:
IEnumerator testasyncusedelegate () {Debug.Log ("testasyncusedelegate"); for (int index=0;index<9;index++) {button Btn=m_heroobjlist[index]. Getcomponent<button> (); Eventtriggerlistener Listener=eventtriggerlistener.get (btn.gameobject); listener. SetParam (index); listener.onclick+=clickbtn;} Debug.Log ("Testasyncusedelegate end"); yield break;} void Clickbtn (Gameobject go,object data) {showbtn (int) data); void showbtn (int index) {Debug.Log (index). ToString ());}
Test resultstransfer from Http://blog.csdn.net/huutu http://www.thisisgame.com.cn
Then the problem is solved.
But for the cause of this problem is very tangled, so again to consult another great God colleague, the Great God replied to me, is because the implementation of mono and donet the implementation of inconsistent results, you can go to see the anti-compiled IL code.
Then I went to see the anti-compiled IL code.
The anti-compilation process is described in the previous article:
Unity3d anti-compilation hack game Simple example (use ILDASM to decompile DLL modifications and recompile DLLs)
Transfer from Http://blog.csdn.net/huutu http://www.thisisgame.com.cn
First, learn about closures in C #
http://blog.csdn.net/huutu/article/details/46576279
knowledge Point advance:
For variables in anonymous functions, C # Creates a class for these variables when it is being translated into IL code.
Such as
Transfer from Http://blog.csdn.net/huutu http://www.thisisgame.com.cn
We see that a in the sync function creates a class <testsync>c_anonstorey2
A in async, eh? A class was not created for a, but a class was created for the iterator ' <testasync>c__iterator1
First, let's look at the IL code that the synchronization code compiles:
. method private Hidebysig instance void Testsync () CIL managed{//code size 101 (0X65)//define the maximum depth of the stack used by the function code and can also be understood as the call St The number of arguments to ACK. maxstack 12//Below we think of it as the initialization//definition of the int type parameter V_0,class type (Button) V_1,class type in the completion code (<testsync>c__anonstorey 1) v_2//(at this point the v_0,v_1,v_2 has been deposited in the call stack). Locals init (int32 v_0, class [Unityengine.ui]unityengine.ui.button V_1, Class newbehaviourscript/' <testsync>c__anonstorey1 ' v_2) il_0000://Pushes new pairs of strings ("Testsync") stored in metadata Like a reference. ldstr "Testsync" il_0005://Invokes the method indicated by the method specifier passed (Debug::log (object)). call void [Unityengine]unityengine.debug::log (object) il_000a://Pushes the integer value 0 as int32 onto the evaluation stack. ldc.i4.0 il_000b://POPs the current value (index=0) from the top of the evaluation stack and stores it in the local variable list at index 0. stloc.0 il_000c://Unconditionally transfer control to the target instruction (il_0052). BR il_0052 il_0011://Instantiate AnonStorey1, which is the class that the variable a closure produces newobj instance void newbehaviourscript/' <TESTSYNC&G T;c__anonstorey1 '::. ctor () il_0016://POPs the current value (Button) from the top of the evaluation stack and stores it in the local variable list at index 2. Stloc.2 il_0017://Loads the local variable at the specified index (2) onto the evaluation stack. LDLOC.2 il_0018://The parameter with index 0 is loaded onto the evaluation stack. ldarg.0 il_0019://New value replaces the value in the field of the object reference or pointer stfld class Newbehaviourscript newbehaviourscript/' <testsync>c__anon Storey1 ':: ' <>f__this ' il_001e://The parameter with index 0 is loaded onto the evaluation stack. ldarg.0 il_001f://Find the value of the field in the object whose reference is currently on the evaluation stack. ldfld class [mscorlib]system.collections.generic.list ' 1<class [unityengine]unityengine.gameobject> Newbehaviourscript::m_heroobjlist il_0024://The parameter with index 0 is loaded onto the evaluation stack. ldloc.0 il_0025://Invokes a late-bound method on an object and pushes the return value onto the evaluation stack. callvirt instance!0 class [Mscorlib]system.collections.generic.list ' 1<class [unityengine]unityengine.gameobject >::get_item (Int32) il_002a://Invokes a late-bound method on an object and pushes the return value onto the evaluation stack. callvirt instance!! 0 [Unityengine]unityengine.gameobject::getcomponent<class [unityengine.ui]unityengine.ui.button> () IL_002f: POPs the current value (Button) from the top of the evaluation stack and stores it in the list of local variables at index 1. STLOC.1 il_0030://The parameter with index 2 is loaded onto the evaluation stack. LDLOC.2 il_0031://index is 0 (Index) is loaded onto the evaluation stack. ldloc.0 il_0032://New Value (index) Replace the value in the field of the object reference or pointer index to replace a stfld int32 newbehaviourscript/' <testsync>c__anonsto Rey1 ':: a il_0037://The parameter with index 1 is loaded onto the evaluation stack. LDLOC.1 il_0038://Invokes a late-bound method on an object and pushes the return value onto the evaluation stack. callvirt instance class [Unityengine.ui]unityengine.ui.button/buttonclickedevent [Unityengine.ui] Unityengine.ui.button::get_onclick () il_003d://Loads the parameter with index 2 onto the evaluation stack. LDLOC.2 il_003e://pushes an unmanaged pointer (native int type) that points to native code that implements a particular method onto the evaluation stack. ldftn instance void newbehaviourscript/' <testsync>c__anonstorey1 ':: ' <>m__0 ' () il_0044://Create a new object of value type or New instance and pushes an object reference (type O) onto the evaluation stack. newobj instance void [Unityengine]unityengine.events.unityaction::.ctor (object,native int) il_0049://Call late-bound method on object, and pushes the return value onto the evaluation stack. callvirt instance void [Unityengine]unityengine.events.unityevent::addlistener (class [Unityengine] UnityEngine.Events.UnityAction) il_004e://Loads the local variable at the specified index onto the evaluation stack. ldloc.0 il_004f://Pushes the integer value 1 as int32 onto the evaluation stack. ldc.i4.1 IL_0050://Adds two values and pushes the result onto the evaluation stack. Add il_0051://POPs the current value from the top of the evaluation stack and stores it in the local variable list at index 0. stloc.0 il_0052://Loads the local variable at the specified index 0 onto the evaluation stack. ldloc.0 il_0053://Pushes the provided int8 value (9) as int32 onto the evaluation stack (short form). LDC.I4.S 9 il_0055://If the first value is less than the second value (9), the control is transferred to the target instruction. (il_0011) BLT il_0011 il_005a://Pushes a new object reference to the string stored in the metadata ("Testsync end"). ldstr "Testsync End" il_005f://Call the method indicated by the method specifier passed (Debug::log (object)). call void [Unityengine]unityengine.debug::log (object) il_0064://returns from the current method and pushes the return value (if present) from the caller's evaluation stack onto the callee's evaluation stack. RET}//End of method Newbehaviourscript::testsync
The main idea is to compare index with 9, if less than 9 jumps to the beginning to continue for the For loop, each time the loop instantiates a <testsync>c_anonstorey2 (int a = index), so in the synchronization code, a is the correct value.
Transfer from Http://blog.csdn.net/huutu http://www.thisisgame.com.cn
The asynchronous code deserializes the Il file as follows:
Testasync:class[mscorlib]system.collections.ienumerator ()
. method private Hidebysig instance class [Mscorlib]system.collections.ienumerator Testasync () CIL managed{ . custom instance void [Mscorlib]system.diagnostics.debuggerhiddenattribute::.ctor () = (in the case of XX) //code size 15 (0 XF) . maxstack 2 . Locals init (class newbehaviourscript/' <testasync>c__iterator1 ' v_0) Il_ 0000: newobj instance void newbehaviourscript/' <testasync>c__iterator1 '::. ctor () il_0005: stloc.0 il_0006: ldloc.0 il_0007: ldarg.0 il_0008: stfld class Newbehaviourscript newbehaviourscript/' <testasync>c__iterator1 ':: ' <>f__this ' il_000d: ldloc.0 IL_000e: ret}//End of method Newbehaviourscript::testasync
See that the function has been instantiated to create a class for iterators ' <testasync>c__iterator1
Then ' the structure of the <testasync>c__iterator1 is as follows
Main view Movenext:bool () code
turn from Http://blog.csdn.net/huutu http://www.thisisgame.com.cn
. method public hidebysig newslot virtual final instance bool MoveNext () CIL managed{//code size 157 (0x9d). Maxstack il_0000:ldarg.0 il_0001:ldfld int32 newbehaviourscript/' <testasync>c__iterator1 ':: $PC IL_00 06:ldarg.0 IL_0007:ldc.i4.m1 il_0008:stfld int32 newbehaviourscript/' <testasync>c__iterator1 ':: $PC il_ 000d:brtrue il_009b il_0012:ldstr "Testasync" Il_0017:call void [Unityengine]unityengine.debug::log (object) il_001c:ldarg.0 il_001d:ldc.i4.0 il_001e:stfld int32 newbehaviourscript/' <testasync>c__iterator1 ':: ' < ; Index>__0 ' il_0023:br il_007f il_0028:ldarg.0 il_0029:ldarg.0 il_002a:ldfld class Newbehaviou Rscript newbehaviourscript/' <testasync>c__iterator1 ':: ' <>f__this ' IL_002F:LDFLD class [Mscorlib]System . Collections.Generic.List ' 1<class [unityengine]unityengine.gameobject> newbehaviourscript::m_heroobjlist IL_ 0034:ldarg.0 il_0035:ldfld int32 newbehaviourscript/' <testasync>c__iterator1 ':: ' <index>__0 ' Il_003a:callvirt instanc E!0 class [mscorlib]system.collections.generic.list ' 1<class [Unityengine]unityengine.gameobject>::get_item ( Int32) Il_003f:callvirt instance!! 0 [Unityengine]unityengine.gameobject::getcomponent<class [unityengine.ui]unityengine.ui.button> () IL_0044: Stfld class [Unityengine.ui]unityengine.ui.button newbehaviourscript/' <testasync>c__iterator1 ':: ' <btn >__1 ' il_0049:ldarg.0 il_004a:ldarg.0 il_004b:ldfld int32 newbehaviourscript/' <testasync>c__iterato R1 ':: ' <index>__0 ' il_0050:stfld int32 newbehaviourscript/' <testasync>c__iterator1 ':: ' <a>__2 ' IL _0055:ldarg.0 IL_0056:LDFLD class [Unityengine.ui]unityengine.ui.button newbehaviourscript/' <TestAsync>c__ Iterator1 ':: ' <btn>__1 ' Il_005b:callvirt instance class [unityengine.ui]unityengine.ui.button/ buttonclickedevent [UniTyengine.ui]unityengine.ui.button::get_onclick () il_0060:ldarg.0 il_0061:ldftn instance void Newbehaviourscript /' <testasync>c__iterator1 ':: ' <>m__1 ' () il_0067:newobj instance void [unityengine]unityengine.events.un Ityaction::.ctor (object, native int ) Il_006c:callvirt instance void [Unityengine]unityengine.events.unityevent::addlistener (class [Unityengine] UnityEngine.Events.UnityAction) il_0071:ldarg.0 il_0072:ldarg.0 il_0073:ldfld int32 newbehaviourscript/' < Testasync>c__iterator1 ':: ' <index>__0 ' il_0078:ldc.i4.1 il_0079:add il_007a:stfld int32 NewBehaviour script/' <testasync>c__iterator1 ':: ' <index>__0 ' il_007f:ldarg.0 il_0080:ldfld int32 NEWBEHAVIOURSCRI pt/' <testasync>c__iterator1 ':: ' <index>__0 ' il_0085:ldc.i4.s 9 il_0087:blt IL_0028 il_008c:ld Str "Testasync End" il_0091: call void [Unityengine]unityengine.debug::log (object) il_0096:br il_009b il_009b:ldc.i4.0 il_009c: RET}//End of method ' <testasync>c__iterator1 ':: MoveNext
In this code, a is actually a member variable of the generated class, and MoveNext has been re-assigned to a, not multiple times, so the value of a is modified several times and last modified to 8.
Transfer from Http://blog.csdn.net/huutu http://www.thisisgame.com.cn
This solves the problem.
Turn from Http://blog. transfer from Http://blog.csdn.net/huutu http://www.thisisgame.com.cnCsdn.net/huutu http://www.thisisgame.com.cn
About the anti-compile DLL method:
http://blog.csdn.net/huutu/article/details/46573327
About C # closures
http://blog.csdn.net/huutu/article/details/46576279
Introduction to ILDASM usage and IL syntax
http://blog.csdn.net/huutu/article/details/46573417
About more IL syntax command queries
http://blog.csdn.net/huutu/article/details/46573435
Sample Project Download:
Http://pan.baidu.com/s/1sjLsjrn
http://download.csdn.net/detail/cp790621656/8825391
transfer from Http://blog.csdn.net/huutu http://www.thisisgame.com.cn
Mono2.0 's different implementations of C # closures and donet lead to unity Bugs and solutions