«

Oct
19
2013

An Opinion on Unity 3D

When Microsoft pulled the plug on XNA (or rather, the moment Shawn Hargreaves left the team, but I have the suspicion that at least inside Microsoft, that’s more or less the same point in time ;)), I started looking for alternatives.

At first, I toyed around with Ogre3D and its C++/CLI-based .NET wrapper "Mogre", but the more I saw of Ogre’s internals, the less convinced I became, as it seemed to have entered that phase in an Open Source project were the community throws mediocre code at it and leadership lets it stick, unwilling to evolve the design and betting on the "version 2.0" fix.

Because I believe writing custom middleware is too big a task for a single developer (unless your goal is a 16 bit era game), I went engine shopping once again. My choice fell on Unity 3D.

That was about 18 months ago.

Unity quickly got me to the point where I was play-testing instead of designing internal APIs for behind-the-scenes stuff. Actually reaching the point where I had to create art and work on gameplay issues was scary and refreshing at the same time. I’m now a seasoned Blender user, own a small library of licensed music tracks, 3D models and textures and I learned a lot about how to fit my own work into the ecosystem provided by Unity.

Here’s what I think so far:

 

Strengths

  • Scene Editor

    This is doubtlessly the biggest advantage of Unity. Where most free 3D game engines leave it up to you to come up with a working content creation pipeline or provide barebone editors that don’t do much beyond moving 3D objects around, Unity has a really good and extensible editor.

    It lets you place "GameObjects" in a scene and attach 3D models, height maps, particle systems or custom scripts to them. You can then establish references between those objects (for example, a switch may have a property called light, onto which you can drag & drop any light in the scene and Unity will save this reference).

    On top of that, you can create prefabs (blueprints for GameObjects with all the stuff already added to them) that let you place ready-to-go enemies, items and anything else you can come up with. Extending the editor so it displays additional overlays (think waypoint markers, enemy spawn spots) that are only visible in the editor is also very easy.

  • Asset Import Pipeline

    Many engines have a cumbersome workflow for artists. If you spot a problem on some 3D model in the game, you’ll have to:

    1. Locate the original model
    2. Open said model in your 3D suite
    3. Make the adjustment
    4. Export the model to FBX or Collada
    5. Possibly run a special command-line conversion tool
    6. Copy the prepared model into the game’s asset directory

    In Unity, you can store your original models in their native format within your game’s asset directory. Unity will monitor all files for changes and immediately reimport them when they change. So in Unity, this becomes:

    1. Double-click the model in Unity
    2. Make the adjustment
  • Mono

    The decision to use Mono for scripting within Unity was brilliant. Scripting languages in most games tend to put you into situations where:

    1. Your code outgrows the scripting language (example: doing complex stuff in Skyrim’s "Papyrus" script with the only means for storing data being flat variables).
    2. The scripting language’s standard library is missing vital functionality that you have to write yourself in C/C++ (example: Lua).
    3. The scripting language is a dynamic language which makes refactoring without a thorough unit test suite and IDE support very dangerous (example: Python in Blender Game Engine)

    Mono supports statically typed languages, has a large and well-designed standard library and is (as of August 2013) around 20 to 40 times faster than Python or around 10 to 20 times faster than Lua. Mono also has full reflection, native calls via P/Invoke and a large user base.

  • GUI

    Unity’s GUI is something many users are complaining about, but I think it’s actually a very powerful and easy to use GUI system with excellent automatic layouting and good theming capabilities. Its only real problem is that there’s no visual designer for non-programmers.

    As an example, to create an area with a scrollbar containing mixed images and text, this is all you need to do:

    private Vector2 scrollPosition;
    private Texture imageTexture;
    
    void OnGUI() {
      this.scrollPosition = GUILayout.BeginScrollView(this.scrollPosition);
      GUILayout.Label("This is some text that will appear before the image");
      GUILayout.Label(this.imageTexture);
      GUILayout.Label("This is some text that will appear after the image");
      GUILayout.EndScrollView();
    }
        

    And if the various layouting capabilities aren’t enough, you can either leave the layouting system alone or ask it to reserve a rectangle for you via GUILayoutUtility.GetRect(), within which you can freely position other controls that will still clip and scroll correctly if the rectangle is part of a window, scroll-view or other container.

    You can even do those cool in-helmet displays where GUI controls are skewed to give them some perspective. Just assign the matrix created by the following code snippet to Unity’s GUI.matrix:

    /// <summary>
    ///   Creates a matrix that offsets the Y coordinate by
    ///   a fraction of the X coordinate
    /// </summary>
    /// <param name="factor">Factor by which Y will be offset per X</param>
    /// <returns>The shearing matrix</returns>
    private static Matrix4x4 createShearMatrix(float factor) {
      Matrix4x4 matrix = Matrix4x4.identity;
      matrix.m10 = factor;
      return matrix;
    }
        
  • Performance

    Generally, Unity’s performance has been beyond excellent. It’s clear that there has been a lot of effort under the hood to ensure that the stuff in your scene gets drawn and updated as fast as possible.

    Even if I view my entire scene with 3,000,000 polygons in total in the editor, my dated GeForce GTX-260 is still fed so well that I don’t ever notice the FPS dropping below 60.

 

