Setup FNA Framework in macOS with Apple Processors

FNA is a reimplementation of the Microsoft XNA Game Studio 4.0 Refresh libraries, the framework with which I started learning to create video games.

FNA has good support for Windows and Linux to create a viable environment, but it has some problems with macOS, specially with M processors. So if you want to develop a FNA game in macOS is not as simple as seen, but I finally got to have a setup in my Mac.

If you want to develop games in macOS using FNA, follow these steps, these are working for me, but I can’t guarantee that it will work 100% for you. If you have problems, you can join to the Discord Server.

Install brew

First of all, we need to setup brew to be able to install all the requirements.

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Install the required prerequisites

Once you have installed brew, now is time to install python, cmake and git.

brew install cmake python3 git

Get the fnalibs and compile for the platform

This is the more important step, FNA uses natives libraries to develop and run games, the FNA team provides the native libraries for Windows, Linux, and x86_64 macOS but for arm64 we need to compile by our self, but thanks to TheSpydog and his repo, we can compile our libraries easier than before.

First, you need to download the repo: https://github.com/TheSpydog/fnalibs-apple-builder

The repo has clear instructions for building, so we only need to reproduce here.

For example, if you download the repo in the Download folder, you need to go to the folder in the terminal and run the commands.

cd ~/Downloads
./updatelibs
./buildlibs macos    # Build for mac, you can build for ios/ios-sim/tvos/tvos-sim/all too

After building the libraries, you are going to have a bin directory where you are going to find the native libraries that you are going to use.

Install dotnet 8

FNA uses C# to write games, so we need to install dotnet in your mac, go to the official site and install the Arm64 version.

Create your project

Finally we can create our project for our game. Open to your terminal, then go to the location where you are going to put your project.

cd ~/Desktop
mkdir MyGame
cd MyGame
dotnet new sln      # Create a solution file to add the projects
dotnet new console -o MyGame
dotnet sln add MyGame/MyGame.csproj

You need to add the FNA repo, the FNA FAQ’s has a explanation why they are not using NuGet packages, so, in the terminal run the this command.

git clone --recursive https://github.com/FNA-XNA/FNA
dotnet sln add FNA/FNA.Core.csproj   # Add the core project because we are using dotnet core

We need to add the reference in our project so we can get the FNA Framework working in our code.

cd MyGame
dotnet add reference ../FNA/FNA.Core.csproj

Once we have created the reference, we need to build our project.

dotnet build

The build command, is going to generate a build directory, you need to put the native libraries that compiled early in the build/Debu/net8.0 path.

Creating the Content folder

If you have used Monogame or XNA, you know that we need to use a Content Pipeline to convert the files in a .xnb format. In FNA we have more freedom in our assets,

The FNA documentation says:

FNA supports loading common data formats like PNG, WAV and OGG directly, and the community maintains a few libraries for font loading and rendering.

But we want to emulate XNA, so in your project root folder, create a folder and named Content.

But we need a little of configuration in the MyGame.csproj to add the Content folder to our bin directory. Open and modify MyGame.csproj to add the next code.

<ItemGroup>
    <None Include="Content\**" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

Your file should looks like this:

<Project Sdk="Microsoft.NET.Sdk">

  <ItemGroup>
    <ProjectReference Include="..\FNA\FNA.Core.csproj" />
  </ItemGroup>

  <ItemGroup>
    <None Include="Content\**" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

</Project>

Test our FNA setup

Now, is time to test and see if all the steps are working.

First, download the ball image (I took from Monogame Tutorial) and save it as ball.png in the Content folder.

Now, replace the Program.cs file with this code

using System;

static class Program
{
    [STAThread]
    static void Main()
    {
        using (var g = new MainGame())
        {
            g.Run();
        }
    }
}

Create a new file and named it MainGame.cs and add this code

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

public class MainGame : Game
{
    private GraphicsDeviceManager _graphics;
    private SpriteBatch _spriteBatch;

    Texture2D ballTexture;

    public MainGame()
    {
        _graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
    }

    protected override void Initialize()
    {

        // TODO: Add your initialization logic here

        base.Initialize();
    }

