Fix Stretched Image Distortions in Phaser 3 with 9-Slice Scaling

A better way to scale images that preserves the visual integrity of edges and corners

by on 5 minute read


Are you having image distortion problems when using scaleX or displayWidth to make a button or panel graphic bigger?

You could have multiple versions of the same button asset at different sizes but that doesn't seem very efficient in terms of download size for players or your own time.

Imagine having to update every button size each time a new change is made! 😱

There's a solution for this and it is called 9-slice scaling or Scale 9 Grid.

This technique was first introduced in graphics editing software but has since become commonly used in games as well.

9-slice scaling is a better way to scale images that preserves the visual integrity of edges and corners.

In this article, we'll show you how to use it with the phaser3-nineslice plugin.

Example Set-Up

We are going to keep the code here pretty generic so it should work with your specific project set up.

But just in case you run into problems, we are using the phaser3-snowpack-template, and importing the plugin might be a little bit different. We'll make note of it when we get there.

The example button asset that we'll be using is from Kenney's UI Pack where you'll find many assets that can be used with 9-slice scaling.

Scaling without 9-Slice

Some assets scale better than others when simply using scaleX or displayWidth. Anything with interesting edges or corners are going to scale poorly with this approach.

You'll end up with something that looks like this 👇

Original vs Stretched

You'll certainly see the glaring distortion of the edge outline on the left and right sides. They are scaled proportionately to the change in scaleX while the top edges are left alone since setting displayWidth does not change scaleY.

This is quite undesirable and we can fix it with the phaser3-nineslice plugin.

Installing the Plugin

There are a few ways to install the plugin as the Github repository notes and we are going to use the npm approach with this command:

npm install phaser3-nineslice

Next, we need to add the plugin to the GameConfig in main.js like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import Phaser from 'phaser'

// 🚧 this import can be different 
import NineSlicePlugin from 'phaser3-nineslice'

export default new Phaser.Game({
	type: Phaser.AUTO,
	width: 800,
	height: 600,
	// ...
	plugins: {	// add to plugins.global 👇
		global: [ NineSlicePlugin.Plugin.DefaultCfg ]
	}
})

The first thing to note is the import statement for the plugin. There are named exports like Plugin but the bundled output from phaser3-nineslice doesn't play nicely with Snowpack.

Therefore, we can use the default export instead and then reference the Plugin named export from there.

If your project uses Parcel or Webpack then you should be fine to do this:

1
2
3
4
5
6
7
8
9
import { Plugin as NineSlicePlugin } from 'phaser3-nineslice'

// then..
export default new Phaser.Game({
	// ...
	plugins: {
		global: [ NineSlicePlugin.DefaultCfg ]
	}
}) 

Scaling with 9-Slice

Next, let's use this plugin to create a distortion-free version of our scaled button.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import Phaser from 'phaser'

export default class NineSliceScene extends Phaser.Scene
{
	preload()
    {
        this.load.image('button-bg', 'assets/blue_button06.png')
    }

    create()
    {
		const { width, height } = this.scale
		
		this.add.nineslice(width * 0.5, height * 0.6, 200, 50, 'button-bg', 16)
			.setOrigin(0.5)
    }
}

We create a button with a width of 200 and a height of 50 using the 'button-bg' asset from Kenney.

The last argument of 16 designates the width and height of the corner piece.

9-slice scaling works by splitting an image into 9 pieces:

  • 4 corner pieces
  • 4 edge pieces between the corners (top, left, bottom, right)
  • 1 middle piece

The single middle piece gets scaled in both x and y directions. The top and bottom edge pieces are only scaled in the x direction while the left and right edge pieces are only scaled in the y direction.

The 4 corner pieces are not scaled. They are just placed in the appropriate places depending on the desired width and height.

The result is a scaled image that is distortion-free 👇

Original vs 9-sliced

One final note, the size of the corner piece will vary depending on your image asset.

This particular asset has a raised bottom which means the corner size needs to account for the worst-case scenario. Compare the top left corner with the bottom left corner and you'll notice that the corner values for the bottom left would need to be bigger than the values needed for the top left.

In practice, you can just change the corner value until you find something that looks like it works. A more precise approach would be to measure the largest corner in a graphics program and then use that value. Either way will work. 😎

Next Steps

9-slice scaling should help you create a more space-efficient game by reusing the same assets for different sized in-game elements.

For more information on using 9-slice scaling with modal dialogs and other beginner-friendly introductions to features like gamepad support, sound effects, music, preloading assets, custom fonts, and more check out the Memory Match Extras course.

You'll also learn some coding best practices and how to organize code to help you become a better developer.

Not yet ready for a course? No problem! We are regularly publishing content to help you make games so join our newsletter to keep learning.

Drop your email into the box below 👇

Don't miss any Phaser 3 game development content

Subscribers get exclusive tips & techniques not found on the blog!

Join our newsletter. It's free. We don't spam. Spamming is for jerks.

Phaser 3 UI 9-slice scaling nineslice

Want tips and techniques more suited for you?


You may also like...


Video Guides


Beginner Guides


Articles Recommended For You

How to Load Images Dynamically in Phaser 3

by on

Does your game have a lot of images that not every player sees? Maybe a collectible card game? Loading those images …

6 minute read

Command Pattern to Undo Player Actions

by on

Are you looking for a clean and reusable way to implement undo for player actions? Perhaps you are making a turn-based …

15 minute read

Advanced Logging with the Strategy Pattern

by on

Have you ever tried debugging a problem with your game that only seems to happen in production? The Developer Tools …

7 minute read

State Pattern for Character Movement in Phaser 3

by on

Writing clean and well-organized code is something all game developers aspire to. We want code to be reusable and easy …

7 minute read

Didn't find what you were looking for?


comments powered by Disqus