воскресенье, 9 апреля 2017 г.

Top100 Unity errors

In my programming practice, I encountered many code construction errors. Most worse are hidden. All works, but bugfixing and extending functionality is a nightmare. I will write some which are funny or puzzling in my blog.

1. Created object shows "null" in debugger.

Suppose code:
public class LocationsDB : MonoBehaviour { ... }

locationsDB = new LocationsDB();
Why do you think, debugger can say us, that locationsDB is "null" ?

суббота, 1 апреля 2017 г.

Exception stacktraces in Unity: why do they lose so many frames ?

Sometimes, I got weird stacktraces while logging them with Debug.LogException. I investigated further and found out what's happenning. Just note it here.

Suppose, we have the following code in Unity:
class SomeCrashingScript : MonoBehaviour
{
    // on click listener set from Unity
    public void Crash1()
    {
        try
        {
            A();
        }
        catch (IndexOutOfRangeException exception)
        {
            string catchStackTrace = new StackTrace(0, true).ToString();
            TestLogger.Trace("catchStackTrace:\n" + catchStackTrace);
            TestLogger.Trace("exception.StackTrace:\n" + exception.StackTrace);
            Debug.LogException(exception); // will see, what Unity logs with that call
        }
        A(); // will see what unity logs itself
    }

    private void A()
    {
        B();
    }

    private void B()
    {
        int[] elements = new int[5];
        int b = elements[2];
        int c = b + elements[6];
        int d = c + elements[1];
    }
}
exception.StackTrace is:
   at SomeCrashingScript.B () [0x0000c] in C:\\playroom\\UnityProjects\\PlayUnity\\StackTraceTest\\Assets\\SomeCrashingScript.cs:34
   at SomeCrashingScript.A () [0x00002] in C:\\playroom\\UnityProjects\\PlayUnity\\StackTraceTest\\Assets\\SomeCrashingScript.cs:26
   at SomeCrashingScript.Crash1 () [0x00003] in C:\\playroom\\UnityProjects\\PlayUnity\\StackTraceTest\\Assets\\SomeCrashingScript.cs:15
