Skip to content

Instantly share code, notes, and snippets.

@remmah

remmah/fna-fs.md Secret

Created December 3, 2021 02:07
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save remmah/d7bcca26f46e799471d02395337c86ea to your computer and use it in GitHub Desktop.
Save remmah/d7bcca26f46e799471d02395337c86ea to your computer and use it in GitHub Desktop.
Setting up FNA and F# for game development

Setting up FNA and F# for game development

Back in the Xbox 360 era, Microsoft promoted XNA as a way for indies to make games for Microsoft's console, desktop, and mobile platforms. Being a code-centric framework, its API is closer to the metal than modern-day engines. This approach allows devs to build small, form-fitting engines for the kinds of games they want to make. Many well-regarded indie games such as Celeste, Bastion, and Axiom Verge were built using XNA.

While Microsoft discontinued XNA in 2013, open-source projects have kept the framework alive. MonoGame is one such framework (check out the video series from Dave Thomas to get started with MonoGame and F#). The project I will focus on for this article is FNA, which is a reimplementation of XNA focused on compatibility, allowing indie games from the Xbox 360 era to be ported to modern platforms. Even though FNA's focus is on porting existing games, developers use it to make new games as well.

Because XNA and its modern reimplementations are based on .NET, that means F# is available as a language for making games with FNA. Of course, as with most things related to .NET, C# has been the dominant language for XNA development, but even in the Xbox 360 days, F# could be used with some effort.

This article will guide you through the steps to set up a new FNA project from scratch with F# on Windows. The FNA project eschews Nuget and package management in favor of a one-time manual setup, so that is the approach we'll be taking.

Step 1: Install Visual Studio 2019

  • Install Visual Studio 2019 if you haven't already (Visual Studio 2022 is not compatible at this time due to changes in how VS 2022 handles .NET Framework projects).
    • Under the Workloads tab, make sure that .NET desktop development is checked
    • Under the Individual components tab, make sure that F# desktop language support is checked

Step 2: Create and adjust a new F# Console solution

  • From the VS 2019 launch screen, click on Create a new project
  • Proceed through the wizard as follows:
    • Select F# from the language drop-down menu
    • Choose Console Application (.NET Framework) from the list, click Next
    • Give the project a name, choose .NET Framework 4.8 from framework drop-down menu, and click Next

You should now have a VS windows displaying your solution. To get this F# console app working properly with FNA, we will have to make a few small adjustments to the build settings:

  • On the main toolbar, click the drop-down menu that says Any CPU, then click Configuration Manager...
  • From the dialog box that appears, click the Active solution platform drop-down menu, then click New
  • From the new dialog box that appears create a new x64 platform. Coping settings from Any CPU is fine, and make sure the Create new project platforms box is checked.
  • x64 should now be the active platform for both your solution and your project. Close the Configuration Manager.

Step 3: Download and add the FNA sources to your solution

FNA releases are distributed as plain file archives, so now you are going to grab those and integrate them into your project:

  • Download the latest source code release from the FNA download page, along with the archive of the native libraries. FNA releases are versioned according to year.month, so the most recent release at time of writing is 21.12.
  • Unzip the FNA source to a convenient location (can be either within your solution directory or outside of it)
  • Back in Visual Studio, right-click on your solution within the Solution Explorer, then go to Add → Existing Project from the context menu
  • From the file selection dialog that pops up, navigate to the FNA source directory you unzipped, then choose FNA.csproj (NOT FNA.Core.csproj as that version is designed for Xbox support). The FNA project should now be present in Solution Explorer.
  • From Solution Explorer, right-clicks on your project, then go to Add → Project Reference from the context menu
  • In the Reference Manager that pops up, check the box for FNA, then click OK

Step 4: Build your project and add the FNA runtime

Now that you have the FNA source code hooked up to your project. It's time to perform an initial build and add the FNA runtime to the build directory:

  • From the main Visual Studio menu, choose Build → Build Solution (or use the Ctrl-Shift-B shortcut). The build should report 2 succeeded, 0 failed. If not, make sure the x64 platform is selected and that FNA is properly linked and referenced from Step 3.
  • In Explorer, unarchive fnalibs.tar.bz2
  • In the the unarchived directory, select and copy the following DLLs:
    • FNAudio.dll
    • FNA3D.dll
    • libtheorafile.dll
    • SDL2.dll
  • In Explorer, navigate to your project's output folder (usually [your-solution-folder]\bin\x64\Debug\net48)
  • Paste the DLLs into that directory

Step 5: Create game scaffolding using F#

Will all the setup done, now you can create the basic FNA game scaffolding inside of your project. The following steps and code are adapted from the official FNA tutorial and from Mark Pattison's monogame-fsharp repository:

  • From within Solution Explorer, create two new folders in your project: src and content
    • src will contain your source files
    • content will contain game assets like sprites
  • Inside of src, create a new file: Game.fs, containing the following code:
module Game

open System.IO
open System
open Microsoft.Xna.Framework
open Microsoft.Xna.Framework.Graphics
open Microsoft.Xna.Framework.Input

type Game1() as _this =
    inherit Game()
    // let mutable input = Unchecked.defaultof<Input.State>
    // let mutable gameCOntent = Unchecked.defaultof<Sample.Content>

    let gdm = new GraphicsDeviceManager(_this)

    do gdm.GraphicsProfile <- GraphicsProfile.HiDef
    do gdm.PreferredBackBufferWidth <- 1280
    do gdm.PreferredBackBufferHeight <- 720
    do gdm.IsFullScreen <- false

    do gdm.ApplyChanges()
    do base.Content.RootDirectory <- "content"

    //let updateInputState() =
    //    input <- Keyboard.GetState() |> Input.updated input

    override _this.Initialize() =
        base.Initialize()

    override _this.LoadContent() = 
        //input <- Input.initialState()
        // gameContent <-
        base.LoadContent()


    override _this.Update(gameTime) =
        // updateInputState()
        // if Input.justPressed input Keys.Escape then _this.Exit()
        base.Update(gameTime)
    
    override _this.Draw(gameTime) =
        _this.GraphicsDevice.Clear(Color.CornflowerBlue)
        base.Draw(gameTime)
  • Below Game.fs, create another file: Program.fs:
module Program

[<EntryPoint>]
let main argv =
    let game = new Game.Game1()
    do game.Run()
    0

With all of the setup and these two source files in place, you can now build and run your solution to be greeted with a game window showing a pleasant blue background.

Tips and next steps

Although it took a good bit of manual work to set things up, you now have the scaffolding in place to learn how FNA works with F#.

There is an FNA wiki with documentation: To get familiar with the basics — including how the method calls in the above scaffolding fit into the structure of an FNA game — you can work through the FNA From Scratch tutorial, translating the C# to F# as you go. There is also an FNA Discord where one can ask questions about FNA itself, though I am not aware of anyone there besides myself who is using F#. Make sure to read the documentation before asking a question, as the maintainers will ask that you donate $5 to the project if you ask something that the documentation already answers!

FNA releases monthly, so make sure to redownload sources and DLLs if you want to stay up to date. Given how FNA aims to be an accurate reimplementation of XNA, you can rely on the API not changing much (if at all), so updates shouldn't break existing projects.

FNA's focus on compatibility also means that you can affordably obtain used copies of old/out-of-print XNA books, and most of the content will still apply to FNA. I was recommended Learning XNA 4.0 by a community member and have been using it on my own learning path. The main difference to be aware of is that FNA does not support the XNA Content Pipeline; the reasoning for this omission is well-documented by the main FNA maintainer. What this means is that you can use the Content folder directly to get started, and if your project grows enough to where a pipeline is helpful, you can build one that fits your needs (as opposed to the generic and archaic XNA pipeline).

What about using a 'modern' .NET like 5 or 6? The FNA.Core.csproj file supports .NET 5, but only for an Xbox One target. While there are reports of .NET 5-based FNA projects working for desktop platforms, support from the community will be very limited, as the expectation for these projects is to use .NET Framework.

Happy coding!

@mrange
Copy link

mrange commented Dec 5, 2021

Thanks for the tip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment