Game Programming Vexations Part 2: The Four Challenges of Game Development

By Shamus Posted Thursday Sep 5, 2019

Filed under: Programming 84 comments

So why do we need a new language for games? Isn’t the world full of languages at this point? Is it really true that in all the dozens of active languages out there, none of them is suitable for making video games?

A year ago Blow gave a talk where he said he didn’t want to use any of the extant languages because they contribute to an ongoing “general insanity” going on in software development. It’s a long and multi-faceted argument, but if I had to boil it down to a soundbite I’d say that there’s a general desire to hide the hardware away behind abstractions and this is making our software slow.

I’m not qualified to support or refute that argument, so if you think Blow is wrong then you’ll have to take it up with him. I’m going to come at the argument from a different angle…

Invading Another Language

Nice language you've got here. We've just got ten thousand teeny tiny little requests before we switch to it.
Nice language you've got here. We've just got ten thousand teeny tiny little requests before we switch to it.

Personally, it seems like it would be a bad idea to co-opt the likes of Go, Rust, or D for game development. If you look, you can see these languages are “nearly” ready for game development. To date, nobody has shipped a seriousReminder: By “serious” I mean a proper 3D simulation and not something programmatically trivial like a side-scroller. game in any of these languagesWe do have commercial 3D games made with Unity, and thus C#. But for the most part I think everyone gets why C# isn’t a viable alternative for AAA game development.. Which means the tools aren’t there. Which means the main adherents of these languages are not game developers. At the same time, there must be a reason that game developers haven’t already embraced these languages. We have to allow for the possibility that these alternate languages are missing some features that game developers are likely to need.

Switching over to an extant language and changing it to suit the needs of game development would be significantly more controversial than just starting your own language. Maybe the C++ refugees would be welcomed with open arms at first, but as soon as they began demanding changes or complaining about limitations it would cause rifts. Maybe the language would be forked, or maybe a bunch of features would be added that existing users don’t like or need. Or maybe the incoming game developers would need to cajole and plead for every feature. If the only C++ refugee was Jon Blow all by his lonesome, the community would probably tell him to go make his own damn language if this one doesn’t suit him.

Shamus, what are you talking about? What are these special features game developers supposedly need? A general programming language is suitable for general tasks, and that includes games! What is it you think you’ll need?

I’ll come back to this question later in the series. For now just humor me.

I realize I have to be really careful here, because suggesting that language X isn’t suitable to a given task will quickly rally all the supporters of X against you. Everyone will feel the need to defend the honor of their favorite language. They will gather to inform you that actually, X is FANTASTIC and you’re just bad at using it and maybe you should do some research before running your mouth. If you claim that NO extant language is suitable, then you’ll find yourself at war with the adherents of every language simultaneously.

This really does put the game developer in a “damned if you do, damned if you don’t, and damned if you compromise” type situation. If you ask for language features to suit the problem you’re trying to solve, then people will just accuse you of being ignorant or entitled. If you go off to make your own language, people will accuse you of being a naive fool. If you stick with what you’ve got, then people will accuse you of being stuck in the past. Your only escape is to pick a language, pay the high cost of transitioning, and then fall in line behind the faithful.

INT – Video Game Industry – Day

Game Developer: Man, this job is a drag. I wish there was an alternative to C++.

Foo Evangelist: You should try the Foo language! It’s amazing.

Game Developer: I don’t think this new Foo language will do what I need.

Foo Apologist: You can’t criticize Foo! You don’t know anything about it.

Game Developer: Okay, I’ve just spent half a year learning the language, adapting libraries, and attempting to build a game. Foo really doesn’t have feature X, and I need it.

Foo Grognard: X is a stupid feature. I’ve been making web and mobile apps for a decade and I’ve never needed anything like that. We don’t need to clutter up the language with obscure features for lazy game developers. Go use C++ if you want those kinds of weird-ass features.

Rather than try to convince you that alternative languages are insufficient, I’m going to try to sell you on the more general idea that – all other things being equal – a language devised specifically for games would be better than any of those other choices.

With that in mind, I want to talk about what I think are the four main challenges of games programming:

1. State Transformation

All programs change the state of the machine. As a result of running the program and entering input, internal variables change, new data is written to disk, the contents of the screen are changed, and data is broadcast over the network where it will cause further state changes on other machines. If there was a program that didn’t cause state changes, then there would be no point in running it because it would, by definition, do nothing. However, I think games really do face some unique problems with regard to the frequency, intensity, and variety of the state changes. Not only do games change a lot of state very quickly, but the types of transformations and the causes of those transformations are incredibly complex.

In a game you can have situations where the player shoots a gun that fires a bullet that hits a barrel that triggers an explosion that generates a physics event that knocks over a physics object that lands on a mook and kills them, which results in spawning loot that lands within the player’s pickup radius and is added to their inventory. A single input action caused a state change for seven different objects spanning multiple gameplay systems.

Even worse, it’s not a linear chain reaction like the previous paragraph suggested. The state changes actually branch outward as they cascade through the system. The explosion also spawned a particle emitter, which will in turn spawn particles. The death of the mook will lead to a new mook being spawned sometime in the next few seconds, somewhere nearby. Shooting the gun creates sound effects, creates more particles, and updates the state of the gun itself to deal with the reduced ammo count and kick. Information on the player’s HUD is changed. Damage floaties will spawn at the location of the mook’s former head. Other AI actors in the area will change behavior. The player earns experience. The physics event shoves a bunch of small objects around, which creates even more particles as the debris kick up dust. That single pull of the trigger expands outward, touching countless gameplay systems and eventually altering many megabytes of memory.

Not only are the changes myriad and complex, the causes for those changes are also complex. State changes in response to player input, AI behavior, the rules of the simulation, and information coming over the network. This is one of the reasons AAA game devs shy away fromOr openly despise and denounce. strict Object Oriented Programming. OOP encourages you to wall off all of your different objects. A bank transaction should not be able to change the name of the customer, and so on.

Pictured: Some state transformation.
Pictured: Some state transformation.

But in a game where all of the different systems are tightly connected and interact in complex ways, you can’t treat each system like a black box and consider it in isolation.

In strict OOP, systems that interfere with the ideal of encapsulationSealing one chunk of code off from other systems. are referred to as cross-cutting concerns. The problem is that modern game design pushes us towards situations where nearly everything is a cross-cutting concern.

In a game, that dumpster is actually 3 different conceptual systems: There’s the model that needs to be rendered, there’s the collision hull that makes it run into things, and there’s the physics object that gets knocked around (and knocks other things around) once the shooting starts. All three of these things are very different, they’re used at different times and in different places by different systems, and they need to be packed in memory with similar objects for efficient processing. If your collision hulls are mixed in with other types of game objects in a big generic list of random crap, then doing batch operations on those objects will be expensive.

Somewhere in the chaos, you look at the data and notice that the mook’s body is a million meters outside of the bounds of the gameworld and you have no idea where to look to find the cause of the problem. Literally any of these systems might have caused a change or a side-effect that altered the position of the mook in ways you can’t think of just now. Debugging becomes very difficult. Bugs are hard to reproduce and harder still to track down.

Here's another picture of state transformation in action.
Here's another picture of state transformation in action.

Yes, other programming domains have lots of state change. In accounting a single transaction will take some money from one account, add it to another account, and record the transactions in many different places. I’ll bet the error-checking and handshaking between financial computing systems is monstrously fussy and it always has to contend with the Two Generals Problem. There’s a lot of state change, but it follows fairly predictable lines. Every state change is an explicit part of the spec. You’re not going to have a weird edge-case deposit that changes the customer’s first name, issues a new credit card to everyone over 18, and modifies the street address of the bank.

In gaming, having emergent state change is considered a bonus. “Oh look. I broke this wall which released water which put out a fire, even though that wasn’t a specifically coded or scripted event. Cool!” In just about any other domain, non-spec interactions are a serious bug!

Mass state change is almost a design imperative in the world of games. Designers are always pushing for more complexity, more interactivity between systems, and more reaction to player input. We want fires to burn things, flowing water to displace things, light to illuminate things, wind to shove things, and explosions to destroy things. Characters should push foliage out of the way, leave footprints, trigger sound effects, make sounds, stir up dust, interact with objects in the environment like chairs and doors, and exert force on physics objects. As we work toward making our simulations more interesting, we make them increasingly interconnected and unwieldy. This makes it challenging to keep the program running fast. It also makes bugs more likely, and makes them harder to find when they show up.

