Hacker News new | ask | show | jobs
by peremptor 1001 days ago
Im not well versed in game programming, however I have some knowledge on how to properly structure your software architecture in other domains.

In regards to third party dependencies I agree with what Uncle Bob says, which is to keep them as far away from your stuff as possible. Only introduce a hard dependency if you have to. In my current project I have been doing that and I enjoy the flexibility that this gives me.

For example I can exchange the DI framework for the whole project in a matter of days if need be.

Which leads me to my question with the Unity debacle.

Is it not possible in game development to also structure your architecture that way ? Is the extra work not justified if you have deadlines ? Or is there just a lack of common interfaces that can serve as proper abstraction ?

I am really interested if someone with more insight on game development could shed some light on that.

Thanks.

12 comments

The problem is that Unity is a game engine, not a game framework. That means that any code you do will be following their architecture and using their features. The game logic gets really tight to the engine.

...unless you are Brian Bucklew https://threadreaderapp.com/thread/1703163364229161236.html

Note: That is not the "normal" way to use a game engine.

Holy porting Batman.

Caves of Qud is definitely going on my watchlist now.

I'm pretty sure when Unity was new all games kinda looked the same. And killed my battery no matter how lightweight they seemed.

How is it today? I'm not sure what uses it and what doesn't any more. That's a good thing, i guess.

That is glorious. I would recommend that you post it as its own thread.
It was posted recently.
That's absolutely incredible. I agree this would make its own great post.
Can you abstract away your website so it can run on node.js or angular?

Game development is typically very tightly linked to a mess of proprietary tools and products and platforms. It is the game engine itself that abstracts away for example) the payment/subscription api.

If you were to write your own game abstraction layer we would call what you did a game engine.

Secondly, performance (frames per second) is key in game software. Imagine if you wrote a lightweight abstraction layer for a physics/gravity engine. You’re effectively writing code to slow down your game FPS.

Thirdly, game development is often done by young beginner programmers, who often don’t even know programming yet. They get into game programming by following online YouTube tutorials in a specific game engine. If you know the importance of abstraction your already too high paid to work in a game development position :)

> Can you abstract away your website so it can run on node.js or angular?

This is an excellent point. At that point you'd need so much abstraction that you'd basically be building something as complex as the underlying engines.

In rare cases that works out, like for Caves of Qud, but mostly because of the nature of the game: https://news.ycombinator.com/item?id=37548720

The best most folks can dream of is decoupling some of their game logic or mechanisms from the engine somewhat, like what Captain of Industry did:https://news.ycombinator.com/item?id=31588018

That said, I'm surprised that engines like Stride or NeoAxis don't try to imitate the Unity API more - making porting from another engine easier, or being able to reuse some of your existing skills would surely be a good selling point!

I read the Caves of Qud post. What exactly is he using a game engine for in the first place? If it can be completely swapped out like that then maybe it wasn't that important in the first place.
Ease of porting and platform support.
Yea, to be fair, you can abstract away some parts of games to be sure. You can usually put non graphics related game logic, data persistence, networking into a shared library in your preferred programming language as long as that language can expose its api using a c layer or some sort.
>If you know the importance of abstraction your already too high paid to work in a game development position :)

Way to tacitly admit the gamedev space is a fundamentally exploitive industrial vertical.

There's 3 kinds of dependencies. The best way to describe them that I have found is a metaphor with the Terminator movies.

* Libraries are tools that your app uses. For example, the JSON parsing library. They are relatively easy to switch from, as they are (usually) used for very specific tasks. For the terminator, a library would be a shotgun.

* Frameworks are pieces of software your app embeds into. They have a set of conventions and procedures that condition the shape of your app. In exchange, they provide a lot of baked-in functionality, which is usable by your app right away. Ruby on Rails is an example of a framework for web development. They are more difficult to switch from, because your code is "shaped" in a certain way and it relies on the framework to several tasks for them. For the terminator, a framework would be a motorbike. It's difficult to jump from a running motorbike to motorbike, if there's a T1000 chasing you on a truck (that would be a deadline). It can be done, though. Much easier to do if you prepare in advance.