    protected override void LoadContent()
    {
        _spriteBatch = new SpriteBatch(GraphicsDevice);
        ballTexture = Content.Load<Texture2D>("ball");
    }

    protected override void UnloadContent()
    {
        base.UnloadContent();
    }

    protected override void Update(GameTime gameTime)
    {
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
            Exit();

        // TODO: Add your game logic here

        base.Update(gameTime);
    }

    protected override void Draw(GameTime gameTime)
    {
        _graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

        // TODO: Add your drawing code here
        _spriteBatch.Begin();
        _spriteBatch.Draw(ballTexture, new Vector2(0, 0), Color.White);
        _spriteBatch.End();

        base.Draw(gameTime);
    }
}

This is a code that was generated in the template of XNA (I just added the ball), and it is a good template for start with your game.

Running the game

Now you can run your game

dotnet run

You should see our FNA window with a ball 😀

From here, you can read XNA, Monogame and FNA tutorials to learn more or if you are a experience developer you can test your ideas.

GBA Development setup in macOS

To develop in GBA we need the tools to do it, and installing it in macOS is simple even if they take a few steps.

Install pacman

The first thing we must do is install pacmac, which will allow us to download the devkitpro for the GBA, to install it we must go to the following URL:

https://github.com/devkitPro/pacman/releases/tag/v1.0.2

And we will download devkitpro-pacman-installer.pkg. Once it has been downloaded, we install the package. The following will appear:

So, we need to go to System Preferences -> Security & Privacy and allow the permissions:

Follow the steps to install the package.

Once that the installation was finished, we need to update our .bashrc or .zshrc file.

Update our environments vars

With the installation of pacman, we need to tell to our system where the files are located, so we need to update our .bashrc or .zshrc file.

Open your file and copy the next exports

export DEVKITPRO=/opt/devkitpro
export DEVKITARM=${DEVKITPRO}/devkitARM
export DEVKITPPC=${DEVKITPRO}/devkitPPC
export PATH=${DEVKITPRO}/tools/bin:$PATH

Install Xcode Tools

Now we need to install the Xcode tools to use the compiler and other utilities. In your terminal write the follow command:

xcode-select --install

Install devkitpro

Now we have all the tools to install our GBA compiler, so is time to write in the terminal.

sudo dkp-pacman -S gba-dev

Enter your password. You will prompt with options to get more control about the things that your going to install, for now we are going to install all of them. So, hit Enter and then Enter to confirm.

If you have Catalina you will get an error.

error: Partition / is mounted read only
error: not enough free disk space
error: failed to commit transaction (not enough free disk space)

This is because Catalina root partition is only for read, and you cannot install anything here, but you can have a workaround installing with this command:

sudo dkp-pacman -S gba-dev -r /System/Volumes/Data

Again, hit Enter, and Enter.

With devkitpro installed, your are not able to create a GBA game, so we need to test if our tools was installed correctly.

Hello World GBA

In your Desktop or whenever you want, create a folder named hellogba, inside create a new file named Makefile, this file will help us to compile our code and generate a rom

Copy the following

#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------

ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif

include $(DEVKITARM)/gba_rules

#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
# DATA is a list of directories containing binary data
# GRAPHICS is a list of directories containing files to be processed by grit
#
# All directories are specified relative to the project directory where
# the makefile is found
#
#---------------------------------------------------------------------------------
TARGET		:= rom/$(notdir $(CURDIR))
BUILD		:= build
SOURCES		:= src
INCLUDES	:= include
DATA		:=
MUSIC		:=

#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH	:=	-mthumb -mthumb-interwork

CFLAGS	:=	-g -Wall -O2\
		-mcpu=arm7tdmi -mtune=arm7tdmi\
		$(ARCH)

CFLAGS	+=	$(INCLUDE)

CXXFLAGS	:=	$(CFLAGS) -fno-rtti -fno-exceptions

ASFLAGS	:=	-g $(ARCH)
LDFLAGS	=	-g $(ARCH) -Wl,-Map,$(notdir $*.map)

#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
LIBS	:= -lmm -lgba


#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS	:=	$(LIBGBA)

#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------


ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------