2. Performance Always Matters

*nonsensical engine-revving noises*
*nonsensical engine-revving noises*

Video games are not the only domain where performance matters, but games are rather unique in the sheer complexity of their performance needs.

Sure, every programmer wants their code to run fast, but in games the user will experience performance fluctuations in realtime. They can both see and feel dropped frames. That 16ms stutter will be infuriating and uncomfortable to a gamer trying to line up a headshot, while (say) a video editor will just accept the occasional stutter as part of the job. A Pixar-style render farm needs speed, but if there’s an optimization that occasionally stalls all rendering for half a second in order to speed up the overall job, then that’s totally acceptable in the world of offlineIn this context, “offline rendering” just means “not realtime”. Like, you start the render process and then go have lunch. rendering. Photoshop needs to run its filters as fast as possible, but the vast majority of the time has the program sitting idle and waiting for user input. It’s completely acceptable for the interface to momentarily stutterOr even go unresponsive for multiple consecutive seconds! when the user applies a complex filter to a large image. But games don’t just need to be fast, they need to be consistently fast. They need to be fast across multiple types of processors running vastly different simulations. Productivity software can get away with exhibiting mildly annoying interface hiccups that would be called egregious and unplayable in a video game.

Remember that I’m not saying that games programming is harder or that game programmers have it tougher. Other fields have their challenges, but not many of them have this particular challenge where performance needs to be both high and uniform. I’m not saying mountain climbing is harder than cross-country skiing, I’m saying mountain climbers will have problems that skiers don’t. Skiers probably have their own unique problems. If so, they’re free to come up with their own programming language to deal with them.

I think I lost the plot on that last analogy. Whatever. You get the idea.

There aren't enough rendering resources to go around, so I decided to MAKE MORE RESOURCES.
There aren't enough rendering resources to go around, so I decided to MAKE MORE RESOURCES.

This problem isn’t going to go away. If you put on the infinity gauntlet and snapped your fingers to make every processor ten times faster, game designers would instantly find a use for all of that power and we’d once again be up against performance limits. We already have technologies sitting around, waiting until we have enough spare power to support them:

  • Raytracing.
  • Real-time fluid dynamics that integrates with physics so a designer can dump a megaliter of water into a space and let the deluge push stuff around and create structural failures without needing to cheat with pre-scripted behavior.
  • More particles, bodies, and debris so the game doesn’t need to constantly tidy up loose bits and clear up smoke.
  • Turn more of the environment over to physics objects that can be broken and destroyed.
  • More ambient NPCs moving around to make the world feel more aliveHitman does REALLY well with this, but I imagine the team has to put in a lot of work to make the huge crowds possible. If we had more power, it would be easier to pull this off and more game designers would want to use it..

Performance always matters, so therefore we’re always going to need a language that allows us to do low-level optimizations that would be seen as unreasonable in most other domains.

3. CPU + GPU

Yes, this is recycled from my most recent video. Look, just don't say anything and nobody will notice.
Yes, this is recycled from my most recent video. Look, just don't say anything and nobody will notice.

It’s been a while since I did a Terrible Car Analogy™, so let’s do one of those.

Imagine you’re driving a box truck. In the back you’ve got a load of sound effects, physics events, and gameplay updates. These need to be delivered to a warehouse on the other side of the country. You’re navigating a complex interchange between three different roads and trying to make sure you’re always in the proper lane. You’re trundling along with your load of data and everything is going good.

However, you’ve only got half the shipment. The other half is the graphics rendering. That’s being delivered by a different vehicle. This vehicle is monumentally faster than your humble box truck. It drives at hypersonic speeds. Maybe it’s bright green and says NVIDIA on the side. It’s so fast that if this was a real car and not a terrible metaphor, the front end of the car would create a compression zone that would surround the car in plasma, like a spaceship going through re-entry. It would also probably kill everyone it passed, which would be basically everything on or near the road.

You can’t control this other car directly, and instead you have to use the driver helpfully provided by Nvidia. You have a one-way radio that you can use to give directions. The driver doesn’t understand your native language, but Nvidia has a phrasebook of stuff the driver understands.

Supposedly you should use the newest drivers to avoid crashes, but frankly I don't feel safe with them until they're over 20.
Supposedly you should use the newest drivers to avoid crashes, but frankly I don't feel safe with them until they're over 20.

The rocket car is insanely fast, but it’s also limited. It can’t use toll roads, it can’t handle making left-hand turns, and it doesn’t understand stop signs. The designers never intended it to go through roundabouts. You can do it, but you’ll lose all your speed advantage because the car will loop through the roundabout 200 times before proceeding with the route. Likewise, it can’t really handle stop signs properly so it always stops for 5 full minutes before proceeding through the intersection. You can’t really see where the rocket car is or track it directly, so you have to shout your directions over the radio and hope for the best. If it doesn’t show up on time, it’s very hard to figure out what went wrong or where it wound up.

Your goal is to get your box truck and the rocket car to the same location using different routes, and the delivery isn’t complete until you both get there. When you do, you’re done rendering the frame and you can get started delivering the next one.

The routes are always changing, and just when you feel like you’ve got a handle on things you get a new rocket-car driver with a slightly different skill set and new ways of misinterpreting your directions.

There’s a lot wrong with this analogy. In fact, it’s mostly random horseshit. This isn’t what graphics programming is like at all. However, the two scenarios are similar in levels of intensity and insanity. This isn’t what it’s like to write a rendering engine, but this is what it feels like.

The graphics hardware is ridiculously fast, but also sort of limited and difficult to use. It’s very easy to slip up and waste a lot of the available power.

Shamus, the vast majority of programmers don’t need to interface with the hardware like this. Most of us use graphics engines or game engines provided by someone else, and those other programmers have solved a lot of problems for us.

It’s true! A lot of problems are indeed solved. Mostly. But I’ve never heard of a AAA game that was able to use a general game engine right out of the box. Every time I listen to a GDC talk from a programmer, at some point they’ll allude to all the work they had to do customizing the engine to suit their game. Maybe the engine doesn’t support open-world gameplay, or streaming content, or procgen environments, or realtime modifications to level geometry, or some other thing. if you’re doing anything remotely unconventional with your rendering then you’ll probably need to crawl down into the guts of the rendering pipeline and make changes. That puts you on that crazy road trip with the NVIDIA rocket car, so you’d better know what you’re doing beforehand.


Link (YouTube)

John Carmack once claimed that rocket science was “simple” compared to graphics programming. He even had the audacity to say this to a bunch of NASA engineers. And to be clear: He might be the only living person to have done both of these things professionally.

Because of this dance between the various bits of hardware, even a small bottleneck can lead to serious performance costs. Despite the massive gains we’ve made in raw processing power over the years, we still haven’t reached the point where we can afford to sacrifice a few extra cycles in exchange for making life easier for our poor programmers.

You could argue that maybe we should stop pushing the hardware and just settle for the graphics of yesteryear, which would give us 90% of the visuals for a fraction of the computing cost. That’s a worthwhile idea to explore, but now we’re talking less about a technology problem and more about a business / cultural problem: How do we convince the suits to stop wasting so much time and money chasing graphical fidelity? I mean, if we can’t even disabuse them of obviously stupid ideas, then what are the odds we can change their minds on less obvious ones like this? The people signing paychecks want MOAR GRAFIX, and that’s the reality engineers are living in.

Even if the industry did somehow lose interest in chasing the shiniest pixels, I’m not sure it would help. VR is forever hungry for framerate and consumers really do want 60FPS. Moreover, console makers are always trying to limit the cost of their machines. If we started asking less from our hardware, then Sony and Microsoft would probably be less aggressive about stuffing their gaming machines full of power in order to cut costsEither to lose less money on each unit sold, or so they could lower the retail price to undercut their rival.. In that case, we’d be right up against the wall again – we’d have just barely enough power to do what we need and CPU cycles would still be precious.

4. Design Through Programming

