Building a 3D Game Engine with .NET Core

Anyone who has been following the progress of .NET Core knows we’ve been talking a lot about console and server applications. In this blog, I’ll be exploring another use of .NET Core, and showing a multi-platform 3D game engine, editor, and game that I’ve built from scratch. As a dev on the .NET team, I follow a lot of community discussions and have seen some increased interest in this area (for example). I’m hoping this blog will shed some light on the capabilities of .NET Core, and excite others to explore this space.

In this first post, I’ll be introducing my game Crazy Core, an arcade-style 3D platformer where you guide a ball through various obstacle-filled levels. This will be a broad overview, and subsequent posts will dive into more details about the game, its engine and editor, and the technologies I’ve used to make it all work.

Disclaimer: this is purely a one-man developer game built from scratch. It will be evident that no artists were hired during the creation of this game, and I can’t promise that they won’t feel harmed upon viewing the primitive artwork.

Crazy Core

This slideshow requires JavaScript.

https://github.com/mellinoe/CrazyCore

Windows.zip
OSX.zip
Ubuntu-1604.zip

This is a self-contained program. Simply download the zip file for your platform and run the “CrazyCore” executable inside it. If you are running on Ubuntu, there are some prerequisites that need to be installed first.

The mechanics of the game are pretty self-explanatory. You control a ball, rolling through a 3D world. This world is very simple at first. Gradually, new mechanics are introduced, and the environment and obstacles become more and more complicated as you progress. The final levels have some truly “crazy” mechanics, if you manage to make it that far.

Why .NET Core?

Primarily, this project was a great way to put .NET Core through its paces, and test a scenario off the beaten path. Although I may be a bit biased, I also believe .NET Core has a ton of technical advantages for games:

  • Open-source. You can view, modify, and optimize any part of the stack.
  • Lean, optimized runtime designed for multiple platforms. This means that there is significantly less to worry about when releasing on different platforms.
  • It’s .NET. You can use any .NET language with all of the great tools and debugging experiences that are already available.
  • Future-facing. .NET Core is rapidly evolving, and performance optimizations are merged in every day. On another front, the CoreRT project is going to be a very interesting technology for building games. While not ready for prime-time yet, the ability to deliver a heavily-optimized, ahead-of-time-compiled binary will be great for games.

Game Engine Architecture

As with any game engine, there are a ton of different components intertwined here that make a real-time 3D game possible. I hope to explore some of these topics in much more detail in subsequent posts, but to give a general idea of the structure and capabilities of the engine, I’ll highlight the most important things here.

  • The engine is component-based, with a programming model loosely similar to Unity. I’ve used Unity a lot in the past, so this was a familiar way to develop for me.
  • Support for both Direct3D11 (Windows) and OpenGL (all platforms) for graphics. The graphics are implemented on top of an API-agnostic abstraction layer called “Veldrid”.
  • Support for both XAudio2 (Windows) and OpenAL (all platforms) for audio. There is another (much thinner) abstraction layer which allows either API to power the 3D positional audio in the engine.
  • Windowing and input handling are handled by the OpenTK library.
  • Real-time 3D physics, using the awesome BEPUphysics library.
  • Custom asset management and serialization system, used for storing a variety of things including:
    • Binary assets (models, textures, audio, etc.)
    • Entity and component state
    • Scene composition and linkage
    • Meta-information, like player progress and high scores
    • Project settings

To construct the different levels of the game, I built a separate editor application, which reuses various components of the engine. The editor is a standalone 3D program that lets you view and modify game assets, put together game entities, construct full scenes, and manage project-level options.

Like a typical editor, the game itself can be tested from within the application, and various pieces of game state can be tweaked live. The editor also has features enabling things like project publishing, debugging, code hot-swapping, and more.

This video above shows some of the cool things you can do by tweaking things live in the editor.

One of the most useful features of the editor is that it is trivial to run on multiple platforms, since it also uses .NET Core. This makes it extremely easy to quickly apply and test changes to the engine and game anywhere.

Open-Source Libraries

Although I said this was built “from scratch,” I used quite a few open-source libraries for features of the engine. Some of these libraries are available on NuGet and were used as-is. Others needed to be recompiled for .NET Core, but none needed very complicated re-writing. I’ve found that game-related libraries are often low-level enough that their dependencies are not difficult to satisfy.

Below are the libraries I used in this project, and what they were used for. Anyone interested in game-related stuff in .NET should take a look at some of these libraries, they are quite cool!

  • SharpDX – Bindings for Direct3D11 and XAudio2. These technologies are Windows-only, and are used by default there in the engine. SharpDX already supports a PCL profile, so it can be used unchanged on .NET Core.
  • OpenTK – Bindings for OpenGL and OpenAL (alternatives for the above), as well as windowing and input. This library is quite large and quite old; it needed some work to compile on .NET Core.
  • ImageSharp – Cross-platform image loading. This is used to load all of the textures used in the game. Shout out to James who is doing a really great job with this project.
  • BEPUphysics – Real-time 3D physics. This is a really cool and well-designed library. It recently moved to GitHub and a very intriguing v2.0 is planned.
  • dear imgui – Immediate mode UI, used for the editor’s GUI, and for the menus in Crazy Core. I’ve written a .NET wrapper for the original C++ library: ImGui.NET.
  • SharpFont – TrueType font loading and rendering. This is used for some 2D text rendering components in the engine.
  • Newtonsoft.Json – I think everyone knows this library. I’ve used it to serialize all of the custom data types in my engine, including entity, component, and scene data.

Thanks for reading! I hope this post has illuminated some of the things that are possible with .NET Core today. In the next post, I’ll talk more about how 3D rendering is handled in the engine. It is the most complicated part of the engine, but also turned out to be the most interesting and fun part to develop. Stay tuned!

Advertisements

16 thoughts on “Building a 3D Game Engine with .NET Core

    1. I have viewed this project more as a learning exercise rather than a concrete project “to be completed”, so I don’t really have in my mind what a finished product would look like. It’s more likely that I will use the engine as a basis for other targeted projects involving graphics, audio, physics, etc.

      Like

    1. Thanks, that’s actually great advice. Technically this is for the “regular version” of OpenTK, but it looks like it will pull in all of the native prerequisites of OpenTK, which are the same for the .NET Core version. Cheers!

      Like

  1. Very nice! I was able to beat all the levels in Crazy Core (although I didn’t get all the points).

    I’m also building a game engine in .Net Core (for fun after hours) but focusing on 2D game development. I ended up using SDL2 for window creation, input handling, image loading (sdl_image), and sound (sdl_mixer). The library already existed for .Net it was mainly just cleaning up the marshaling layer. For graphics, I converted the opengl4csharp bindings (which is based on OpenTK but with OpenGL 4) and left everything else alone.

    On top of OpenGL I ended up writing my own scene graph and instanced quad batcher for fast rendering. I also added texture packer atlas support, bitmap font loader (angelcode’s bmfont), JSON configs (json.net), and an asset cache which can load assets from files or zip archives. I don’t know if I’ll go full blown editor but at least something to create or load scene graph prefabs would be nice.

    Anyway, just wanted to say awesome work and I’ll definitely be following.

    Like

    1. Thanks for playing through the game and for the comment! I would love to see or read about the engine you’ve built, it’s really cool to hear that others are interested (and already working!) in this space with me. I’ll give your page a follow as well. Cheers!

      Like

  2. On OS X I get trying to run ./CrazyCore

    Unhandled Exception: System.TypeInitializationException: The type initializer for ‘Engine.Graphics.SphereModel’ threw an exception. —> Veldrid.Graphics.ObjParseException: An error ocurred while parsing position data on line 2, “v -1 0 1.4” —> System.FormatException: Input string was not in a correct format.
    at System.Number.ParseSingle(String value, NumberStyles options, NumberFormatInfo numfmt)
    at Veldrid.Graphics.ObjParser.ParseContext.ParseVector3(String xStr, String yStr, String zStr, String location)
    — End of inner exception stack trace —
    at Veldrid.Graphics.ObjParser.ParseContext.ParseVector3(String xStr, String yStr, String zStr, String location)
    at Veldrid.Graphics.ObjParser.ParseContext.Process(String line)
    at Veldrid.Graphics.ObjParser.Parse(String[] lines)
    at Veldrid.Graphics.ObjParser.Parse(Stream s)
    at Engine.Graphics.SphereModel.LoadShereMesh()
    at Engine.Graphics.SphereModel..cctor()
    — End of inner exception stack trace —
    at Engine.Assets.EngineEmbeddedAssets..ctor()
    at Engine.Assets.AssetSystem.CreateAssetDatabase(SerializationBinder binder)
    at Engine.Assets.AssetSystem..ctor(String assetRootPath, SerializationBinder binder)
    at Engine.Program.Main(String[] args)
    Abort trap: 6

    Like

    1. Hey Erik,

      That is actually a bug that another user reported to me. It was a really simple fix, but I didn’t publish another version of the game yet. Unfortunately, the code that loads OBJ mesh files has a bug which causes it to fail on systems with certain language settings. I will try to set aside some time to create a new build of the game with that bug fixed.

      Like

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s