export OUTPUT	:=	$(CURDIR)/$(TARGET)

export VPATH	:=	$(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
			$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
			$(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir))

export DEPSDIR	:=	$(CURDIR)/$(BUILD)

CFILES		:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES	:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES		:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES	:=	$(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))

ifneq ($(strip $(MUSIC)),)
	export AUDIOFILES	:=	$(foreach dir,$(notdir $(wildcard $(MUSIC)/*.*)),$(CURDIR)/$(MUSIC)/$(dir))
	BINFILES += soundbank.bin
endif

#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
	export LD	:=	$(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
	export LD	:=	$(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------

export OFILES_BIN := $(addsuffix .o,$(BINFILES))

export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)

export OFILES := $(OFILES_BIN) $(OFILES_SOURCES)

export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES)))

export INCLUDE	:=	$(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir)) \
					$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
					-I$(CURDIR)/$(BUILD)

export LIBPATHS	:=	$(foreach dir,$(LIBDIRS),-L$(dir)/lib)

.PHONY: $(BUILD) clean

#---------------------------------------------------------------------------------
$(BUILD):
	@[ -d $@ ] || mkdir -p $@
	@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile

#---------------------------------------------------------------------------------
clean:
	@echo clean ...
	@rm -fr $(BUILD) $(TARGET).elf $(TARGET).gba


#---------------------------------------------------------------------------------
else

#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------

$(OUTPUT).gba	:	$(OUTPUT).elf

$(OUTPUT).elf	:	$(OFILES)

$(OFILES_SOURCES) : $(HFILES)

#---------------------------------------------------------------------------------
# The bin2o rule should be copied and modified
# for each extension used in the data directories
#---------------------------------------------------------------------------------

#---------------------------------------------------------------------------------
# rule to build soundbank from music files
#---------------------------------------------------------------------------------
soundbank.bin soundbank.h : $(AUDIOFILES)
#---------------------------------------------------------------------------------
	@mmutil $^ -osoundbank.bin -hsoundbank.h

#---------------------------------------------------------------------------------
# This rule links in binary data with the .bin extension
#---------------------------------------------------------------------------------
%.bin.o	%_bin.h :	%.bin
#---------------------------------------------------------------------------------
	@echo $(notdir $<)
	@$(bin2o)


-include $(DEPSDIR)/*.d
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

This template include all necessary things to compile and generate a rom, for now, the most important lines are:

TARGET		:= rom/$(notdir $(CURDIR))
BUILD		:= build
SOURCES		:= src
INCLUDES	:= include
DATA		:=
MUSIC		:=

That are the directories where you’re going to write your code and generate the output. Create the rombuildsrc and include folders.

Inside src directory, create a new file named hello.c and write the next code

int main()
{
    *(unsigned int*)0x04000000 = 0x0403;

    ((unsigned short*)0x06000000)[120+80*240] = 0x001F;
    ((unsigned short*)0x06000000)[136+80*240] = 0x03E0;
    ((unsigned short*)0x06000000)[120+96*240] = 0x7C00;

    while(1);

    return 0;
}

This code show three points in the screen, we are going to use only to test our environment.

Now is time to compile!

In your terminal, write this

make

And if you see this message

linking cartridge
built ... hellogba.gba
ROM fixed!

Your environment is ready to work with. In the rom folder, you are going to see two files, hello.gba and hello.elf, the last one is util for debugging.

Now we need an emulator to test our “game”.

I am using mGBA and you can download it here https://mgba.io/

Load the hello.gba in the emulator and you should see this screen

Now, you are ready to create GBA games!

Resources

Some resources to start GBA development

https://www.youtube.com/channel/UCQvQbVuWMM7OYTVrmGQq0hA

http://www.coranac.com/tonc/text/toc.htm

http://www.loirak.com/gameboy/gbatutor.php

https://github.com/gbdev/awesome-gbadev

https://www.reinterpretcast.com/writing-a-game-boy-advance-game

OpenGL Setup in macOS

We have configured SFML in macOS, now we’re going to configure OpenGL with macOS and Xcode.

First, you need to install GLFW

brew install glfw

Then, you need to download GLAD to get the OpenGL headers: Use gl version 4.1 if you want to be compatible with macOS and in Profile select Core. Click in Generate. Download the glad.zip and unzip in your Download folder.

https://glad.dav1d.de

Once you are downloaded and unzipped the folder, put the glad and KHR folders in the /usr/local/include directory of your mac.

Creating the Xcode Project

After installed GLFW and downloading the glad files, is time to create the XCode project.

Create a new Project and select macOS, then Command Line Tool, finally click on Next

Then, put a name to your project and select C++ as language, next and save the project

In the zip that you downloaded, you will have a glad.c file, add that file to your project

We need to configure Xcode to find the libraries that we added, so in your Xcode, select the Project Target and then Build Settings and in Header Search Paths add /usr/local/include

Now, in Framework and Libraries you need to add the libglfw library, so clic on + symbol and add the library that you will find in /usr/local/Cellar/glfw/3.3/lib

Now, in the main.cpp file, copy the follow code to test OpenGL

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>

void framebuffer_size_callback(GLFWwindow* window, int width, int height);

int main()
{
    GLFWwindow* window;

    // Initialize the library
    if(!glfwInit())
        return -1;

    // Define version and compatibility settings
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
    glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    // Create a windowed mode window and its OpenGL context
    window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
    if (!window)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }

    // Mathe the window's context current
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    // Initialize the OpenGL API with GLAD
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    // Loop until the user closes the window
    while(!glfwWindowShouldClose(window))
    {
        // Render here!
        glClear(GL_COLOR_BUFFER_BIT);

        // Swap front and back buffers
        glfwSwapBuffers(window);

        // Poll for and process events
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    // make sure the viewport matches the new window dimensions; note that width
    // and height will be significantly larger than specified on retina displays
    glViewport(0, 0, width, height);
}

If you are in Catalina and run the project, your are going to see an error, that is because the library has not the permissions to run in your mac, so you will need to sign it to run.

In the terminal copy and paste the command

codesign -f -s "your@email.com" /usr/local/Cellar/glfw/3.3/lib/libglfw.3.3.dylib

Edit: Thanks to a comment of Maxwell Omdal, if you can’t sign with you email, you can do it with your name:

codesign -s -f "Developer ID Application: Name /usr/local/Cellar/glfw/3.3/lib/libglfw.3.3.dylib"

Now, run the project again and you should see the window 🙂

Now you will be able to play with OpenGL 🙂

SFML 2.5.1 setup on macOS with CLion

I know that the title is very specific but that is the platform that I am doing some experiments with SFML.

So, if you want to create games with SFML and you have macOS and you want to use CLion as IDE just follow the next steps:

Install SFML

There are a lot ways to install SFML but the easiest is to install it with brew

brew install sfml

With this command, you’re going to install the version 2.5.1.

Create the project with CLion

Now is time to create a new project with CLion, put the name that you want

Now, you need to update the CMakeList.txt to compile and link your project with the SFML libraries.

cmake_minimum_required(VERSION 3.14)
project(HelloSFML)


set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")


set(SOURCE_FILES main.cpp)
add_executable(HelloSFML ${SOURCE_FILES})
include_directories(/usr/local/include)

find_package(SFML 2.5 COMPONENTS system window graphics network audio REQUIRED)
include_directories(${SFML_INCLUDE_DIRS})
target_link_libraries(HelloSFML sfml-system sfml-window sfml-graphics sfml-audio sfml-network)

My recommendation is that you link the libraries that you are going to use, for example, only link and require sfml-graphics and sfml-audio for example.

A simple SFML project

Now , create a simple SFML code to check if all was installed correctly.

#include <SFML/Graphics.hpp>

int main()
{
    sf::RenderWindow window(sf::VideoMode(640, 480), "SFML Application");
    sf::CircleShape shape;
    shape.setRadius(40.f);
    shape.setPosition(100.f, 100.f);
    shape.setFillColor(sf::Color::Cyan);

    while (window.isOpen())
    {
        sf::Event event;

        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }

        window.clear();
        window.draw(shape);
        window.display();
    }
}

Run your project and you will see

Now you are ready to create games 🙂 !