The Witness. There are a lot of features in this game that wouldn't be supported by standard AAA engines. By necessity, idiosyncratic games will require some custom engine work to bring them to market. Which means you can't just treat a game engine like a black box and the leave 'engine work' to experts.
The Witness. There are a lot of features in this game that wouldn't be supported by standard AAA engines. By necessity, idiosyncratic games will require some custom engine work to bring them to market. Which means you can't just treat a game engine like a black box and the leave 'engine work' to experts.

A bank is going to start with an incredibly detailed and rigorous specification, and the project involves turning that inflexible spec into code. No financial system is going to set their engineers loose with a directive like, “Just play around a bit and see what design gives the most correct results.” In many fields, you know exactly what you want before you write your first line of code.

In video games the spec is more fluid and open to changes in response to playtesting. Jon Blow refers to this as “Design through programming”, which is a common approach when your lead designer and lead programmer are the same person.

Obviously this varies by genre and studio. If a game is following a well-understood design pattern like “Cinematic first person shooter” then the spec will probably be fairly stable. On the other hand if you’re doing something weird and unconventional like Portal, Miegakure, Manifold Garden, Gravity Rush, and Antichamber, then there is no firm spec. You have to experiment to see what’s possible and what’s fun. Likewise if you’re making something full of complex interacting systems like Dishonored, Prey 2017 or Deus Ex, then you’re probably going to have to iterate a lot to get all the systems working in harmony.

In either case, if you’re trying to design a game during the process of building it, then refactoring code becomes a major concern. You thought you’d only have a couple of bad guys in the scene at a time when you started, but now that the gameplay is taking shape you realize you need dozens. The animators really made a lot of fantastic animations and blending those all together is expensive, so now you have performance problems you couldn’t have foreseen at the start.

5. Extra Bonus Challenge

You wanted the hardware specs? Here. These are the hardware specs.
You wanted the hardware specs? Here. These are the hardware specs.

I know I only said there were four challenges but I wanted to sneak this one in. It’s a sort of extra layer of challenge on top of all the others that you need to do all that crap using whatever random, cobbled-together heaps of parts people have sitting on their desks. There are hundreds of oh-so-slightly different variants of graphics cards, several generations of chipsets with different features, many different processors with different numbers of cores, different memory pools and different versions of Windows devouring some portion of all those resources. Some people overclock, other people don’t have all the latest updates, and everyone has different usability needs.

Also, did I mention the consoles? They’re more like PCs than they’ve ever been before, but they’ve still got a lot of unique quirks you’ll need to account for.

It’s true that this particular challenge faces anyone making desktop apps, but it all becomes that much more complex when you combine it with the other four challenges above.

Good Luck!


Link (YouTube)

So this is the challenge of game development: We need to make programs that handle tons of complicated state change, we need them to run consistently fast, we need to do so using the complex and tightly constrained framework that exists between the CPU and the graphics hardware, we need to do it in a situation where the spec is fluid and refactoring is common, and we need to get it running on most of the endless permutations of consumer-grade hardware. These aren’t the only challenges of game programming, but I think these are the big ones. These four-ish challenges are interconnected and they tend to exacerbate each other. These things are so important and so universal that I think it could make sense to devise a language specifically to help with these problems.

Again, I’m not promising that Jai is going to magically make everything better. I’m just saying that I find it plausible that this particular collection of problems could be better solved using a language specifically designed for the job.

 

Footnotes:

[1] Reminder: By “serious” I mean a proper 3D simulation and not something programmatically trivial like a side-scroller.

[2] We do have commercial 3D games made with Unity, and thus C#. But for the most part I think everyone gets why C# isn’t a viable alternative for AAA game development.

[3] Or openly despise and denounce.

[4] Sealing one chunk of code off from other systems.

[5] In this context, “offline rendering” just means “not realtime”. Like, you start the render process and then go have lunch.

[6] Or even go unresponsive for multiple consecutive seconds!

[7] Hitman does REALLY well with this, but I imagine the team has to put in a lot of work to make the huge crowds possible. If we had more power, it would be easier to pull this off and more game designers would want to use it.

[8] Either to lose less money on each unit sold, or so they could lower the retail price to undercut their rival.



From The Archives:
 