catchStackTrace is:
   at SomeCrashingScript.Crash1() in C:\playroom\UnityProjects\PlayUnity\StackTraceTest\Assets\SomeCrashingScript.cs:line 19
   at UnityEngine.Events.InvokableCall.Invoke(System.Object[] args) in C:\buildslave\unity\build\Runtime\Export\UnityEvent.cs:line 153
   at UnityEngine.Events.InvokableCallList.Invoke(System.Object[] parameters) in C:\buildslave\unity\build\Runtime\Export\UnityEvent.cs:line 634
   at UnityEngine.Events.UnityEventBase.Invoke(System.Object[] parameters) in C:\buildslave\unity\build\Runtime\Export\UnityEvent.cs:line 769
   at UnityEngine.Events.UnityEvent.Invoke() in C:\buildslave\unity\build\Runtime\Export\UnityEvent_0.cs:line 53
   at UnityEngine.UI.Button.Press() in C:\buildslave\unity\build\Extensions\guisystem\UnityEngine.UI\UI\Core\Button.cs:line 35
   at UnityEngine.UI.Button.OnPointerClick(UnityEngine.EventSystems.PointerEventData eventData) in C:\buildslave\unity\build\Extensions\guisystem\UnityEngine.UI\UI\Core\Button.cs:line 44
   at UnityEngine.EventSystems.ExecuteEvents.Execute(IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) in C:\buildslave\unity\build\Extensions\guisystem\UnityEngine.UI\EventSystem\ExecuteEvents.cs:line 52
   at UnityEngine.EventSystems.ExecuteEvents.Execute(UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.EventFunction`1 functor) in C:\buildslave\unity\build\Extensions\guisystem\UnityEngine.UI\EventSystem\ExecuteEvents.cs:line 269
   at UnityEngine.EventSystems.StandaloneInputModule.ProcessMousePress(UnityEngine.EventSystems.MouseButtonEventData data) in C:\buildslave\unity\build\Extensions\guisystem\UnityEngine.UI\EventSystem\InputModules\StandaloneInputModule.cs:line 531
   at UnityEngine.EventSystems.StandaloneInputModule.ProcessMouseEvent(Int32 id) in C:\buildslave\unity\build\Extensions\guisystem\UnityEngine.UI\EventSystem\InputModules\StandaloneInputModule.cs:line 430
   at UnityEngine.EventSystems.StandaloneInputModule.ProcessMouseEvent() in C:\buildslave\unity\build\Extensions\guisystem\UnityEngine.UI\EventSystem\InputModules\StandaloneInputModule.cs:line 410
   at UnityEngine.EventSystems.StandaloneInputModule.Process() in C:\buildslave\unity\build\Extensions\guisystem\UnityEngine.UI\EventSystem\InputModules\StandaloneInputModule.cs:line 184
   at UnityEngine.EventSystems.EventSystem.Update() in C:\buildslave\unity\build\Extensions\guisystem\UnityEngine.UI\EventSystem\EventSystem.cs:line 287
As you see, catchStackTrace is just the full stack trace of Crash1() invocation. Methods in it wount change, if we collect that stack not from catch, but from first line of that method. And the other stack, exception.StackTrace, as you see, contains sequence of calls: Crash1(), A(), B(). It doesn't contain calls that lead to Crash1() call, so that stack is RELATIVE. So, sum of exception.StackTrace and catchStackTrace will give us full stacktrace of exception. What do we see in Unity, when we log exception with Debug.LogException(exception):
SomeCrashingScript.B () (at Assets/SomeCrashingScript.cs:38)
SomeCrashingScript.A () (at Assets/SomeCrashingScript.cs:30)
SomeCrashingScript.Crash1 () (at Assets/SomeCrashingScript.cs:16)
UnityEngine.Debug:LogException(Exception)
SomeCrashingScript:Crash1() (at Assets/SomeCrashingScript.cs:23)
UnityEngine.EventSystems.EventSystem:Update()
As you see, stacktrace contains exception.StackTrace and some stacktrace, different from catchStackTrace:
UnityEngine.Debug:LogException(Exception)
SomeCrashingScript:Crash1() (at Assets/SomeCrashingScript.cs:23)
UnityEngine.EventSystems.EventSystem:Update()
The question is, why do we lost so many frames (Button.OnPointerClick(), Button.Press()...) and see only EventSystem:Update() ? Let's look what Unity logs itself when A() is called without try-catch:
SomeCrashingScript.B () (at Assets/SomeCrashingScript.cs:38)
SomeCrashingScript.A () (at Assets/SomeCrashingScript.cs:30)
SomeCrashingScript.Crash1 () (at Assets/SomeCrashingScript.cs:25)
UnityEngine.Events.InvokableCall.Invoke (System.Object[] args) (at C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:153)
UnityEngine.Events.InvokableCallList.Invoke (System.Object[] parameters) (at C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:634)
UnityEngine.Events.UnityEventBase.Invoke (System.Object[] parameters) (at C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:769)
UnityEngine.Events.UnityEvent.Invoke () (at C:/buildslave/unity/build/Runtime/Export/UnityEvent_0.cs:53)
UnityEngine.UI.Button.Press () (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Button.cs:35)
UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Button.cs:44)
UnityEngine.EventSystems.ExecuteEvents.Execute (IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:52)
UnityEngine.EventSystems.ExecuteEvents.Execute[IPointerClickHandler] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.EventFunction`1 functor) (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:269)
UnityEngine.EventSystems.EventSystem:Update()
As you see, stack is longer and almost same as exception.StackTrace + catchStackTrace. So, why Debug.LogException misses so many frames, why Unity misses some frames (4 frames missing, all from StandaloneInputModule) and what do we, developers, need to use when logging exceptions manually ??? Note: both Application.logMessageReceivedThreaded and Editor console affected

суббота, 21 января 2017 г.

Why I prefer longer class names even when namespace guarantees no name conflict

Suppose:

namespace Tutorial {
class CarnavalEventTutorial {...}
}


I keep Tutorial at end of class because 2 reasons:
a. you may keep some field with the same name, e.g.

public CarnavalEvent CarnavalEvent { get; private set; }

And you don't get name collision here. And no collision with class name too.

b. Suppose, that you can set that Tutorial as field of Unity component in editor. Unity editor wount show you namespaces, so you'll get 2 CarnavalEvent and can't diferentiate which one is Tutorial and which is not. More ugly example:

namespace Windows
{
    namespace Shop
    {
        public class Window : MonoBehaviour {}
    }
}