* Game engines are similar to frameworks, except the dependency goes deeper. Your game is not embedded into the engine. Instead, it is made of the engine. Unity is a game engine. It's very difficult to switch from one game engine to another one. The engine would be the terminator's endoskeleton - the metal skull, torso, arms and legs, plus the initial "bios". Your game would be the living tissue put on top of that metallic frame, as well as the directives programmed in the brain ("Protect John Connor").

It's not really possible because a game engine works differently from a library.

A library is a component that you add to your application. While it might be opinionated in the way it presents its interface, you can typically build some kind of abstraction layer on top of it and swap it out with something else in the future.

A game engine basically is the application, and your game builds on top of it, filling in the game logic, assets, level design, etc. The earliest game engines like Unreal Engine 1 were basically just the game Unreal with all the game-specific bits stripped out.

An engine is more than just opinionated. It determines the general application flow and structure, how each component is conceptualized in the architecture and how things connect. It even determines which programming language you can use. You also just use a lot of components from the engine: rendering, input handling, physics, animation, networking, parallelism, asset processing, etc. Things of that scale would probably be separate libraries for most other types of applications.

Beyond programming, much of your work will be in engine-specific formats that simply cannot be automatically converted to another engine: project files, level design, component connections and settings, graphical programming and shaders, animation state machines. You could design all of this in your own custom formats and build it programmatically, but why would you take that development overhead when engine's editor already does it so well?

That's not to say that porting from one engine to another is impossible. Assets like graphics and audio can be moved with no or minimal adjustment. Game design is still the same, and typically most concepts are similar enough between engines that you can do a line-by-line conversion for most of your code. And some games really do use Unity more like a rendering and input library, Caves of Qud is one example. But those are rare and most games will simply require a lot of elbow grease to shift engines.