84 thoughts on “Game Programming Vexations Part 2: The Four Challenges of Game Development

  1. Asdasd says:

    Whole hoo-hah’s on the boo-bah, chief.

    1. evileeyore says:

      Front page posting for teh win!

    2. Ander says:

      It just feels like home

      1. Typo Police says:

        Typo! “It feels just like home.” would be correct.

        1. Syal says:

          Nope! Would have to be:

          “It feels… just. Like home.”

        2. Decius says:

          It just feels like home. It doesn’t look or sound or smell like home.

  2. Daimbert says:

    This is one of the reasons AAA game devs shy away from[3] strict Object Oriented Programming. OOP encourages you to wall off all of your different objects. A bank transaction should not be able to change the name of the customer, and so on.

    At the risk of being “that guy”, this isn’t actually true. Encapsulation does not mean that other objects can’t change the states of an object, just that they can’t do so DIRECTLY. They always have to ask the object to do it for them. So, in this case, there’s nothing in OO rules that says that a bank transaction can’t, as a side effect, change the name of the customer. It only can’t do so by reaching into the instance of the account and setting the “customerName” instance variable itself. It would have to call something like “setCustomerName” or, more reasonably for the sorts of cases you’re talking about, call the “changeCustomerName” method.

    This sort of thing is, in fact, what a system with a lot of varied and strange events that can impact a lot of different objects wants to do. First, you don’t want to have to have all of these varied objects actually have to know what the actual logic of changing a customer name is. Not only does this add complexity to them, but it also is a royal pain if you ever have to change that logic. The more places where you might want to change a customer name, the more you want to encapsulate it into an object. Second, OO allows for polymorphism, where anyone who wants to do something can simply call a method on all the objects it can reach and let them figure out what it means. So, to use your explosion example, if you defined at your highest level — potentially even at “Object” — a method called “dealWithExplosion” then all you’d need to do is call that on any relevant object and then go on with your life, letting them figure out what it means to them. You would have to add in all possible relevant information you have — or at least have that available to the object — but ultimately each object decides what that means to them. So if you have objects that don’t explode and never take explosion damage, you can simply have it do nothing when it gets told to deal with an explosion. Third, if you have many objects doing this all at once, you’re going to have to deal with synchronization, so that you don’t end up with negative HP because the heal happened while the damage was happening and everything got screwed up. If you don’t encapsulate the things that need to be synched in an object, then each caller has to remember to do it and release it when done, whereas in an object you can do it all internally without anyone else having to bother with it. Fourth, if you want to have mixed lists of various objects having them encapsulated as per the polymorophism above is the most efficient way, as you can group them together and call the base method on all of them without having to check to see if they support the method or not. Finally, OO here makes bug fixing easier, because while you can still have many, many objects changing the state they all have to go through the class’ state changing methods, meaning you can simply put logs there telling you what’s happening and where it gets screwed up and then trace it out to the caller, whereas without encapsulation you have to find any instance where anything changes at least that state variable to see if it could be causing the issue.

    The way massive events tends to be handle in Java is that when something happens to an object it generates an event, passes it up to some sort of EventDispatcher, which then distributes it to any object that cares about that event by registering itself as a Listener. This is relatively clean, but does have a bit of an issue with performance (especially since in the programs I’ve worked with a lot of the time you get duplicate events that all need to be processed). But that’s the general idea for handling these sorts of asynchronous events: generate an event and tell everyone who cares to deal with it themselves, generating events as necessary. Doing that is SO much easier if things are properly encapsulated.

    1. Bloodsquirrel says:

      The problem is that the player just bought a perk that adds a new rule to explosions where they gain health based on how far the explosion tosses an enemy (but only if they’re humanoid), but the explosion only tosses wooden objects half as far. So now you have to either:

      A) Distribute this logic amongst the various HandleExplosion methods, which is messy
      B) Come up with a system for adding “Rules” to explosions which HandleExplosion methods have to process, which gets into quasi-scripting territory
      C) Throw in a bunch of arguments and parameters to the HandleExplosion method to give the event raiser more control over how it gets processed, which largely breaks the point of having each object process it itself

      And then you have to make it so that each HandleExplosion method reports back to the even raiser. And then make sure that the event raiser is reporting back to the character, in case it was the weapon, and not the character, raised the event. And then you have to make sure that all of this data that’s being generated, stored, and passed around isn’t being done wastefully, since each HandleExplosion method doesn’t specifically know what results which other object needs, and is just broadcasting the world to them.

      Also, OO is more than just making sure that you have to call an object’s ChangeName(String newName) method in order to change its name. Deciding what data in an object can be changed directly, what is immutable, and what can only be changed through other operations is a big part of design. Changing a customer’s name, for example, should basically never happen, at least from the Customer object’s perspective. If the customer is changing his name, then that should be a separate part of the program that makes the change directly to the database. The Customer object should always be pulling its name from the database when its generated.

      Actually, the Customer object should probably be almost completely immutable. Changes to anything important need to be done directly to the database. You’d never want to change the Customer object’s account balance and just assume that it’ll get passed along properly at some point. Hell, you probably don’t want Customer storing the account balance at all, because what if some other system changes his account balance (ie, an electronic check sent by his employer clears while he’s withdrawing money from an ATM)?

      The point is that OO isn’t just about wrapping all of your internal data in getter/setter methods. It’s about deciding in what fundamental ways other objects are allowed to alter a class- can they set an object’s location if they want, or can they only push/pull the object with a given amount of force? The latter might seem more reasonable when you thought that all you were going to have is some physics simulations, but then someone comes in and decides that you should be able to teleport an object.

      1. Daimbert says:

        So now you have to either:

        A) Distribute this logic amongst the various HandleExplosion methods, which is messy
        B) Come up with a system for adding “Rules” to explosions which HandleExplosion methods have to process, which gets into quasi-scripting territory
        C) Throw in a bunch of arguments and parameters to the HandleExplosion method to give the event raiser more control over how it gets processed, which largely breaks the point of having each object process it itself.

        Or create a Perk class and get IT to handle the explosion and updating all of the appropriate other classes.

        I’ll agree that OO tends to require more design time to avoid making something that’s a complete and total mess that’s hard to rework (although often it’s not as difficult as it might seem at first). But even in this case, the alternative isn’t much cleaner, unless you just have one huge whomping function that does everything to everyone. As soon as you break it down even into different threads or functions, you run into the problem of figuring out who should update what where, and usually in those cases it’s always multiple cases. At least in the OO case you could change the Player’s handleExplosion case to check if it relates to the perk and look up the appropriate information if it needs it. Although, yeah, there’s some performance hits in this.

        The point is that OO isn’t just about wrapping all of your internal data in getter/setter methods. It’s about deciding in what fundamental ways other objects are allowed to alter a class- can they set an object’s location if they want, or can they only push/pull the object with a given amount of force? The latter might seem more reasonable when you thought that all you were going to have is some physics simulations, but then someone comes in and decides that you should be able to teleport an object.

        Absolutely. But the key is that if you do have to change that sort of thing, it only has to be done in the object itself, and only for those cases where it makes sense. So, for example, for the teleporting case you can add a functionality to do that, leave all other cases alone — so, for example, having a case where any attempt to move from a bullet hit gets flagged as an error remains the same — and then you only have to figure out what operations in what cases trigger teleport. It’s a lot easier than trying to add teleport functionality by going through every operation that might do it and ensuring that it does it correctly.

        This might stop some unexpected cool causal chains, but the tradeoff is when those do stupid things it’s also a lot more difficult to debug as well.

        To be clear, I’m not saying that OO is the solution for these sorts of games in general. There is more overhead in OO and that might be enough to kill it. But the massive causal chains idea is exactly what OO encapsulation is meant to deal with, since all anyone has to do is call the functionality on the object and then they can mostly forget about it and don’t have to know what it all means, whereas without encapsulation everyone needs to know the logic of the operations to make it work out.

    2. Geebs says:

      That sort of event dispatching paradigm doesn’t really work when you have to put the entire current state of your program into a buffer right now because it’s time to render a frame.

      OOP isn’t bad for real time rendering because of encapsulation, but because of all of the stuff that enables encapsulation.

      1. Daimbert says:

        Yeah, if you need to generate one large global structure of data in a form that a renderer can understand, then encapsulation makes things worse because to generate it from a bunch of objects takes time. It’s doable, but definitely adds overhead.

        I didn’t take that as Shamus’ objection, though, which was more about multiple parts having to change other parts at odd times. For those sorts of cases, encapsulation is really what you want. And the issue with events in the Java idiom is that they’re slow.

      2. Dan Efran says:

        This is one way a new language could potentially help. OOP/encapsulation is a great way to think about interactions in your code, and so expressing game logic in a java-like style helps you reason about it and create a robust design. But for speed you want the implementation to be more about buffers of similar data.

        For example, OOP gives you:
        Mook1: position, health, speed, …
        Mook2: position, health, speed, …

        But a game engine, to use the hardware efficiently, should instead be set up like:
        Positions: mook1, mook2, …
        Healths: mook1, mook2, …
        Speeds: mook1, mook2, …

        A language that would automatically translate from the OOP formulation to the anti-encapsulated buffers implementation would definitely help game developers.

        (In practice, the “entity/component/system” pattern is a good compromise architecture, providing most of the benefits of both schemes. It’s currently very popular in game development.)

        1. Paul Spooner says:

          Yes! JB talks about this as AOS (Array of Structs) vs SOA (Struct of Arrays) memory arrangement in his Jai series. It’s one of the language features that made me say “Yeah, that’s good. Wait! No other languages allow you to do this?”

          1. Olivier FAURE says:

            Obligatory plug: D has some libraries that help you do Struct of Arrays.

            (that said, straightforward Struct of Arrays isn’t quite what you want either, because then every element in your collection has the same field, whereas game developers usually want an ECS-style workflow with a big collection of entities that get to pick and choose what components they have)

          2. Richard says:

            C++ does let you do that, but it does not encourage you to do that and in schools people are explicitly taught not to do that.

            In a game, most of the logical “Objects” are more-or-less ‘behaviours’ that an entity could have, rather than the entities themselves.

            The entities are instead built by composition and not inheritance, and often are just a list of pointers to the objects composing them.
            So the “mook” entity has a list that’s pointers to the “model”, “weapon”, “armour”, “AI type”, “physics properties” etc

            The renderer owns the actual models and textures, the mook simply acts as the link between the various components. It might not even have any actual code of its own other than generically looking after the set of pointers.

            So the renderer can drive straight down the model array and spew it to the screen, the AI engine drives straight down the AI state array, the physics engines down the physics one etc.
            With any luck the user has multiple real CPU cores so multiple engines can be operating simultaneously.

            This is exactly the opposite of OOP as taught in schools, so it generally takes a fair bit of unlearning what a logical “Object” looks like – that mook there ain’t an object, it’s a collection.

            Commercial game engine APIs try to encourage this type of optimization, but they won’t stop you – or even warn you – if you don’t.

            1. Paul Spooner says:

              Well, sure, implementing one or the other is possible in any language with granular memory management. The thing that shocked me is that Jai seems to be the only language that will let you easily change from one to the other.

              1. Richard says:

                The reason is that it’s effectively impossible for a language to do that and remain appropriate for a AAA game.

                Changing memory layout can break the program in ways that range from the very obvious (instant crash due to very wrong data) or extremely hard to even detect until something goes strange many hours of gameplay later (passing the wrong buffer or variable).

                Calling any external library (eg the OS, Direct X, OpenGL, Vulkan etc) requires that the data you’re passing is laid out in exactly the way that library expects, and the data you get back will be laid out in exactly the way that library does it.

                So it’s only (theoretically) possible to do ‘automatically’ in contexts where the programmer has no control over the memory layout and management at all, and can only offer ‘hints’.

                Thus it’s only possible for a langauge (or a library) to offer this by setting specific, limited contexts where the programmer does not have control over memory management and layout, and exposing ways to copy data from ‘external’ to these ‘internal’ places. Eg C#, Java et al.
                That act of requiring a copy may of course defeat the entire point…

                The “industry standard” approach is for your Magical Mystery Engine (Unity, Unreal etc) to take over memory management for the ‘game objects’, and the programmer to retain full control outside of that domain.

                1. Paul Spooner says:

                  Doesn’t seem impossible to me. I didn’t say “automatically”, I said “easily”, though it seems like it wouldn’t be too difficult to rig up an auto-memory-layout-optimization system in Jai.
                  Here is Jon talking about AOS and SOA data layout: https://www.youtube.com/watch?v=ZHqFrNyLlpA&list=PLmV5I2fxaiCKfxMBrNsU1kgKJXD3PkyxO&index=7&t=0s
                  Seems like he has both control over memory layout and easy layout alteration. Doesn’t seem impossible to him either.

      3. Moss says:

        Geebs, can you elaborate on what enables encapsulation?

        1. Geebs says:

          Bear in mind I’m only an enthusiast who dabbles in 3D, not a real programmer.

          Briefly, you’re writing a class (I’m mainly experienced in ObjC, so usually subclassing the Object class, or a Dictionary) when you could be using a struct. There’s a bigger memory overhead since the class has a whole bunch of other functions which enable memory handling, inclusion in sets / arrays, etc. Enumerating arrays of objects is often slower than arrays of types. Objects are stored in memory where the language vendor wants them, not the programmer, and keeping things in cache can make a big difference to a realtime 3D application. You’re changing state by telling objects to talk to each other (slow) rather than just running an array through a function (fast).

          Also GPUs are about as far away from OOP as you can possibly get, and you’re going to have to talk to them at some point, too. When you’re going to have to sling blocks of raw memory about every single frame anyway, all of that extra overhead introduced by OOP seems a bit unnecessary.

          1. Daimbert says:

            Yeah, classes are basically complicated structs, but the idea of encapsulation is that you don’t ever have one big struct containing all the data, and segment it as much as you can into tinier and tinier structs. So if you need to get a set of all the data for the game, you have to iterate over all objects — which could be anywhere in memory — and ask them for all their state data and add it to a struct to pass to the GPU. If you aren’t doing OO, you could literally build a global struct of the type the GPU wants you to pass and simply maintain that, so that when you need to tell the GPU something you can literally just give it that structure that you’re going to need to build anyway. So there’s a performance hit there. And as mentioned objects always contain functions and non-state information, and so they would actually take up more memory than a simple global struct would. All of that is indeed overhead that’s there just to allow data to be properly encapsulated.

            1. Richard says:

              Actually they take up exactly the same amount of memory that the equivalent struct plus code-to-use-it would.

              An “Object” is all the functions (actual lines of code) for the thing, stored exactly once somewhere in the EXE file, along with the “struct” (layout of the variables, like the XYZ position, hair color etc)

              When you “Construct” it, the program allocates a block of memory big enough for the whole “struct” and calls the special function(s) to set it all up appropriately.

              The potential performance problem arises because RAM is very slow.

              Daft car analogy:
              Your PC is pretty big. The CPU is in New York, the RAM is in Teaxs, the GPU is in Las Vegas and the hard disk is in Africa. And the Internet is on the Moon.

              Everything travels on a Big Truck. Luckily these trucks can be both cloned and utterly destroyed really easily.

              Texas is big, loads of room. Africa’s simply huge. But they’re both a long way from the CPU so it takes ages to drive a truck there and back.

              New York is pretty busy, so there’s not much room to park many trucks. Maybe a hundred trucks at most (L1 cache)
              So you build some truck parks in Philadelphia and Baltimore (L2 and L3 cache) that can each take a few thousand trucks.

              Whenever the CPU needs something, it has a look in New York to see if it’s already there. If it isn’t, it asks Philly and Baltimore to send the truck with the little piece of data it needs.

              If they’ve got it, they don’t unpack the truck – they clone it and send the whole thing.
              When it arrives, one of the trucks currently in New York is vaporized to make room.

              If they don’t have it, they ask Texas to send the right truck to them.
              Texas doesn’t bother sending just one truck though, it clone and sends a whole bunch of trucks around the one that was asked for, just in case they’re needed later. Again, some Philly/Baltimore trucks are destroyed to make room.

              Sometimes Texas doesn’t have the right truck.
              When that happens, a few thousand trucks are shipped to Africa and Africa is asked to send a ship full of trucks to replace them.

              When the CPU has finished changing what’s on one of the trucks in New York, it sends Philly/Baltimore and Texas clones of this truck, and they vaporise their version and replace it with this one.

              So the problem is – what can you fit on one truck?
              If each object has the whole state of Mook, maybe a truck only has one mook!

              The CPU wants to move all the mooks. So it gets the truck for Mook A, updates the position, sends Mook A back.
              Then Mook B etc…

              That’s a lot of trucks, when the CPU only wants three numbers from each.

              It’d be better if you could stuff all the mook positions on a single truck, as then they’d all get to New York together!

              Meanwhile, Texas has been told to send the GPU in Vegas a thousand trucks. But that’s a tale for another day.

              (In C++ “class” and “struct” are actually exactly the same, except that in one everything is private by default, and in the other everything is public by default)

  3. Dev Null says:

    Everyone will feel the need to defend the honor of their favorite language.

    I’ve always found this sort of language proselytizing weird. I’m not disputing that it’s very very real; just weird. It’s like walking into a hardware store and having to listen to two guys argue about which is better: screwdrivers or hammers.

    1. Liessa says:

      Honestly I could totally see that happening. People will argue about anything if they’re in the mood for it.

    2. Arkady English says:

      ARGUMENT ABOUT HOW TO STICK TWO BITS OF WOOD TOGETHER

      Hamilton Murr (HM): Screwdrivers suck, because they don’t work with my nails!

      Sue Diver (SD): You’re meant to use screws, fool. What did you expect.

      HM: I hate using screws. The thread makes them almost impossible to get in with my hammer.

      SD: Why would you use nails anyway? Once in they’re almost impossible to get out again. With a screwdriver you just run it other way.

      HM: So what? Just make sure you build it right in the first place. Besides, my hammer works with any type of nail, but you need compatible screwdrivers for each kind of screw.

      SD: It’s easy to change tips. And it means we can use security screws to detect tampering. I also love how easy it is to use a screwdriver. I don’t need to make stupid amounts of noise, or build upper body strength.

      HM: Screwdrivers must therefore be for weaklings who can’t manage a hammer – the true tool of a builder.

      Gerald Lui (GL): Excuse me guys, can you let me through? You’re standing right in front of the wood glue I use to stick two bits of wood together.

      [SD and HM turn to glare at GL. They speak simultaneously.]

      HM & SD: First of all …

      [Smash cut to black]

      1. Lino says:

        Daniel Tompson (DT): Hey, guys! Do you know where I can find some duct tape? There’s these two pieces of wood I really need to stick together!

        1. Moss says:

          Gerard Clarksson (GC): Hey guys, I found these clamps I don’t know what to do with.

          1. Lino says:

            DT: Well, you can throw them in that trash can over there, since everyone knows the best way of sticking wood together is with duct tape – it’s fast, efficient, doesn’t leave a mess behind, you can remove it easily at any time, and at the end you’re left with a roll of perfectly good duct tape – a tool that’s so versatile, there are even memes about how you can use it for anything!

            1. Decius says:

              Hey guys, what kind of welding machine do I need to join mahogany and balsa wood?

      2. SiriKeet says:

        Why would you even bother with all those extraneous “screws” and “nails”. Everyone should just use japanese wood joinery.

        1. Lino says:

          DT: Don’t buy into the fad, man! Sure, everybody’s talking about it now, but in a year or two people will move on to the next new shiny thing, and no one will be using wood joinery anymore. Have some foresight and switch to duct tape!

        2. Ander says:

          My company tried Silverlight instead of Javascript, too. It’s still a sore spot.

      3. Ed Weatherup says:

        HM = ANSI C
        SD = C++
        GL = Java
        DT = JavaScript
        GC = Lua

        SiriKeet’s unnamed programmer … Haskell?

    3. Ander says:

      It’s not a flattering answer, but I think this article has a reasonable answer that is at least part of the cause. A professional programmer needs their current method of doing things to be viable.
      “Within 18 months your skills could be, if not quite valueless, suspect. … Programmers are often angry because they’re often scared.”

      1. Kylroy says:

        I worked in (US) individual insurance administration from 2002 to 2014. The passing of the ACA in 2010 rendered large swathes of my regulatory knowledge completely moot – it’s not a coincidence that I left the field as it began to be implemented.

        And it’s not like i particularly *studied* to get that knowledge, or that I didn’t have years of advance warning, or that I didn’t have other skills that could readily transfer elsewhere (office admin is similar in a lot of fields). A programmer can only maybe hope for some advance warning, or else start learning a new programming language.

        1. Daimbert says:

          When the old product I was on died there were issues with people thinking that I only knew the older languages and not the new one that people were working on. Fortunately, my current manager took a chance and it turns out that I was able to pick that language up relatively quickly.

          Unfortunately, I didn’t know of a group using Python at the time because I’m actually included on an academic paper for software written in Python …

  4. Joshua says:

    Not much to say, but another great article, and I had to avoid laughing too loud at the office when seeing the picture of the excited driver and her terrified passenger.

    1. Nimrandir says:

      Speaking of the picture . . . the parking brake is engaged, isn’t it? That poor car is going to be absolutely shredded.

  5. Lino says:

    I really liked the article – I’m already feeling sad for when this series is going to end! And we’re only two parts in!

  6. Baron Tanks says:

    Great article Shamus. I feel like it would also make for a great video series :) But I think your Vulkan video just put me in that mindset.

  7. mockware says:

    “In many fields, you know exactly what you want before you write your first line of code.”

    Been programming for 30 years and i have yet to see initial requirements not get run through a blender after you start coding. People are very good at telling you what they don’t want but can’t tell you what they do want.

    1. Cubic says:

      “I don’t want THAT!” (and so now we have Agile)

    2. Paul Spooner says:

      What’s your sub-discipline, if you don’t mind me asking?

    3. Daimbert says:

      Our process started to include having a designer sit down and write out in detail the requirements, translating what the business guys/customers were asking for into something that made sense and everyone agreed with. There were obviously still changes, but it worked out pretty well. Although this was required for and helped by the fact that we were doing this to support equipment where things were more constrained and we all definitely needed to know what each side was going to try to do.

  8. Alberek says:

    That Manifold Garden looks pretty interesting…
    At first I though it was from the guy that made Antichamber because they look pretty similar…

  9. Cubic says:

    OK OK but what are the features to support developing next generation cut scenes?

  10. Jabberwok says:

    “X is a stupid feature. I’ve been making web and mobile apps for a decade and I’ve never needed anything like that.”

    I am not a programmer at all, but I’ve spent enough time prowling tech support forums to recognize this attitude coming up over and over again. Or, in its simpler form, it’s usually just:
    “I want to do X.”
    “I don’t understand why you would want to do X, so I’m not going to help you.”

    1. Paul Spooner says:

      Perhaps a bit more charitably, the response might be summarized as:
      “I too once wanted to do X, but eventually I discovered that Y was a better solution for me, so you should probably try Y.”
      Which, if you really do need to do X, amounts to the same thing, but at least it’s nominally motivated by helpfulness rather than stubborn ignorance.

      1. Jabberwok says:

        True. And I’ve no doubt interpreted this stuff through my own filter. Though I do think that sometimes people try to force others to their way of thinking without understanding the context of the way the person asking for help works, or the things that they need. Or even the restrictions they might be working under. Just as a very simple example, trying to explain my issues with automatically updating software and online DRM to people who have never dealt with poor/unstable Internet connections is often like trying to explain drowning to a fish.

    2. Retsam says:

      The flip side of this is the XY Problem. People ask “How do I do X?” when they mean “How do I use X to do Y?”, when they really should be asking “How do I do Y?”. It’s a common enough problem that StackOverflow has a FAQ entry for it (and the programming Discord I’m on has a chat macro for explaining it to someone).

      For example the FAQ entry has an IRC conversation where someone asks “How do I get a string between two delimiters” (X), but what they meant was “How do I get a string between two delimiters in order to parse a JSON Object” (X to do Y), but they really should have asked “How do I parse a JSON Object” (Y). There’s a simple answer to parsing JSON, and it doesn’t require a function to get a string between two delimiters.

      You can of course just ignore XY problems and just answer questions as they’re asked, without pushing back for more info but this isn’t ideal for either party: the question answerer often ends up explaining a much more complicated solution for X, when a simpler solution for Y exists, or else gets hit with a follow-up question that clarifies the original question and makes it clear that the “X” solution wasn’t the right choice and so their time ends up wasted because of how the question was asked. And the question asker ends up with a sub-par solution.

      Having someone push back when you just want an answer to your damn question, thank-you-very-much, can be frustrating, and of course, not everything that seems like an XY problem is one, but often a bit of push-back is the path to getting a better answer.

      The truth is, people are bad at answering questions on the internet, but if you’ve ever spent time answering questions on the internet, people are bad at asking questions on the internet, too. The only “solution” is patience and good-faith on both sides, and that’s always in short supply.

      (Though to be clear, Shamus’s original example is not an XY problem)

      1. Kylroy says:

        I’ve always said that in any level of IT, whatever the necessary qualifications to actually perform the job, 90% of your actual effort will be spent turning “Thing no work!” into an answerable question.

  11. Lee says:

    He might be the only living person to have done both of these things professionally.

    Did you have someone else in mind that made the “living” qualifier necessary here? ;)

    1. Decius says:

      There are an uncountable number of people who have not yet been born.

  12. Olivier FAURE says:

    Rather than try to convince you that alternative languages are insufficient, I’m going to try to sell you on the more general idea that – all other things being equal – a language devised specifically for games would be better than any of those other choices.

    Wait, wouldn’t a new language also suffer from having tons of people complaining about it lacking features? This seems like a good example of your earlier metaphor of creating a language being like adopting a puppy: it’s really easy to see the reason why you’d want to do it, but it’s also really hard to see how much responsibility it entails.

    And, I mean, one only has to look at the issue count on Godot’s Github page to see how huge an undertaking this would be.

    (as an aside, Godot’s own scripting language is just good enough for me to tolerate it, and yet it lacks a bunch of features that make me wish they’d switch to Javascript or WebAssembly).

    1. Richard says:

      Creating a programming language is not difficult.

      Creating a toolchain for that langauge is hard. Creating an efficient toolchain is very hard.

      Convincing anybody else to actually use it is very nearly impossible – the learning curve is a cliff, and management would rather like you to produce something right now, please, or development of this game is going to enter crunch mode rather quickly. Or the company is going to fold.

      “Go” is barely used at all (outside of Google), despite being extremely good in the niche of “massively-parallel processing” and having the might of Google behind it.

      Which is why Jai is almost certainly never going to be used for AAA.
      But even if it never gets used for a single real project, the exercise of thinking about it will be interesting and the standards/maintenance committees of other languages will learn from it.
      And just perhaps, it might take off.

  13. Erik says:

    Hoo boy. This is the kind of article almost guaranteed to set me off, because I Have Opinions on this.

    The key insights here are two-fold. One is the necessity for a language that is optimized for event-driven programming, preferably with a model that supports clear state manipulation. The other is the necessity to support low-level memory management that allows optimized transfer of data to the graphics hardware, and allows as many operations as possible to be zero-copy, both preserving cache and reducing CPU bus bandwidth. The core issue is that no language I’m aware of does both well. Oddly, with my long background in machine vision, embedded systems, and engineering diagnostic UIs for them, I have extensive experience with both issues. :) My exact issues have been different from the gaming issues, but there is overlap (the Two Generals problem is something I need to deal with ALL THE TIME, communicating with secondary processors over unreliable links).

    The best language I’m aware of for programmer-efficient event-driven logic is actually Tcl, in which most of my scripts consist of setting up the world then surrendering control to the event loop. Virtually all activity is triggered by some event or other, and it’s amazingly efficient to write in. Unfortunately, it’s not performant on the level of C/C++ for general operations. It was the first language to use the model that Python, Lua, and many others have adopted: write all of your performing code in C, then have all the high-level logic in the scripting language. In theory, 98% of the code is in Tcl/Python/Lua, while 98% of CPU cycles are in the C libraries. This works fairly well for the rendering side, but not nearly as well for the event-driven side, where flurries of events need to be handled in the scripting environment.

    On the other side, nothing beats the C derivatives for efficient memory management (barring assembler, because sanity). The ability to directly manipulate memory pointers is incredibly powerful, and allows all sorts of tricks like zero-copy or SoA vs. AoS logic that one can play for maximal efficiency. But C is completely naive about events, and event management in C++ (and Java, and most intellectual descendants) is heavy-weight and not an optimized core feature of the language.

    So I can certainly see the desire for a good domain language for gaming, even as a reference language that other languages will steal ideas from. :) If I have time at the end of my current project, I may even get in touch with Blow and see if he needs help on Jai. It’s certainly an intriguing problem space to work in.

    1. Paul Spooner says:

      First off, thanks for weighing in! Good to hear the context.

      I don’t recall seeing anything specifically about event handling in the Jai talks, but if I’m not mistaken, it mostly has to do with running a series of very efficient tightly nested loops? The people I’ve talked to about “real time programming” always seem to boil it down to that, but I probably have an overly simple view of the problem.

  14. Retsam says:

    Personally, it seems like it would be a bad idea to co-opt the likes of Go, Rust, or D for game development. If you look, you can see these languages are “nearly” ready for game development. To date, nobody has shipped a serious game in any of these languages. Which means the tools aren’t there. Which means the main adherents of these languages are not game developers. At the same time, there must be a reason that game developers haven’t already embraced these languages. We have to allow for the possibility that these alternate languages are missing some features that game developers are likely to need.

    (Emphasis mine)

    I don’t think the reason that game developers haven’t embraced these languages needs to necessarily be flaws in the language. My read on the dominance of C++ is that it’s an industry-wide case of lock-in, that most everyone who’s making AAA games is already doing it in C++ and the switching costs are high enough that no one really wants to risk making the switch to a relatively untested language. Even if you think C++ is an “evil”, it’s the well-known, well-understood evil.

    There’s a fundamental catch-22, where “we aren’t going to switch to X language because the tooling isn’t there” and “the tooling isn’t there because nobody is willing to switch to X language”. This dynamic applies, regardless of how suitable X is to game development, and it’s a dynamic that Jai is going to face as well: if a single developer, even a talented one like Jon Blow, could single-handedly build all the tooling necessary for a language to facilitate a mass emigration from C++, the other languages wouldn’t be caught in this dynamic.

    Saying “the main adherents of this language aren’t developers” is largely just a restatement of the above – it doesn’t mean that those languages weren’t designed with game-dev use-cases in mind, (I’m less interested in the “adherents” to a language and more interested in the design goals) just that they haven’t yet overcome the inertia needed to become a mainstream industry choice.

    In the case of D and Rust, at least, their design goals are largely to be “C++ but better” (I’m not sure about Go, but I think it might qualify too), and game development is a major use case for C++, then it does seem that these languages were at least indirectly designed with the intent that they’d be used for game development.

    Of course, it’s possible that there are fundamental reasons why X language might be unsuited for game development: (mandatory garbage collection being an obvious example), but I don’t think “there must be a reason why game developers haven’t switched” is sufficient evidence. Personally, I’d be more interested a writeup on the the ways that languages like Rust, Go, or D are or aren’t suitable for game development than a focus on Jai, (which while being more laser-focused on game dev, also doesn’t really exist yet).

    1. Shamus says:

      ” I’d be more interested a writeup on the the ways that languages like Rust, Go, or D are or aren’t suitable for game development”

      There’s no way I could write that. It would require a massive investment of time to learn a language well enough to criticize it, because “You’re just a newbie what do you know?” is such a handy dismissal. Even covering one of the languages would require a big up-front investment, and I’d just get a bunch of hate mail for my trouble. Covering all three would be impossible.

      My point about Rust and Go not being used is that they haven’t been used even once. Like, of all the people who know and love these languages, we don’t have a single shipped game? (Or, maybe we do. I searched and didn’t find anything, but that doesn’t mean it doesn’t exist.)

      Zero shipped games means there are huge question marks and roadblocks that nobody has attempted to overcome. It’s easy to imagine, “Yeah, this language seems pretty much ready to go. I can see shipping a game with it.” But then you try and you realize how many things you’re missing and what a huge job it is to overcome them.

      Has nobody tried, or have people tried and run into obstacles they couldn’t overcome? I don’t know.

      1. Retsam says:

        My comment about being more interested in a write up of Rust/Go/D was more wistful than actual suggestion or criticism. I do think you could get some mileage out of writing up experiences with new languages (the Unity series had a bit of this with C#, but not a ton), but certainly it would take a ton of research to do any sort of comparative analysis.

        My point about Rust and Go not being used is that they haven’t been used even once. […] Zero shipped games means there are huge question marks and roadblocks that nobody has attempted to overcome.

        Is that true of Rust and Go, or is it true of all languages that aren’t C++? (If we treat Minecraft Java as an outlier) As far as I know, with how we’ve defined “games” for purpose of discussion it’s basically true for all languages. If it is true for all languages, then that means one of two things: either all languages other than C++ are critically deficient for game dev in some way, or there’s other factors that cause C++ to be overwhelmingly favored.

        It’s possible that C++ is just the only language suitable for game dev, as this series appears to be arguing (or at least strongly suggesting), but I find that hard to believe. C++ was not designed for game dev, there are other languages that feature manual memory management, and there are multiple languages targeting the exact use cases as C++. I find it unlikely that every other language has some critical landmine that makes it unsuitable or that Bjarne Stroustrup just hit the secret sauce when he decided to add object-oriented programming features to the C language in 1985.

        So I don’t think game programmers are using overwhelmingly C++ because it’s currently the only good tool for the job. (I also don’t think it’s because they’re “dum-dums”) I just think that there’s overwhelming institutional inertia. It’s both a tooling issue – the tools around C++ game dev have had literal decades of head-start over new languages – and also a workforce issue: everyone in game dev knows C++, a lot of people are hugely experienced in C++: the cost of retraining or rehiring your entire workforce is monumental. So the C++ monopoly is self-perpetuating.

        A new language other than C++ doesn’t have to just be better than C++, it has to be so much better that it’s worth the massive disruption it would take to switch to a different language. I think it’s quite likely that many languages, including some of the ones under discussion, (and including probably Jai), are better than C++ for game development. Whether they’re so much better that it’s worth the switching cost… well apparently no major game developer thinks so, at least.

        Maybe that’ll change as the languages mature a little more and tooling develops a bit more. Maybe not. But in any case, I think that says more about the state of the industry than the languages themselves per se.

        For another example of ‘overwhelming institutional inertia’ within our discipline: Javascript. It has a comparable monopoly to C++ in the web world, not because there’s anything particularly unique about the language, and not really for a lack of viable alternatives, either: almost any language can be compiled to JS, and there have even been a history of Jai-alikes: languages explicitly designed for the problems of web development: notably Dart, Elm, and Reason.

        This leads to a similar question as in this post: have all of those languages largely languished because they’re simply missing the “magic Javascript sauce”? Or because JS devs are “dum-dums who don’t understand the awesome features of [literally anything that isn’t JS]”?[1] I don’t think it’s any of these – a lot of languages are superior to JS in a lot of ways (though there’s no free lunch, of course), but just aren’t worth the switching costs for most people, and especially not larger teams/organizations.

        [1] A lot of people on the internet would un-ironically answer “yes” to this question. Assholes.

        1. Matthew Downie says:

          Game developers are mostly pretty relaxed about switching to C# for Unity projects. They’re willing to learn Javascript or Python for applications that use those.

          There’s an inertia factor, but I don’t think it’s that hard to overcome, as long as the new language (a) offers tangible benefits without major drawbacks, and (b) isn’t aggressively novel; a language will probably be easier for C++ coders to accept if familiar syntax like this still does what they expect:
          if (x != 0)
          {
          x = 1;
          }

        2. Decius says:

          Does Star Fox count as a game under the definition used? It’s an actual 3d simulation.

    2. Paul Spooner says:

      I’m not an expert either, but Jon Blow describes his objections starting at 5:30 in his first video on the topic.
      In summary (just copying text off his slides) the problems are:
      Go: Garbage Collection and concurrency model.
      D: Too similar to C++ to justify switching.
      Rust: Too much friction and safety-consciousness.

      The switching costs are, indeed, high. Perhaps the cost is high enough that even the perfect programming language for games wouldn’t be worth switching to at this point. But given that reality, it’s not really surprising that programming languages designed around other things (or worse, to be a general purpose multi-tool) aren’t good enough to justify the transition.

    3. Timothy Coish says:

      “Of course, it’s possible that there are fundamental reasons why X language might be unsuited for game development: (mandatory garbage collection being an obvious example), but I don’t think “there must be a reason why game developers haven’t switched” is sufficient evidence.”

      I think you have the answer staring you in the face my friend. There are language features for why X language is unsuited for game development. Garbage collection is one of them, but the main problem with a garbage collected language like C# is the lack of manual memory management. This has crippling performance implications, especially in multithreading.

      As for the latter, C++ is garbage, and everyone who writes code in C++ agrees. It’s a dumpster fire of a language. It has unbelievably long compile times, due to it’s late 60’s compilation model. Header files are stupid and an annoying time sink. The syntax is a mess. Templates make debugging compiler errors an exercise in futility. The modern features are totally slapped on, best example being Lambda functions. Because compile times are so slow you need build tools, which introduces more bugs. It just goes on and on.

      The fact that no one has switched away from C++ is extremely good evidence for these other languages being unsuitable. If they are suitable in some ways, then they have their own problems that make them a net negative. The lack of existing Game Dev tools is a much smaller issue than I think you’re making it.

  15. Nimrandir says:

    I don’t have much to say on the subject (my programming these days is either on a TI-84 or in a specialized Perl derivative), but the article is a fascinating read just the same.

    Also, after the video this week had the gall to use a House Analogy, I am relieved to see you haven’t forgotten your Terrible Car Analogy roots.

    1. pseudonym says:

      A ti-84? We used that in high school. I have fond memories of that thing. It uses some weird quirk of basic that “does assignment this way” -> A. Also not having named variables is a pain. But on the other hand it was nice to have a programmable thing with you all day.
      I used it to program stuff for tedious repetitive calculations. This was usually not time-saving, but a lot less tedious. Some students loaded games on them that were written in assembly. That ran crazy fast. And most students used the program area to store cheat sheets in plain text. So it was pretty useful as a students tool.

  16. Olivier FAURE says:

    By the way, to any OOP adepts out there: OOP is overrated, use functional programming instead. Fight me.

    To any function programming adepts out there: monads are overrated and worthless. Fight me.

    1. tmtvl says:

      What about logic programming?

      It’s so good that yes.

      1. Olivier FAURE says:

        Get out.

  17. Zaxares says:

    As somebody who knows next to nothing about programming, care to elaborate why C# isn’t a viable language for AAA games? You don’t need to go into much detail, I’m just curious as to what the answer is. :)

    1. Matthew Downie says:

      C# removes a lot of the hassle of C++. For example, it keeps track of all your memory allocations, and keeps count of how many things are using that memory (or at least, pointing to it). When nothing is pointing to the memory any more, automatic garbage collection kicks in and frees it up. In C++ you have to keep track of what needs freeing when.

      But C#’s memory tracking takes time.

      C# tries to stop you shooting yourself in the foot the way you can in C++. In C++ you can move memory pointers around and operate on whatever bits of memory they’re pointing at, and if it breaks something, that’s on you. C# wants to know exactly what you’re using the memory for before you can operate on it and will report if you try to do something unexpected.

      C++ allows you to carelessly overwrite random bits of memory, creating problems that are entirely unpredictable in their symptoms, so they can take ages to track down. But if you use direct memory access carefully, you can be a lot more efficient.

      Note that the distinction between the two isn’t quite as clear as that sounds – there have been smart pointers added to C++ that help track memory usage automatically, C# has an “unsafe” compiler option that allows you to do C++-like things, you can have C# code that calls C++ library code…

      1. Kyte says:

        You’re describing ref counting, which is not what C# does. C# uses mark-and-sweep: Stop the world, track every object down to its root, marking them as it goes, then get rid of everything left unmarked. The fundamental problem here is that it’s literally designed around creating hitches.

        But it doesn’t have to be that way. C# doesn’t dictate how exactly memory must be managed. Nothing in its language semantics does. If somebody were to take C# and pair it with a runtime that followed a different paradigm (like refcounting!), you could presumably use C# just fine (for this particular case).

        But programming a language runtime is hilariously expensive (perhaps more than actually figuring out syntax, semantics and writing a compiler). It’s effectively keeping the wallpaper and furniture and remaking the walls and floors.

        1. Olivier FAURE says:

          Refcounting is inefficient too.

          When you’re dealing with code that allocates thousands of objects per second, what you really want is arena allocation, where you group similar objects together in big contiguous memory chunks that are deallocated at the same time, and, more importantly, don’t tax the CPU caches too much.

          Problem is, once you do that, you lose most advantages of language-based memory management.

    2. Mistwraithe says:

      Performance. In the general case C# lags behind well written C++ code. Although the Unity C# burst compiler is intended to change that by making it rather easier to make reliably performant code in C# Unity than it is in C++ (where making the slightest mistake in understanding how the code will be compiled into machine code can sabotage your supposed high performance solution). The problem with that is that the burst compiler achieves this goal by limiting how you can code in ways which are likely to be too frustrating for many programmers. They are still working on it though, will be interesting to see where it goes…

      Personally I use Unity and C# instead of Unreal and C++. Various reasons, some of them good, but at core I also just don’t like C that much (which is a good reason for me but not a good general reason :-) )

  18. Misamoto says:

    Sooo… That new white aside thing (game developer vs Foo evangelist) messes up the whole page on mobile. Makes what’s inside it basically unreadable

    1. Nimrandir says:

      The issue has arisen before; one of the last Chainmail Bikini posts used the same construct, with a similar result on mobile devices.

      1. Misamoto says:

        Yep, but I was late to that party, and also I wonder if Shamus noticed the complaints

  19. Sven says:

    So the short version of this article is, you want a language that can do ECS really efficiently. ?

    1. Richard says:

      At the moment ECS is almost universally done in C++ (the guts of Unity are C++, Unreal and Godot are C++ etc).

      The goal is to make it easy to do it right and hard to do it wrong. C++ doesn’t help there, and C# actively encourages you not to use ECS at all.

      1. Olivier FAURE says:

        I think D is probably very good for coding ECS (powerful meta-programming, manual memory management), but I don’t know if anyone seriously tried.

  20. samuel222 says:

    This was very well put. I was sceptical about this series, but this was actually very well argued.

    However I am still critical of Blow. Smarter people than him have tried and failed to produce a programming language that is actually useful (Scala being the prime example).

    And since you noticed my constant negativity: I use this alias purely when I want to say something negative. I post under a different nickname otherwise.

  21. Lars says:

    A bank is going to start with an incredibly detailed and rigorous specification

    That made me laugh. It’s more like the “seven perpendicular lines” video. The only thing a bank does better is: “Not releasing a Software that isn’t rigorously tested.”
    But even that doesn’t prevent everything. I remember a software I tested that suddenly didn’t work for guys with names like IgOR or RolAND. :-)

  22. JjmaCXIII says:

    I had a lot of fun reading this post. Starting from *nonsensical engine-revving noises* where simply reading that compelled me to try making those noises myself, then up until the elaborately devised driver analogy which had me cracking up in its entirety. The Thanos joke got me briefly as well. Anyways, thanks Shamus, for a good read, especially the funny bits.

Thanks for joining the discussion. Be nice, don't post angry, and enjoy yourself. This is supposed to be fun. Your email address will not be published. Required fields are marked*

You can enclose spoilers in <strike> tags like so:
<strike>Darth Vader is Luke's father!</strike>

You can make things italics like this:
Can you imagine having Darth Vader as your <i>father</i>?

You can make things bold like this:
I'm <b>very</b> glad Darth Vader isn't my father.

You can make links like this:
I'm reading about <a href="http://en.wikipedia.org/wiki/Darth_Vader">Darth Vader</a> on Wikipedia!

You can quote someone like this:
Darth Vader said <blockquote>Luke, I am your father.</blockquote>

Leave a Reply

Your email address will not be published. Required fields are marked *