February 16, 2022 Unity Game Development C#

Reflecting on using 2D in Unity in 2021/2022

I was asked a while ago by Unity to send them some thoughts on what I think about Unity with regards to 2D development, with emphasis on sharing all the gory details. Nothing about any of the communication I had with them indicated that the information had to be private, and because I spent a lot of time writing it all out I thought it would be nice to put on my blog as well.

The asked me some specific questions, so I’m basically going to post my exact responses below, sparing no details (as the specifics from experience I think will really matter to people in a similar situation).

TLDR: The better you know C# programming, the more diminished the value of Unity is, ESPECIALLY in 2D games. Our main use case for Unity is for its UI system, and we would consider doing a custom engine in the future with some UI middleware instead.


On General 2D Use In Unity

Which features are you currently using in your productions? In what ways do they work as expected. In what ways are they broken or inadequate.

Unity UI

  • This is by far the biggest feature we use. Strategy games have a ton of UI and Cantata is no different. It took me a bit to get used to Unity UI and the way it wants things to work, and I’m still unclear on some of the ways things are named (i.e. “Child Force Expand Width” along with Child Control Size” can mean a few different things). I’d say I’m generally fine with Unity UI at this point, and have internalized its logic.
  • Where it falls down by far is data binding. I know UI Elements solves some of this, but I’m not interested in trying to internalize a new UI framework at this stage of production (~1 year from release) and I also dont think it’s ready for Game mode yet last I checked.
  • For Unity UI, we bind all of our events in code, because the inspector event bindings are too limiting, allowing for only basic types. This means we use Unity UI only for basic layout and config, with all the business logic in scripts. It feels like leaning on inspector events would be absolutely untenable, even at our small size (not easily searched, not easy to follow logic flow, etc.)
  • Because of late binding the events, we also generally procedurally fill out all of our data. The means that auto layout functions are crucial. We’ve split things up as much into seperate canvases as possible to make sure these calls are only as costly as they need to be and dont trigger full UI redraws.
  • For reasons listed later, 99% of our work in unity” is just setting up UI.
  • For us, Unity is basically a UI layout engine.

Nested Prefabs

  • Godsend for UI!
  • I have no complaints here, and it really feels like these were implemented right.
  • These making working with individual reusable elements of UI great, whereas before we had to make more UI.

2D Animation

  • This was also a major godsend. I wrote a whole post on it for game developer here.
  • Cut down on a lot of boilerplate work
  • There is some weirdness with the way animations have to be timed (like actually clicking on the sprite to animate, as the hash is impossible to know) that make working with it strange, but overall it’s exactly something I was looking for.

Which features did you evaluate and then decided NOT to use. What are the reasons for that decision?

Tilemap

  • This seems great, but came far too late into development for us to switch.

  • Additionally, our use case requires runtime tilemap generation, as we are providing a level editor to players. Unity’s story on runtime generation was unclear to me.

  • Rule tiles additionally (last I checked) seemed under documented and under considered, and would have been our main use case for the tool, as every tile in the game has rules.

  • Instead, we developed our own GPU-accelerated tilemap renderer, that allows us to render maps basically as large as we want, with every tile also have its own rules (250,000 tiles+).

    Input

  • This is TBD, but right now we’re just using basic Unity Input class. We may switch to the new Input system if need be, but our control needs are pretty simple, and coding our own simple keybinding system was pretty easy as well.

  • The readiness” of this seems to be sort of floating. I’m wary to use it, but still interested. We may switch to this if we need to due to consoles.

    Networking

  • This is a total mess. There are three different networking APIs all in various states of readiness and documentation, with no clear path on what is the right way”.

  • Additionally, we don’t need the complexity.

  • We only need basic P2P networking, so are just using the P2P API of Steam via the Facepunch.Steamworks plugin.

  • I tried Unet and whatever the other ones are, but they just felt like too much to manage, with a moving API target, so we dropped them altogether.

    ScriptableObjects

  • I used to extoll the virtues of Scriptable Objects (slide from a talk I gave at a gamedev meetup):

sob4

  • But now I’m over them:

soafter

  • Despite being promised as pure data”, they need a ton of management. Additionally, there is no good way to author them outside of the context of Unity, as they need to be turned into a proper asset. They are assets, not data.
  • I’ll talk more about why I moved away from these in the section below our needs, uses, etc.”

Collaboration

  • This messed up like 5 times when we first tried and I just dropped it. It doesn’t work well at all for a branch workflow even between two different team members.

Addressables

  • I had high hopes for these but totally lost the thread on them. They were announced 3 years ago or something then I never heard anything since then. Additionally, I’ve more or less removed Cantata’s need for them now via the systems I talk about below.
  • The documentation for them now also states that they are deprecated? What’s the replacement?

