How to Let Players Choose a Character in Phaser 3

Create different characters dynamically based on player selection

by on 6 minute read


Are you making a game that allows the player to choose their character?

Something like Crossy Road? Or an adventure or role-playing game with different character classes?

Making a character select screen is pretty straight forward but how do you send that information to the Game Scene so that the right sprite sheets can be loaded?

And then how do you make it so that all characters can share the same animation code for moving, jumping, standing, etc?

There are ways to do all that and we'll show you how in this article!

Character Select

We are going to assume that you have a Character Select screen of some kind.

Something like this:

By the way, the character art in the example is from MarwaMJ. Lots more on their Itch.io page here.

We are using unique keys for each of the characters such as boy, ghost, and girl.

The corresponding key is saved when the player clicks on a character. It is then sent to the Game Scene when the space bar is pressed to start the game.

Passing Selected Character To Game Scene

You can pass data from one Scene to another using the second parameter of this.scene.start().

For example, we listen to the space bar to be pressed and then we pass the selected character key to the Game Scene:

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

class CharacterSelect extends Phaser.Scene
{
	// other code...

	create()
	{
		// other code...

		this.input.keyboard.once('keydown-SPACE', this.handleContinue, this)
	}

	handleContinue()
	{
		this.scene.start('game', { character: this.selectedKey })
	}
}

We will be using modern JavaScript in this article so you'll see the use of classes and the module import syntax like the example above.

If you are unfamiliar with modern JavaScript then… what are you waiting for? 🤔

We have a free book to help you get started so there's no excuse. Get the book and level up your skills!

Learn to make an Infinite Jumper in Phaser 3 with modern JavaScript!

Drop your email into the box below to get this free 60+ page book and join our newsletter.

Learn more about the book here.

Notice that second parameter being passed to this.scene.start() on line 16.

It is an object with a character property that is given the selected key depending on which character was selected: boy, ghost, or girl.

This object will be passed along to the next Scene as the first argument in the init() and create() methods.

Preloading Sprite Sheet for Selected Character

Once our Game Scene knows which character was selected in the Character Select Scene, we can load the corresponding sprite sheet or atlas.

Be sure to use each character's key as the file name for their sprite sheet or atlas to keep the code simpler.

For example, the ghost character has the key ghost and atlas files named ghost.png and ghost.json.

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

class Game extends Phaser.Scene
{
	/**
	 * @param {{ character: string }} data 
	 */
	init(data)
	{
		this.selectedCharacter = data.character
	}

	preload()
	{
		this.load.atlas(
			this.selectedCharacter,
			`characters/${this.selectedCharacter}.png`,
			`characters/${this.selectedCharacter}.json`
		)
	}
}

We set the passed in character property to the class property this.selectedCharacter in the init() method on line 10.

Then we load that character's atlas by dynamically generating the PNG and JSON file names using this.selectedCharacter.

Next, we can create the selected character and associated animations.

Creating the Selected Character

Let's assume you are lucky and all your characters’ animations have the same number of frames and those frame PNGs uses a standard naming convention.

We will also cover a solution if that is not the case.

But assuming the lucky case ☘️:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// this would be in the Game Scene
create()
{
	// create idle and run animations
	this.anims.create({
		key: 'idle',
		frames: this.anims.generateFrameNames(this.selectedCharacter, {
			start: 0, end: 5, zeroPad: 3, prefix: 'Idle_', suffix: '.png'
		}),
		frameRate: 10,
		repeat: -1
	})

	this.anims.create({
		key: 'run',
		frames: this.anims.generateFrameNames(this.selectedCharacter, {
			start: 0, end: 5, zeroPad: 3, prefix: 'run_', suffix: '.png'
		}),
		frameRate: 10,
		repeat: -1
	})
}

Here we create an 'idle' and 'run' animation from the atlas we loaded in preload().

The art we are using share the same naming convention for individual frames like Idle_000.png, Idle_001.png, run_001.png, run_002.png, etc and both animations are 6 frames long.

This lets us easily share the same code for all characters.

What If I'm in the Bad Place?

If you aren't living in this goldilocks world then one solution is to have a mapping for each character key.

Something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const AnimationDefinition = {
	boy: {
		idle: {
			frames: { start: 0, end: 5, zeroPad: 3, prefix: 'Idle_', suffix: '.png' },
			frameRate: 10
		},
		// other animations...
	},
	// other character keys...
}

You'll probably want this in a separate file that you import into your Game Scene.

The idea is to define the particulars for each character's animations and then use it when creating animations.

Back in the create() method you can use it like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
create()
{
	const idle = AnimationDefinition[this.selectedCharacter].idle
	this.anims.create({
		key: 'idle',
		frames: this.anims.generateFrameNames(this.selectedCharacter, idle.frames),
		frameRate: idle.frameRate,
		repeat: -1
	})

	// other animations...
}

The trick is to take the definition for each character's animation and then use it when generating frame names and setting properties like frameRate.

This lets you move the messier and more specific particulars to a separate file.

You can also avoid this entirely if you use something like Spine to create your characters and animations.

Let us know in the comments below 👇 if you want more information on doing this with Spine animations!

Bonus Tips

You should now be able to create the selected character as normal.

Something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
create()
{
	// create animations...

	const player = this.add.sprite(x, y, this.selectedCharacter)
	
	// TIP #1
	player.setOrigin(0.5, 1)

	player.play('idle')

	// TIP #2
	this.events.once(Phaser.Scenes.Events.SHUTDOWN, () => {
		this.anims.remove('idle')
		this.anims.remove('run')

		// other cleanup...
	})
}

Notice that we use setOrigin(0.5, 1) on line 8. This puts the origin, anchor, or pivot point of the character to where their feet are.

This makes it easy to position every character on the ground no matter how tall they are. 😎

The second tip is more of a reminder.

Remember that animations created with the AnimationManager are global so you'll have to delete them if you want to use the same keys again when the Scene is reused.

Like going back to the Character Select screen and playing the game again.

Next Steps

You should have everything you need to implement dynamically chosen character skins!

From here you can take what you've learned to create characters that have different or special abilities. Let me know in the comments below if that is something you'd like to see!

Also, be sure to sign up for our newsletter so you don't miss any future Phaser 3 game development tips and techniques!

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 character select animations scenes

Want tips and techniques more suited for you?


You may also like...


Video Guides


Beginner Guides


Articles Recommended For You

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

by on

Are you having image distortion problems when scaling to make a button or panel graphic bigger? Multiple versions of the …

5 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