So, in Unity you'll see Window component and may decide that it's basic component. While it's not.

And one more bad thing, suppose you want to include that "Window" in code:


public class EventTracker {
private List<Window> windows;
private Windows.Shop.Window shopWindow; // you need that, again, to tell reader that it is ShopWindow. And to fix compiler error.
}

So, trying to make names short you make them longer instead :)

Minuses:
The price for that is longer names. But, again, that rule don't ask to include all the names from namespace. E.g.:

namespace Game {
namespace QuestParts {
namespace Tutorial {
class CarnavalEventGameQuestPartsTutorial {...} // not needed
}
}
}

Because, usually, you wount get CarnavalEventTutorial at different levels of namespaces.

So, the rule is: CONSIDER DUBLICATING NAME FROM ENCLOSING NAMESPACE IN CLASS NAME

вторник, 10 января 2017 г.

Collecting managed assert stack traces in release build of iOS Unity App

All below done with BuildOptions.ForceEnableAssertions, messages logged with NSLog or Console.WriteLine to be in release build (not just debug) Unity 5.5.0f3

Suppose client code:
Assert.IsFalse(true); // Unity's assert, not System

That stack we receive in Application.logMessageReceived callback:

1. UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchPress(PointerEventData, Boolean, Boolean)

That message we see in Xcode console (it is made by Unity automatically)

2. some message guys
Assertion failed. Value was True
Expected: False
UnityEngine.Events.InvokableCallList:Invoke(Object[])
UnityEngine.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1)
UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchPress(PointerEventData, Boolean, Boolean)

And that stack we get through new StackTrace(0, true) at the point of the call:

3. MyPanelBehaviour:OnClickA()
UnityEngine.Events.InvokableCallList:Invoke(Object[] parameters)
UnityEngine.EventSystems.ExecuteEvents:Execute(GameObject target, BaseEventData eventData, EventFunction`1 functor)
UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchPress(PointerEventData pointerEvent, Boolean pressed, Boolean released)

You see, that new StackTrace(0, true) at the point of the call is most useful because only it contains OnClickA()*

So, to collect managed stack traces successfully we need to collect stack manually with new StackTrace.

Note: string StackTraceUtility.ExtractStackTrace() may be useful too. It uses new StackTrace(...) inside but additionally formats it to readable string in Unity stack style

Note: I've encountered on one of earlier versions of Unity bug. If you set in iOS player settings Logging from ScriptOnly to Full, you'll get "StackTrace is not supported on this platform" in Application.logMessageReceived callback instead of stack and no automatic message in Xcode console.

Hint: You also can get stack with new StackTrace() in Application.logMessageReceived. It will contain caller code (not just code that collects and dispatches log). I've tested it. And also, in documentation, it is said, that Application.logMessageReceived is allways called on the main thread (vs Application.logMessageReceivedThreaded) and that is the place where usually assertion happens.

* Note that, on earlier version of Unity I got OnClickA() in 2 too

понедельник, 1 августа 2016 г.

Contact

Send e-mail to vnms11@gmail.com

IZEng - engine for game development

IZEng is an open-source game engine with 2D graphics onboard. It encapsulates DirectX work. It includes simple movement models: linear, angular. Supports acceleration, friction, gravity if needed. RockCarrier is one of my games, which is developed on the base of this engine. Documentation included, but not full.

If you have questions, contact: vnms11@gmail.com. Title the letter "IgriZdes Engine".

I developed this small engine when I was studying in the University, so don't be surprised.
Source code with documentation on github  Download

понедельник, 4 июля 2016 г.

Building and running Android Studio gradle project on android device

Recently I was just checking out if AIDE team added support to build gradle-based projects. So, indeed they added:

AIDE also supports basic Android Studio projects, which follow the default project structure. The full gradle build system is not yet supported though
(source: http://www.android-ide.com/tutorial_androidstudio.html)

So, if your gradle setup is simple, you can just open your android project in AIDE and run.

But what to do if not? As before we have a workarond: create eclipse project files (.classpath + .project) for our project. AIDE works with eclipse project structure quite well. But what to do with the dependencies? It's insane to write all of them by hand. Likely, LibGdx enthusiasts gives us script which generates those strings for us (see for example eclipse task here)

I'll give details on example of how to build & run LibGdx gradle project in AIDE.