When you imagine the ultimate 2D feature set for Unity in the future, what would your hopes and dreams be for it? What have you always wished you could do with 2D in Unity?

  • I view it as less features, with a focus largely on workflow optimization. 2D is simple, and putting lots of features” against that just get in the way. Some things like 2D Animation are nice features, but if anything are just adding a UI layer to something that could be easily done in code if Unity allowed easy access to the sprites in an imported tilesheet.
  • Honestly, a better 2D Unity for me would be one that is laser focused on 2D. I look at stuff like Defold and can see how their focus on 2D makes everything easier to reason about and manage. Unity gives you the power to make a AAA game out of the box, but 2D games need like 3% of those features, so most of my time is avoiding most of what Unity offers.
  • This could even be just providing a new set of APIs that wrap common 2D functionality to make for easier use in Unity. Something like Futile, but better.

Other notes/thoughts

A lot of my experience in Unity is informed by my specific use case: Building a Data-driven/Moddable Standalone Pixelart 2D Game.

Here’s my experience working with that:

I don’t use most of Unity’s features

I don’t need most of what Unity offers. I can do most everything I need via code. Most new features Unity seems excited about never seem like they are for me. RenderPipelines, Lighting Engines, Houdini, etc.

Shifting API Targets

Similar to the above, APIs always feel like they are moving around. I’m incredibly reluctant to use anything new Unity puts out because I am now conditioned to think it will be deprecated or ignored for a few years.

Data-driven

Cantata is built from the ground up to moddable, and hence is very data driven. Unity doesn’t provide a good route towards doing that, as ScriptableObjects need to be imported via the asset system, Resources are deprecated, AssetBundles are deprecated, and Addressables are undocumented. I don’t need some massive CDN use case - just easy ways to load data from file. What I’ve done for Cantata is essentially circumvent Unity’s asset pipeline, and instead load most of my data in from disk at runtime. I use my tool Depot to author data outside of the game, and just load that at runtime, building the sprites programmatically from textures, etc. This would be insane to do for a large 3D project, but for a game where sprite sheets are rarely larger than 512x, it’s totally fine and the game runs great.

I also use Unity’s JSONUtility for this, but do wish it was better. Doing things like being able to deserialize arbitrary JSON, or incrementally parse based on keys, would be a godsend. Instead right now I use SimpleJSON to step down the tree a bit, and then pass those nodes into JSONUtility to be deserialized.

Source Generators

I made a thread on the forums about this, but having these in would be a godsend as well for 2D data-driven workflows (and many other things). I really want to lean on the fact that, in a perfect 2D world, Unity would be a best-in-class C# user and provide lots of ways for users to make games faster from C#. I see they are a stretch goal for an upcoming milestone as well, which is great. Generally, having up-to-date C# feature sets would be huge.

I hate not being able to leverage modern C# for Unity projects.

The Better The Studio is at C#, The Less We Use Unity

Additionally, I find that the more I learn about C# the less I care about using Unity for simple 2D things. Declaring static Actions in a single class is 10000% easier than trying to manage EventObjects” to bind to event fields in the inspector (or using the UnityAction panel). C# events allow me to find references and easily follow data flows. Working through the inspector does not. I want to really drill on this as well, especially for a strategy game. The game is highly systemic, so understanding the how/why of something happening is of the utmost importance. This means that solutions that are opaque in their data flow (like inspector-driven workflows) or muddy about what is happening where (inspector + code workflows) are not viable. This means defaulting to code-centric development is effectively the only option for us, meaning I rarely touch Unity’s inspectors.

We probably won’t use Unity for this use case again

If we are to make a similar game again, we probably won’t use Unity. The only thing I really feel like we’d be giving up is the UI stuff, but that can be easily exchanged for something like Noesis or Coherent. I’m also very interested in Blazor. We will definitely stick in C#, and are already in the process of making our own 2D engine that solves a lot of our use-cases above. If we were doing a mobile game, I would strongly consider Unity, but also weigh it vs. our own engine or looking to a tool like Defold.

For 3D, we are considering switching to Unreal and leveraging Unreal CLR, though studio knowledge in 3D via prior work would make us definitely consider Unity here as well.

All that said, we are still 100% using Unity for Cantata and will not change engines before release. We’ve found a good balance and workflow for making Unity work with code, and will use that to deliver Cantata, as well as any future DLCs.


And there it is! For what it’s worth, I didn’t get some angry message in reply (at least publicly). I think Unity is well aware of their deficiencies around traditional 2D development”, but also they don’t really seem to care about making it a lot easier, probably because 3D represents a larger market-share to target.

This is fine and I’d probably do the same thing if I was in their position, but it does also sort of function antithetically to the narrative of Unity being easy to use”. I wouldn’t describe the total lack of a cohesive Input paradigm for a game engine easy”. Or the fact you have to deal with the concept of a renderer” just to get a sprite on the screen, etc.

It’s maybe easy to use” in a context where you’re coming from bloatware AAA engines that need highly technical expertise to function, but for the average person, Unity puts a lot in your face to do very simple things. If you’re making a 2D game, you’re probably doing simple things a lot, so this stuff can be distracting to the point of total refusal to engage with it.

IDK - I know in the above I said we wouldn’t use Unity again for a similar use case, but we in actuality probably will because nothing has stepped up to the plate yet. Maybe my engine will be The Thing I’m really looking for. Maybe not.

For now, hope this was enlightening to other people in a similar position of evaluating Unity for projects of a similar vein. Hit me up @kkukshtel on Twitter if you have any questions.



Date
February 16, 2022