Skip to content

Sample of coding a pixel perfect 2D game in Unreal Engine 5 with Paper2D

License

Notifications You must be signed in to change notification settings

Nauja/ue4-pixelperfect2d-sample

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

68 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ue4-pixelperfect2d-sample

UE5 GitHub license

Sample of coding a pixel perfect 2D game in Unreal Engine 5 with Paper2D.

Preview

The sole purpose of this sample is to compile all the config required, and common pitfalls to avoid, to correctly render pixel perfect sprites with Paper2D.

For an Unreal Engine 4 version, check the branch ue4.25.

Go further:

Table of contents:

Importing textures

When importing new textures, check that the Texture Group option is correctly set to 2D Pixels (unfiltered), and that the Compression Settings is set to UserInterface2D (RGBA). This will prevent the textures from being antialiased:

TextureGroup

Also, expand the Advanced section, and set the Filter option to Nearest:

SpriteNearest

Scaling up sprites

Here is the charset used for Mario:

Mario

As you can see, the size of one frame is really small (16x32 pixels). If it were displayed as is in UE5, then all your physics would have to be configured with unrealistic values and placing or moving Actors in the editor would become complicated due to their small size.

In order to keep consistent units between the physics and the rendering, all sprites are configured with a pixels per unit value of 0.5, which effectively make them scaled by two:

PixelsPerUnit

This is how the level looks up in editor with this configuration. You can see it is neither too small nor too big:

LevelEditor

True sprite colors

One common problem is that rendered colors are altered by UE5's post processing effects. By default, many post processing effects are enabled and are causing sprites not to render correctly. In this sample, all post processing effects are disabled by setting EngineShowFlags.PostProcessing to 0 with a custom UGameViewportClient class:

void USampleGameViewportClient::Activated(FViewport* InViewport, const FWindowActivateEvent& InActivateEvent)
{
    Super::Activated(InViewport, InActivateEvent);
    
    // Disable post processing effects when the viewport is activated
    EngineShowFlags.PostProcessing = 0;
}

The custom UGameViewportClient class can be configured in Project Settings > Engine > General Settings:

GameViewportClient

Another possibility is to execute the following console command, however it turns out that it doesn't work in shipping builds where console commands are disabled:

APlayerController* Controller = UGameplayStatics::GetPlayerController(GetWorld(), 0);
check(Controller);
Controller->ConsoleCommand(TEXT("showflag.postprocessing 0"));

Also, in Project Settings > Engine > Rendering, make sure to uncheck the Mobile HDR option and all post processing options such as Bloom, Auto Exposure, Anti-Aliasing, etc.

Pixelated font

Importing a custom font to use in a TextRendererComponent seems complicated. This requires the Font Cache Type parameter of your font to be set to Offline. Next, make sure to uncheck the Use Distance Field Alpha option to prevent the font from becoming blurry:

UseDistanceField

Also make sure to uncheck Enable Antialiasing to prevent any antialiasing:

Nearest

The last step is to create a new material to render the font. This can be done by copying the default material used by any TextRendererComponent and modifying it:

Nearest

Snap pixels to grid

In retro games, all sprites were snapped to a pixel grid, ensuring each pixel fitted in the grid and was correctly rendered. In modern games, your Actors may be at positions containing float values, causing pixels to fall off the grid and render incorrectly. Here is an example of moving the same sprite without pixel snapping (left) and with pixel snapping (right):

PixelSnap

To do that, you can copy the default material used for sprites and modify it to offset vertex positions to make sure they fall correctly on the grid:

PixelSnapMaterial

You can do the same for the font material, but by replacing the PixelsPerUnit parameter by 1.

Orthographic camera

This sample uses a static orthographic camera positioned at the center of the screen. The expected resolution of our viewport is 512x448 pixels, the double of the SNES screen resolution, that's why the OrthoWidth parameter must be set to 512 pixels and the Aspect Ratio parameter to 8/7:

Camera

Revisions

2023/11/14:

  • Added a step to set the Filter option to Nearest on sprites. Without that, the game build was all blurry despite the game being crisp in PIE.
  • Replaced ConsoleCommand by creating a custom UGameViewportClient class to disable the flag as console commands are disabled in shipping builds.

Credits

Sprites are coming from The Spriters Resource.

Font from FontSpace.

Pixel snap trick Ludicrous Games

License

Licensed under the MIT License.

About

Sample of coding a pixel perfect 2D game in Unreal Engine 5 with Paper2D

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published