Creating a project from scratch
This chapter describes how to set up a basic osu!framework game from scratch. It follows the standard project layout, where game logic is put in a Game project and an executable Desktop project is used for Windows/macOS/Linux support.
You can also read the official documentation on how to set up a project.
For this chapter, we'll use an imaginary game called "My Platformer". This makes it easier to explain naming schemes in different contexts.
Creating a Game project
Your Game project will contain all your game logic.
Start by creating a library project that references the osu.ppy.Framework NuGet package. Its name should be your game name followed by .Game, such as MyPlatformer.Game. Your csproj file should look roughly like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Framework" Version="2021.714.0"/>
</ItemGroup>
</Project>
Next, create your Game class. This is a class that inherits from osu.Framework.Game and is typically named by adding "Game" to your game name. It will be the startup class for your game, and be the highest level of the draw hierarchy you can control.
Here is an example of a simple Game class that spins a box on the screen:
public class MyPlatformerGame : osu.Framework.Game
{
private Box RotatingBox { get; set; }
[BackgroundDependencyLoader]
private void Load()
{
Add(RotatingBox = new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(200),
Colour = Color4.Tomato,
});
}
protected override void Update()
{
base.Update();
RotatingBox.Rotation += (float)Time.Elapsed * 0.1f;
}
}
Note: Creating animations by updating them in the Update method is not recommended. In later chapters, we'll look at Transforms which make this process a lot simpler.
Creating a Desktop platform project
A platform project is a runnable project that provides a means to run your game on a platform. The most common one is the Desktop platform which runs on Windows, macOS and Linux, and it's the one you'll mainly use for testing regardless of the platform you're actually targeting.
Create a new executable project with the WinExe output type. This ensures that no console window gets created when you run your game. Then add a reference to your Game project. Your csproj file should look roughly like this like so:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MyPlatformer.Game\MyPlatformer.Game.csproj"/>
</ItemGroup>
</Project>
Next, in your Program.cs class, construct and run your game:
internal static class Program
{
private static void Main(string[] args)
{
using (GameHost host = Host.GetSuitableHost(@"my-platformer"))
using (var game = new MyPlatformerGame())
host.Run(game);
}
}
The argument you pass to Host.GetSuitableHost(string) is your game name. This will be used as folder name for storage and settings, among other things.
If you run your Desktop project now, it should create a window with your game running in it.

Splitting up the Game class
Typically, the Game class is split up to also have a "GameBase" class. This class will contain setup code that would be useful for e.g. the visual test browser. In that case, your code may look like this:
public class MyPlatformerGameBase : osu.Framework.Game
{
public Bindable<Color4> MainColor { get; set; }
[BackgroundDependencyLoader]
private void Load()
{
// Set up an online texture store so we can load an image from a URL
Textures.AddStore(Host.CreateTextureLoaderStore(new OnlineStore()));
// Create a bindable variable that will be used by the game
MainColor = new Bindable<Color4>(Color4.White);
}
}
public class MyPlatformerGame : MyPlatformerGameBase
{
private Sprite RotatingSprite { get; set; }
[BackgroundDependencyLoader]
private void Load()
{
Add(RotatingSprite = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(200),
Texture = Textures.Get("https://i.imgur.com/mu6UPM7.png"),
});
// Ensure the color of the image updates if the color variable changes
MainColor.BindValueChanged(ev => RotatingSprite.Colour = ev.NewValue);
}
protected override void Update()
{
base.Update();
// Change the main color after 5 seconds
if (Time.Current > 5000f && MainColor.IsDefault)
MainColor.Value = Color4.Tomato;
RotatingSprite.Rotation += (float)Time.Elapsed * 0.1f;
}
}
The following code usually resides in the base class:
- global variables that are not exclusively useful to running the full game
- loading resources such as fonts and textures
- setting up dependency injection
The following code usually resides in the main Game class:
- adding Drawables to the draw hierarchy
- starting game logic
In some cases, a Game subclass is made for a platform (i.e. Desktop, Android, iOS). This can be used to provide platform-specific functionality such as getting battery status on mobile or having special update checks.
Adding Visual Tests
To add a visual test project to your solution, see Setup Test Project.
Last verified against v2021.714.0.