Weaknesses

  • Flawed Importers

    The one thing that gives Unity its insanely quick workflow - directly using source models as assets – is at times so slow that it becomes borderline unusable.

    I’m already storing my animations in multiple separate files so that Unity only needs to import a really tiny (<1 MiB) file containing just a set of animations instead of the whole mesh. Yet, whenever I change even the tiniest thing, Unity locks up for 4-5 Minutes to reimport it.

    As if that wasn’t enough, you’re often forced to sit through the whole process 2 times in a row. Reimport once for Unity to update its list of available animation clips you could import, then do it a second time after you’ve actually added your new animation clip to the clips to be imported.

    Unity’s translation of Z-up (Blender and 3ds Max) to Y-up is also wrong. Your model often ends up rotated 180 degrees on the up axis and -90 degrees on the forward axis unless you add bone animation which magically rights it. But the bone orientations will still be what I would, in the most friendly terms, describe as a "clusterfuck".

  • Locked-Down Input Manager

    Unity’s input manager looks nice at first: you can bind any Joystick axis or button to an "Action". Then your game simply queries the state of the action and works with that, not caring what input devices factor into it.

    You only notice the big problem when you’re half-way into your game and want to offer users the option to choose their own keys. There’s no script access, the only way to change the controls is by using Unity’s launcher dialog.

    The input manager could be a fantastic foundation for all your input needs and the Asset Store would surely already contain a host of pre-made key binding configuration screens for Unity users to pick from if there was a way of accessing and changing the bindings from the script side. Instead, you have to write your own, equivalent input manager from scratch if you want changeable key bindings.

  • Modularity, Services, Unit Testing

    …are virtually out the window for Unity projects.

    You can’t run most of your Unity code outside of Unity because vital classes such as Transform, Mathf and even MonoBehaviour (the base class any script that can be attached to a GameObject needs for some reason) use Mono ICalls (internal calls) that won’t work eg. in NUnit or in a standalone application outside of Unity.

    Working with interfaces and decoupling things becomes cumbersome because Unity only allows references to MonoBehaviour-derived objects to be set in the editor or retrieved via GameObject.GetComponent<MonoBehaviourBasedClass>().

    I had to accept a lot of compromises to even get a basic IoC container for dependency injection into my Unity game.

  • Terrain Draw Calls

    This is one place where I’m really unhappy about Unity’s performance. The terrain has all this support for trees, detail meshes and wind, but it seems that placing ten clusters of grass (same model, same material, same everything) in the scene adds twenty or so draw calls.

    Even trees at impostor distances seem to be drawn at one draw call per impostor, making it hard to do anything but sparse vegetation.

    I’ve seen other Unity games (eg. Slender: The Arrival) use lots of grass or trees, so I’m not quite sure about this one. Perhaps disabling wind or doing something magical with the meshes used for detail geometry will remove my problem.

  • Headless Servers

    As I found out, if you want to run Unity in a continuous integration environment, you need dedicated machines doing the builds. That’s because Unity uses the GPU for its build process (I guess resizing textures – anything else?) with no fallback.

    So setting up a cheap VM that you can easily reset and run on your existing home server or rented dedicated web server won’t work. I tried it with the experimental VirtualBox Direct3D drivers, using the -force-opengl argument and even hacking Mesa (software-based OpenGL) into a replacement for OpenGL32.dll on Windows without success.

    Without installing 3D accelerator drivers on the host, I’m forced to do my Unity builds on my workstation and upload each in a slow, multi-hour crawl to my web server.

  • Miscellaneous

    Math library. Someone thought it would be a good idea to switch Unity’s entire set of math routines from radians to old degrees. Anyone who should be working with those routines – (anyone who knows math) – uses radians. And the .NET math routines also callable from Unity scripts still use radians, of course.

    Time stored as floats. I hope that Unity internally uses something else for Time.deltatime, but at the very least Time.time will lose millisecond-accuracy if your game runs for just an hour.

    Ancient Mono version. There have been a lot of useful developments since .NET 2.0 – the new ISet<> interface, asynchronous tasks and if I go as far as .NET 4.5, await, which could replace Unity’s IEnumerable coroutines. Due to a licensing issue (Unity for some reason uses modified Mono sources, relying on a license from Novell that allows those modifications to be kept private, but Novell was acquired by crooks and more recent versions of Mono are now in the hands of Xamarin).

    Threading. You can create multi-threaded code in .NET just fine, but Unity doesn’t even let you retrieve a transform’s position or query for a component from another thread. So if you use threads, interaction with Unity must be Zilch (theoretically, things done purely in Mono like and Rect can be used, but the Matrix4x4 class already contains native calls into the Unity runtime.

    Documentation. There’s documentation for each function, but it tends to state the obvious and miss the things you’d actually want to know. It is improving, though.

3 comments

  1. Jay says:

    The Z-rotation is not wrong, it is just another kind of coordinate system which other 3D-tools also use.

  2. Cygon says:

    I don’t see where I’m saying that.

    If you’re referring to my statement that "Unity’s translation of Z-up (Blender and 3ds Max) to Y-up is also wrong.", I’m not saying that Unity’s choice of coordinate system is wrong, but that Unity’s Blender and 3ds Max import gets the orientation wrong — model a house, drop it into Unity and it will rest on its side.

    I know, that’s easily fixed by rotating the model in Unity or by adding a non-baked 90 degree rotation to the X axis in Blender (R X -90 Ctrl+A (Rotation) R X 90).

    For animated models, however, bones end up in pretty arbitrary orientations. If I create a character with a normally oriented head bone in Blender, attempting to assign its orientation in Unity via Quaternion.LookRotation() will turn the head on its side and make it look up and down instead of left and right.

  3. Cam says:

    Yep, the imported bone orientations are a mess. Fricking surreal. No solution, no workaround, and Unity doesn’t care.
    Now I’m going to have to create a predefined animation for my crowd templates to play, instead of procedural motion.

    You only find out the limitations of prepackaged stuff until you’re pretty invested and hit a wall.
    In general, that’s the kind of stuff that -in the back of my mind- makes me want to roll my own solutions from scratch…

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Please copy the string OmP3qV to the field below:

Social Widgets powered by AB-WebLog.com.