Functional Programming and Data-Oriented Design

These two complementary paradigms, rather than object-orientation, could be the future of game development.

Note: This article is a follow up to the previous article, ‘Why Functional Programming Works for Games’.

When we tell the story of functional programming and data-oriented design in games, we tell a story of trade-offs.

What do you get with Functional Programming in games?

  1. A declarative programming interface, such as Elm-style.
  2. Ease in reasoning about program behavior at a high level.
  3. The complete freedom of an open system.
  4. A great debugging experience.

And what do you pay to get it?

  1. Higher performance costs depending on how declarative you want to be.

Conversely, what do you get with Data-Oriented Design?

  1. Unrivaled speed compared to high-level programming paradigms.
  2. Ease in reasoning about program performance at a low level.

And what do you pay to get it?

  1. Be prohibited from using abstractive language features and techniques.
  2. Difficulty in reasoning about program behavior at a high level.
  3. A requirement to work in an exclusively closed system.

By looking at these trade-offs, we see that functional programming and data-orientated programming are quite orthogonal. But for games, that’s actually a great thing because orthogonal means complementary!

So how do we know which approach to use when writing games?

It’s all about knowing in what way your game needs to scale. For an example, let’s look at game Entities in the Nu Game Engine

In Nu, you have 3 tiers of Entity configurations out-of-the-box –

  1. Optimized (Imperative = true; IgnoreLayer = true; Omnipresent = true)
  2. Functional (the default configuration)
  3. Elm-style (use the EntityDispatcher<_, _, _> type)

Depending on the tier of the Entity, different numbers of them can be used before soaking the CPU –

  1. Optimized => 10,000* On-Screen
  2. Functional => 2,500* On-Screen
  3. Elm-Style => 1000* On-Screen

*These numbers are discovered by running Nu’s Metrics project with MULTITHREAD enabled

As you can see, you can get a lot of scalability if you’re willing to dial back some purity and declarativeness. And since most types of Entities won’t need scalability beyond the hundreds, you can stick with Elm-style Entities by default. If you need a bunch of bullets flying around the screen, you can use the default Functional configuration. If you’re going for bullets on the scale of a bullet-hell shooter, you can opt for the Optimized configuration.

What about bleeding edge games where you have certain game elements on screen that number in the multiple 10,000’s? Well, that’s where you have to eschew Nu’s normal path of programming and embrace the soon-to-be-available data-oriented style. This programming path will be provided in a future version of Nu (to enable this, the Transform type was recently separated out from the EntityState type and an Entity-Component-System has been prototyped in C++). Once this is put in place, Nu will enable a fourth tier of scalability –

4. Data-Oriented => Multiple 10,000’s On-Screen

In my view, game engines of the future will use a mixture of functional programming and data-oriented design. Object-orientation will take a back seat to these emerging paradigms, it having only succeeded in giving us the worst of both worlds — insufficient abstraction and inefficient performance.

By using both functional programming and data-oriented design in games, we will aim for the best of both worlds!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: