16 Eylül 2016 Cuma

Debugging in Unity3D

Now it is time to Debug

It has been four posts so far.

1. Why do we move to Unity3D?
2. Power of Unity3D Editor
3. Coding in Unity3D
4. Testing in Unity3D

Another important topic is debugging, which is an essential point in software engineering. As a developer we read codes, even more than we write code. While reading the code, having good debugging skills and tools is quite important. It makes your life much easier. With Unity you have different kinds of debugging tools as well as very basic debugging approaches.


I have already write about Mono Develop, the default IDE of Unity. It has built-in debugger, which is integrated with Unity. So you can attach to Unity process inside the IDE, and put a breakpoint in the code base and run the editor. You will see that it will stop on the selected line, and you will have the usual actions like pause, continue, run step by step, step inside etc... And also you can see the usual information like stack trace, value of local and global variables etc...

It sounds like a proper debugger, right? 

Yes in theory, it is a proper debugger which has almost every expected feature from a regular debugger. 

But unfortunately in practice it is not like that. It is almost certain that it will crash either Unity or MonoDevelop in each one of two debug try. So it is literally useless, unless you want to live some kind of excitement and believe me it is not that pleasant to restart Unity (or MonoDevelop) in the middle of coding. 

So the old way is the best way. Plain old regular logging. That is it. Put Debug.Log() in every corner of the code base and learn how it is running. And find what you are looking for. Yes it is not that fancy but it is some how enough, and it is the only way of debugging your scripts. So I suggest you to get used to it.

Editor Debugging

In one of previous posts, I have mentioned about Unity Editor. Here it is another very handy feature. Unity will actually let you change the value of a script’s variables on inspector window while the game is running. This is very useful for seeing the effects of changes directly without having to stop and restart. When gameplay ends, the values of the variables will be reset to whatever they were before you pressed Play. This ensures that you are free to tweak your object’s settings without fear of doing any permanent damage.

When you pause the game you can see all your game objects' properties and states. It is a kind of debugger right, even it has step by step option :) actually it is a little different of course. Each step is not a new line in your code, each step is the next frame. That is why it is called "frame by frame" :)

You can see the scene and also all the state of the scene when you pause the game or when you go frame by frame. Actually there is even better option that you can change everything in the scene. Enable / Disable game objects, change their location or scale or color. What ever you change, you will see the change instantly. Then when you hit play again it will resume with this new states. Actually you can do this without pausing the game at all.

This is very powerful feature that you can leverage to debug the scene and game objects, and also you can use this feature to tweak some fine adjustment on the scene like 1 pixel left, 1 pixel right :)

To make it clear, let me give you an example from our games. As you already know we have a card game Gin Rummy Plus. According to rules of this card game you can finish the round when all your cards sorted for certain rules. And to finish the round you have to throw one last card to pile. And it should not be same as throwing a regular card, it should be more impressive and it should give the feeling that you won the round.

To be able to give that feeling we should have try different options. We have created three different bezier path. One for player's last card throw, one for opponent's last card throw and one for regular card throw. And instead of hard coding these bezier paths, we just use editor window to tweak and find the best way of these throw paths.

And one more tip is you can also pause the game programatically. Just type Debug.Break() in your code and stop the world according to a condition. And you don't need to worry about it later, because it will do nothing in production. It has one more significant advantage over manual pausing and stepping forward. It can pause your game in the middle of an execution frame. This means that part of your object may be still waiting for the Update() call and the other part has already been updated. This is really useful for situations when you’re dealing with a bug that appears randomly.

Frame Debugger

The Frame Debugger lets you freeze playback for a running game on a particular frame and view the individual draw calls that are used to render that frame. As well as listing the drawcalls, the debugger also lets you step through them one-by-one so you can see in great detail how the Scene is constructed from its graphical elements.

The Frame Debugger window shows the drawcall information and lets you control the “playback” of the frame under construction. The main list shows the sequence of drawcalls in the form of a hierarchy that identifies where they originated from. The panel to the right of the list gives further information about the drawcall such as the geometry details and the shader used for rendering.

Clicking on an item from the list will show the Scene (in the Game view) as it appears up to and including that drawcall. The left and right arrow buttons in the toolbar move forward and backward in the list by a single step and you can also use the arrow keys to the same effect. Additionally, the slider at the top of the window lets you “scrub” rapidly through the drawcalls to locate an item of interest quickly. Where a drawcall corresponds to the geometry of a GameObject, that object will be highlighted in the main Hierarchy panel to assist identification.

To reduce the drawcalls, frame debugger is the key tool that we are using in our games. As you can see in the screenshots above, we still have some work to do. Our game's lobby screen has 55 drawcalls which is not a problem mid and high end devices, but might be a problem for some low end devices. That is why our target is to reduce it to at least less then 50 drawcalls.

Debug on Device

In general you will not need to make a lot of debugging on devices. According to our experience with Unity, most of the time you will see actual device behaviour inside the editor. And most of the time it is not changing according to device type. But for some rare cases and also for some native calls you might need to debug on device.

Debugging on device is more difficult and primitive. In theory you can have breakpoints in the code especially on iOS environment, but you cannot see the actual code, because it will be converted to native binary files. That means it is not useful at all. So only option is to put logs in your code and tail this logs to see how your code is behaving. Adding logs is not different than the one that I have already mentioned. But to see that log while running on device is a little different.

To see the logs while playing game on device you are going to have different ways on iOS and Android.

First iOS. Before everything make sure that you are on a MacOS system, and Xcode has been successfully installed.

The iOS application build process is a two step process. When you build the Unity iOS game an Xcode project is generated. It is generated with all the required libraries, precompiled .NET code and serialized assets. This project is required to sign, compile and prepare your game for distribution and/or run on the actual device.

When you first run the game on device via Xcode project you are going to notice that it will take minutes to start running. After that you will see the logs in the console view of Xcode. Unfortunately it takes ages to see the logs on device.

One tip, if you want to see the logs on an iOS device without running the application via Xcode. Choose Window -> Devices from the Xcode menu. Then choose the device in the left column. Click the up-triangle at the bottom left of the right hand panel to show the device console.

Another tip, use incremental build when you build iOS app from Unity to quicken to run on Xcode. The C++ code generated by the IL2CPP scripting backend can be updated incrementally, allowing incremental C++ build systems to compile only the changes source files. This can significantly lower iteration times with the IL2CPP scripting backend. To use incremental builds, choose the “Append” option after selecting “Build” from the “Build Settings” dialog. The “Replace” option will perform a clean build.

Second Android. Before everything make sure that Android SDK has been successfully installed. And integrated with Unity.

You can build an Android application and install it to device is just one step. And it is much more faster according to iOS build. After installing and running the app on device you just need to open command line tool and type "adb logcat -s Unity" you will see all the logs related with Unity and your game.

Thanks for reading :) please go on with this post if you wonder how to make performance optimization in Unity3d for mobile games.

Hiç yorum yok: