Making a game - Best practices

Hi everybody,
what is your approach when you are making your game(s)? What is the order in which you accomplish the common tasks? What are the common tasks? What must be taken care of? What needs consideration?

Maybe we could collect a few things here to get an overview.

Some questions for example when making a game with MonoGame may be:

How do I setup a vs solution so I can develop for multiple target environments?

How to make sure, the game works for different screen resolutions?

What to write first, the code for a fancy intro, the menu or gameplay?

Also I think it may be interesting, if you guys have found useful solutions for yourself that you want to share. I mean like a way to handle input for example or how to handle all the references needed everywhere or…

2 Likes

I didn’t expect this post to get so long when I started!

Of course everyone has their own preferences, so I wouldn’t describe it as best practice, but this tends to be my process. The steps certainly blend together a lot.

Considerations before starting:

  • ABSOLUTELY use some kind of source control. If you don’t want to pay for a private GitHub, there’s Bitbucket or GitLab which are basically exactly the same.
  • Decide which platforms you want to support. Look at device distribution and which features they have available. For example ~63% of android devices support OpenGL ES 3.0 which is required for the MRT support that makes deferred rendering work, but this number should be higher by the time I finish my game.
  • Plan it out before you get started. For my current game, I had about 10 pages of text describing gameplay, controls, characters, and actions before I even decided to use MonoGame.

Things to remember throughout:

  • Don’t be afraid to rewrite code. Each feature won’t immediately mesh with the others. I usually add a few features or classes at a time and messily flesh them out til they work, and then slim them down and fit them in with the rest before moving on. Most large codebases need to evolve to be maintainable.
  • Test all your features. If you don’t want to keep a unit test project, then just write data to the console and validate it. When I worked on map generation, I copied and pasted my generated vertices into an online scatter plot.
  • Stress test if necessary, depending on your game and supported platforms. For example, maybe an iPhone can display one really cool character shooting fireballs, but it crashes as soon as you have 5 of them.

Development process:

  1. Set up a Hello World app using whichever framework/engine you decide to use (hopefully MonoGame!) and make sure it builds and deploys for all the devices and simulators you have available. I like to start from the most basic level and install as little as possible, so I forked the MonoGame repo and built from source. Since I didn’t install templates, it may have taken me a little longer to get the solution started from samples on GitHub, but I think I was able to understand how the different projects and content files fit together better.
  2. Start with a basic proof of concept that samples some of the cool features that make your game unique. For example I may start with things like dynamic map generation, seamless level streaming, or weapon trail effects. My focus is never to have a lot of content yet, but try things that you think may not be doable, and just create one sample of each type of feature that can be assembled into a demo. This serves several purposes: it validates that the framework is suited for my game, it provides a cool demo to show people, and it helps motivate me to put a lot of time into the game.
  3. Develop the fundamentals. These are things that are required for most games, and can usually be put in a generic “Engine” project or folder that you may even have from a previous game. This usually encapsulates an object management system (e.g. Entity Component System), the rendering pipeline (I’m using deferred rendering for my game, which takes more effort than what’s built in), content libraries, etc. Anything that’s not content and not a feature usually comes in this stage. This will add a lot of the foundation and efficiency that may not have been required for the proof of concept. This is also the point at which a lot of code will be rewritten. Remember those cool, quick features you wrote before? They probably weren’t designed to work with these systems, so modify them or take what you learned and start from scratch.
  4. Make the remaining features. There were probably only a small fraction of all of the features in the proof of concept, so knock out the rest, one at a time. This is probably where most of my time writing code is spent. I already know what I want to do and where it has to fit in, but I just have to figure out how and bash my head against my keyboard until it works.
  5. Create lots of content. If you have a dedicated artist, then some of this can be done in parallel, but it’s not only art. Every character, ability, or weapon might need a set of stats. Every encounter will add unique experiences. Every interaction will have dialogue and options. This is the point at which your single-level demo can expand into a bigger game if it hasn’t already.
  6. I always leave what I find most tedious for last, especially if it’s not required for core gameplay and you know it should work in any framework. This is usually stuff like menus and audio.
6 Likes

Do workflow hacks count?

  • Embed a tiny HTTP server into your game/tool to use in development builds

    • Quick to do, all you have to do is respond to a Uri however you want
      • Easy to expand, just add new valid Uris to seeks for
      • Easy to query, just trace a query string
    • Get information without tromping through the debugger or tracking down the right place for a breakpoint
      • Editing and creating data is just a smidge more work (or a lot if your object dependencies are crazy)
    • Somewhere you probably have a ‘root’ object of sometype, run reflection on that and dump out the object tree
    • Data URIs are super useful, dump the overhead map of where everything is the scene or a performance breakdown over the past frames as base64 png
    • I thought it was dreamy in Libgdx
  • Use a batch file to copy our MG Pipeline output folder’s contents into your build

    • Archive and version your content separately so you’re not wading through muck finding a version of a shader from 3 weeks ago

    • Include that batch process as a post-build step (you still want the batch file so you can run it manually when only content changes)

        xcopy /s "C:\dev\Sprue\SprueKit\Content\bin" "C:\dev\Sprue\SprueKit\bin\Release\Content\"
        xcopy /s "C:\dev\Sprue\SprueKit\Content\bin" "C:\dev\Sprue\SprueKit\bin\Debug\Content\"
      
  • Return a garbage/trash variant instead of null when you’re after a resource

  • Save your wrists and write code generators to spit that tedious stuff out (like serialization) into partial classes nicely put away in an “XXXX_Autogenerated.cs” file

    • Use reflection offline instead of maintaining a huge and brittle reflective serializer
    • Put version IDs in your class static const int VERSION_ID = 1;, let your code gen deal with it all
      • Move old types into data-only PODs and mark with an attribute [OldVersion(typeof(Data.TexGen.LevelsNode), 1]
  • GZDoomBuilder is silly fast to knock out crude environments for testing, export to OBJ and you’re done - unless you care about textures

    • Useful when debugging custom physics, can throw together a sandbox gym in half an hour
  • Get yourself someway to export a selection of things from a scene out to an OBJ file (and save those selections when you do so you can repeat it)

    • Can load that file into other tools to use for various tasks
      • Author animations against the scene placement
      • Plot motion curves for camera/etc
      • Model geometry to fit the environment (ie. modeler loft/repeat tools)
  • ImageMagick, master it

  • Tiny tools

    • You’ll thank yourself for a 100-300 line throwaway program when it saves you from a tedious process that’d take forever or be error prone
  • Embrace the old stuff that still works

    • Use HTML imagemap tools to get the data to give you snazzier help screens
      • Take a screenshot -> draw the shapes -> load the data -> display and use as required (remember to record the screenshot size for remapping if necessary)
      • Granularity and information sourcing is no longer tied to your widgets/layout
  • Use Tau instead of Pi for better feeling pulsing animations

      public float GetAnimTime() { deltaTimeWasUsed = true; return DeltaTime * 6.28318530718f * 2; }
      public float GetTimeCurve() { return 0.5f + Mathf.Sin(GetAnimTime()) * 0.5f; }
1 Like

@AcidFaucent I know this is an old thread, but do you think you could go into a bit more detail on using a HTTP server as a debugging tool? I’m wondering how the server would look on a webpage and how you would interact with it.

Taking a guess, I would say the main page might look like this image with a tree view representing all child objects of the root object passed to the debug server. An object might look like the locals view in Visual Studio, showing all the variables belonging to the object.

Does this all happen once? Or does the site data change on refresh?

@Emmsii, I used bootstrap for the presentation. I used the the dashboard template with the main links for categories (Resources, Scene-contents, Map-view) and then each thing was it’s own thing. Resources is a flat list, scene-contents a tree, and map-view a fake web-cam steam.

There really isn’t much to say, it’s pretty much do w/e. I’m not a web-developer and despise them so I went super bare-bones so I largely used template HTML files with string-concatenation, not terribly unlike old-fashioned server-side includes (where web-dev should’ve bloody stopped).

I do have an ancient Java version on github that I used with LibGDX that should get the point across. My C# and C++ versions aren’t terribly different, just libraries change and reflecting objects gets stupidly insane on C# (yes, I do mean C# - C++ has so many options for reflection that all make C#'s look like trash).

Does this all happen once? Or does the site data change on refresh?

I do it as an embedded request handler, so it’s always serving new HTML at every request, I haven’t run into a need for AJAX or such yet - most info/data isn’t anywhere near that dynamic in a game - though for overhead map-views I did that as a bogus webcam stream for the refresh of the map as that’s information that it’s more useful when observed over a long period of time.

Thanks for that! That was definitely helpful. I’ve had a look at your Java version, I think the process of getting object data with reflection is somewhat similar to do in C#, so it shouldn’t be too difficult.

I suppose if you wanted to go one step further, you could use websockets to transmit data in (somewhat) real time. I guess it depends on the game.

Cheers!

I think the process of getting object data with reflection is somewhat similar to do in C#, so it shouldn’t be too difficult.

Mostly, generics/collections will be different though.

I suppose if you wanted to go one step further, you could use websockets
to transmit data in (somewhat) real time. I guess it depends on the
game.

Yeah, you could. I’ve run an embedded websocket server in different games as well, though only for Angelscript debugging. It’s a lot more work, so asPEEK’s protocol has really been the only case worth that much work to me - but that involved writing a complete IDE in WPF, so I don’t really know how much real work there was for the core of the websocket stuff.