Reimagining my graphics engine (ncog.link devlog 5)

It’s been two weeks since my last update. In those two weeks, my age stat has leveled up, as has my marriage stat. Progress was a bit slow, partially due to those distractions, but also because I’ve been at a crossroads of figuring out what my game engine will contain.

First, I had the question: my graphics engine or scrap it and use something else? I have what I consider to be a good foundation of a nicely-designed 2D engine working, but what I was missing is some sort of layout system mostly for designing user interfaces. The other question that people often ask when you’re writing an engine is: what goals do you have for your engine that others don’t provide?

Those two questions became intertwined as I began pondering how to proceed. I honestly was stuck for close to a week. In the end, I’ve come up with this short list of reasons why Kludgine vs. another engine. The summary is that Kludgine is going to be opinionated about how games/apps are designed and will not attempt to be a one-size-fits-all engine.

Game design and app design are similar

There’s a mantra in game programming that patterns that work well for designing general-purpose applications don’t work in game programming, and instead, you should use insert industry-standard approach here.

One thing that I think that game design and app design have been converging on is composition of functionality rather than monolithic designs. The hip thing in app design is to build small components that you can compose together into more extensive functionality, and you wire them together through abstractions that help encourage test-ability and keeps cross-dependencies well-defined. I’m greatly simplifying the concepts, but in a way, the game industry’s message of, “Entity Component Systems (ECS) are the way to store data in games” is embracing the concept of tying chunks of functionality (components) together to form entities.

If we look at popular game engines like Godot and Unity, the Scene graph is a hierarchical set of nodes that are combinations of behaviors. Ultimately these two engines are what inspired me to take a step back and re-approach the design of Kludgine.

In the pull request I’m working on, I’ve attempted to create a component system that is similar to many view systems, including the Window trait being broken up so that a Window is a Component with a little extra functionality added on.

I’m still trying to figure out some of the details and how far I want to go in being opinionated vs. flexible. For example, the pull request currently doesn’t expose a general-purpose ECS, but all of the types your components interact with could easily be stored inside of an ECS of your picking. I like this flexibility, but I also like the idea of designing a form of composable Systems, which might encourage me to integrate and expose an ECS.

User Interfaces and Game Elements are the same

This statement is a bit more controversial in my head. As I started thinking about how to design UI in my engine, I ran into several examples of things that I felt cemented in a desire to try to embrace everything from the stance of being a component.

I ran across several games where the player’s sprite was animated inside of a dialog. The most common example is a character sheet layout. If I had utilized a 3rd party UI library such as iced or imgui, when I went to display the player’s sprite, I would need additional code to render it inside of the UI. I want a Sprite to be a Sprite no matter where you want to place it.

Another common example of that code reuse would be the ability to embed a Sprite into a Text element. Why would you do this? Well, think about Twitch or Discord chat, where you can use custom emotes in-line in chat messages. Or, think of many Nintendo games where names like Bowser are accompanied by a mini-graphic of his face.

It’s not just graphics though; it’s also text itself. I know I will be designing some sort of declarative-style animation system for Sprites to move around automatically. Still, there’s no reason that animations can’t be applied to text elements or even individual spans of text. Think about some of the games you’ve experienced where onomatopoeia-style words like SMACK are animated and shake and play a sound when presented on-screen.

The more I thought about it, the more I felt like all of the primitives I would want to make fluid user interfaces would ultimately help with other aspects of game programming.

Kludgine will come with keys-included

One metaphor for the differences between different styles of engines is how much functionality does the engine provide vs. what do you need to bring to the table? I want Kludgine in its standalone form factor to be what I reach for when I want to try a new idea or a new mini-game or a new game jam. I’d love to do game jams using Rust and Kludgine, and the only way to get there is to make it easy to use.

I’m going to be incorporating these features, at a minimum:

  • Audio: Through my libraries muse/amuse
  • Animations: Easy way to automate time-based changes into as many aspects of the engine as possible.
  • Multi-Window: Want write an app, not a game? No problem, just create multiple window types and open them as-desired with easy mechanisms to communicate between Windows.
  • Scripting support: Optionally, I want you to be able to write components in a scripting language. My eyes are on rhai, given my desire to have server-side execution of untrusted scripts in ncog.
  • User Interface: Full-featured user interface, including grids, scrollables, and basic form fields.
  • HID Support: Joystick and other control systems supported out of the box with a method for remapping as well as extracting “intents” so that your code isn’t checking for specific button presses but rather responding to rich events that could be generated via touch, keyboard, mouse, or other HID device.

I’m going to try to keep things as modular as possible so that you can disable functionality you aren’t using. Still, the goal is that by including all of these ideas into one project, all of the features can know about the other features and be designed to work well with them. I believe it’s this keys-included approach that will help make experimentation and iteration quick inside of Kludgine.

How does this impact ncog?

This update was mainly about Kludgine, but it has been inspired by thoughts of how I want ncog to work. As I’ve been working on Kludgine I’ve been inspired to keep ncog’s services designed for use in any game. For example, if you’re creating a single-player game, you could still utilize inventory systems or leaderboards without needing to embrace the multiplayer socket design.

This switches ncog from being purely a multiplayer game engine to being a service for “connected” games. However, the ultimate goal is still to provide an easy way to create multiplayer game experiences.

What’s next?

I’m continuing to work on the pull request to refactor and implement the UI components of Kludgine. My immediate list of things to tackle is a grid-based layout component, vector graphics, event handling (designing a callback system to handle rich component-created events like a Button’s action event), and finally, to spend some time refactoring and trying to start documenting the project.

After that, I’m not sure if I want to focus on getting an initial release of Kludging out to crates.io or if I want to switch back to Muse to continue maturing that stack and eventually incorporate it back into Kludgine. Part of the desire to bring Kludgine back out was to be able to use it to create UIs for Muse. One of the best ways to mature a framework is to eat your dog food, so it seems more likely that I will clean up the project a bit but then switch to Muse to start using the new functionality I’ve been writing.

As always, feel free to sign up here to follow along (or post comments and questions!), or follow me on Twitter or Twitch for more updates or to watch me work on these projects.

Have a wonderful week, everyone.