In other words, game engines are frameworks. Porting from Unity to Unreal may feel like porting from Rails to Django: not an impossible task, and you can keep some key bits, but you have to rewrite and rethink a lot. (Ruby and Python are even closer than C# and C++.)
I would agree with the parent poster that game engines are more than frameworks. They often begin with a platform abstraction layer, contain a framework on how to structure your tools and data, often contain an embedded programming language, have custom data formats, and determine the syntax and semantics of how you build the rest of your game.

Then in order to move between one engine and another it is a far bigger task than just switching programming languages and frameworks (which in web development are very similar). You will need to do things like:

Re-import all of the external assets that you have created in external editors. Things like meshes, rigs, animations, textures, sounds, music, etc. These may require changes to adapt them to the new engine, and it assumes that you still have the original data for all of these (which is often binary and very large in size).

Then re-create all of the specific in-engine assets that you don't have external sources for. These are things that game designers often create and describe the elements in the game, like character, vehicle, item definitions, loot tables, experience curves, etc. In addition to these being engine specific they're also fairly manual to create.

This of course assumes that you can easily bring over the world/level data from one engine to the other. At best it will require sufficiently skilled programmers to write an exporter for the old engine and an importer for the new engine. At worst it will require designers and artists to recreate the levels by hand.

Then the last thing mentioned would be changing all of the automated processes to the new engine and educating everyone in the studio on how things are meant to work.

The web based analogy for all this would be rewriting the code in both a new language and framework, changing the frontend from HTML/CSS/JS to Latex (or something), moving the data in the database to another style of data storage, and converting all the images from PNG to JPG.

More than port, I would call it conversion.
I don't feel like an expert, but I'm a full time Unity dev with experience on non-Unity AAA projects.

There are the obvious advantages of having a pre-built editor, standard environment, fantastic build tools, and generally handling a lot of the complexity of managing a project, particularly in the early stages.

It's possible to limit your use of the engine to these time-savers, and essentially use it as a front-end for your game, however many workflows within the Unity projects make heavy use of engine-specific features which become an integral part of the game over time.

Rewriting scripts to not make use of the Unity engine's C# features isn't necessarily major challenge, depending on how reliant you are on engine-specific features. But when you've set up dozens of entities and items using Unity's animation state machine UI, laid out your levels in Unity's editor, and saved hundreds of different reusable assets as Unity-specific "prefabs", setting up all the relationships in your project which aren't determined by code would be a massive time sink.

> when you've set up dozens of entities and items using Unity's animation state machine UI, laid out your levels in Unity's editor, and saved hundreds of different reusable assets as Unity-specific "prefabs"

Perhaps this was a mistake.

Or may be not. People commit to use a tool, so why not doing it fully so you get the most benefit from it?

It makes everything harder if you want to use a tool without really using it so you can move to a different one if needed.

1. Unity projects mainy use C#. So that automatically limits the frameworks you can switch to unless you want to port to a new language.

2. A fair chunk of your code will be calling in to the Unity runtime to use functionality provided for you. This may not exist in other engines or exist in very different form

3. Unity controls the gameloop and the renderer. Other engines might have very different architectures and constraints.

4. Abstractions cost performance. You're usually trying to hit a very tight millisecond budget per frame so adding extra layers of abstraction is not good practice.

> In regards to third party dependencies I agree with what Uncle Bob says, which is to keep them as far away from your stuff as possible. Only introduce a hard dependency if you have to. ... For example I can exchange the DI framework for the whole project in a matter of days if need be.

Game engines aside, this is a very late 90s/early 00s philosophy that has really not stood the test of time. The code that strictly follows this principle ends up looking like Enterprise FizzBuzz[0]. Trying to do this results usually in a lot of time spent building an abstraction layer around one thing, and that one thing not actually being replaceable because it was only ever built or tested with that one thing, so trying to replace Thing One with Thing Two is even more work as you now have to rip out both Thing One and adjust or remove the abstraction layer around it.

Abstractions aren't free, and the more complex the library you're trying to abstract, the more expensive they are.

There's nuances to this of course -- it's a lot easier to write a custom wrapper around a logging library than a DI framework, for example. But in general if you're using a third party library, things end up cleaner if you just commit to it rather than write an abstraction layer.

[0] https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpris...

My very limited understanding (speaking only as a professional software developer but not a game developer) is that there’s very rarely an abstraction that’s common across multiple libraries in the same domain, or that, in order to make an abstraction that could be adapted to different libraries, you would have to give up a significant amount of performance.
This worried me as well when I started looking at Godot. It seems like it is, like other game engines, designed to not really allow you to separate your code from the engine code in any meaningful way.

The only things I have thought of, or seen others do, is to separate the game into a native library, or even a separate server process, that handles the game-world part of the game, and then use Godot as the front-end (client) gui only. But you will miss out on many of the engine's features since a lot of it is built around mixing on-screen objects with game-world objects. There isn't really any separation at all between what happens "in the game" and what is rendered to the screen.

But building the game from the ground up using Godot nodes, with a bit of GDScript here and there to tie things together, is definitely easy and very tempting to do. It pushes you towards that easy way of doing things.

I'd say a big part of it (aside from the valid "framework" comments) is that game engines abstract over a large and complex mess related to graphics card APIs on different platforms. That abstraction is of the type that basically forces game engines to be frameworks, as opposed to libraries. If you want your game engine to run games on Mac, Linux, Windows, Android, iOS and the web, the game engine needs to "wrap" the game. Or at least this "wrapping" makes both game engine and the games simpler to write. But as mentioned, it means switching a game between game engines isn't really realistic - you write a unity game.
> Is it not possible in game development to also structure your architecture that way ? Is the extra work not justified if you have deadlines ? Or is there just a lack of common interfaces that can serve as proper abstraction ?

The simple answer is:

Yes, you 100% can, if you code it like that from the very begninning.

No, most people don't, because most game engines are not desiged for this so it's extra man-hours with no visible value.

Yes, it is possible, but you need to use a game framework not a game engine.

Example of a game framework: http://www.monogame.net.

Still, if you don't map everything all the time, common value types will propagate through the codebase.

Even if you code against MonoGame/XNA or a similar framework, migrating to another framework is not a trivial task unless you have written an abstraction layer beforehand, cf. Player.cs from Celeste.