Create a Dialog Modal Plugin in Phaser 3 – Part 2

In part one of this tutorial, we covered the following topics:

  • How to create a basic plugin in Phaser 3.
  • How to load and register a plugin.
  • Setup the default properties for our plugin.
  • Setup the logic for dynamically placing the dialog window.
  • Created the basic dialog window.

In case you missed it, you can find part one here. In this tutorial, we are going to wrap up our dialog window plugin by adding the following:

  • Adding a close button to the dialog window.
  • Adding logic to show/hide the window.
  • Added logic to set and display dialog text in the window.
  • Add logic to animate the text.

If you didn’t complete part one and would like to continue from here, you can find the code for part one here.

You can see what we will be creating below:


You can download all of the files associated with the source code here.

Let’s get started!

Learn Phaser 3 with our newest Mini-Degree

The HTML5 Game Development Mini-Degree is now available for Pre-Order on Zenva Academy. Learn to code and make impressive games with JavaScript and Phaser 3!

Get Instant Early Access

Closing the window

In order to create our close button, we are going to use Phaser’s Graphic and Text GameObjects. First, we will use the Text GameObject to create the close button, and then we will make it interactive so we can click on it. Then, we will use the GameObject.Graphics to create a small rectangle border around the button to set it apart.

Open up  dialog_plugin.js in your code editor and add the following code below the  _createOuterWindow method at the bottom of the file:

Let’s review the code we just added:

  • First, we created the ‘x’ close button by calling  scene.make.text, and we passed the x and y position of the text, the text that is displayed in the game, and the style of the text we are displaying.
  • In order to have our plugin work with different game sizes, we calculate the x and y positions dynamically.
    • For the x position, we call the  _getGameWidth() method to get the width of the game, and we subtract the padding between the window and the edge of the game. This will give us the position of the right border of our window, and from there we can subtract some pixels to keep the button near the right of the window.
    • For the y position, we call the  _getGameHeight() method to get the height of the game, and then we subtract the height of our dialog window and the padding between the bottom of the window and the bottom of the scene. This will give us the position for the top of the window, and from there we can add a few pixels to keep the close button near the top of the window.
  • Next, we call the  setInteractive method on our  closeBtn GameObject. The  setInteractive method will pass the GameObject to the Input Manager so it can be enabled for input.
  • Lastly, with the  closeBtn enabled for input, we add code to listen for the  pointeroverpointerout, and  pointerdown events.
    • In the  pointerover event, we change the tint of the  closeBtn, and on the  pointerdown event, we remove the tint.
    • In the  pointerdown event, we call a new method called  toggleWindow. This method will be used to show/hide our dialog window.

Next, in the  _createWindow method add the following code at the bottom of the function:

If you reload your game in the browser, you should see the new close button.


If you move your mouse over the close button, it should change colors.


Now, we will add the border around the close button. In  dialog_plugin.js, add the following code below the  _createCloseModalButton method we just added:

In the code above we:

  • Calculated the x and y position of our rectangle border using the same logic we used when we created the close button.
  • Called the  strokeRect method to draw our rectangle without any fill.

To see the new border in our game, we need to add the following code to the bottom of the  _createWindow method:

If you refresh your game, you should see the new border.


Finally, to show/hide the window we need to create the  toggleWindow method. We will use this method to update the  visible property of each GameObject of the window.

In  dialog_plugin.js, add the following code below the   _createCloseModalButtonBorder method we just added:

Now, if you refresh your game and click on the close button, the dialog modal should disappear from the game.


Adding text to the window

With the basic functionality of the window in place, we will now work on adding the actual dialog piece. In  dialog_plugin.js, add the following code below the  toggleWindow method we just added:

Let’s review the code we just added:

  • We added two new methods:  setText and  _setText. The  setText method will be the method we call from  game.js, and it takes the text we want to set in the dialog window as a parameter. Currently, this method just calls the  _setText method, and we will add more code to this method in a bit.
  • The  _setText method is used to create a new Text GameObject and to place the new GameObject in the dialog window.
  • In the  _setText method, we first check to see if the  text property of our plugin has been set, and if it has been, then we destroy that GameObject.
  • Then, we calculate the x position for the new GameObject by adding the  padding property and 10 pixels, which will place the text GameObject just inside the dialog window border.
  • Next, we calculate the y position for the new GameObject by getting the height of the scene. Then we subtract the padding between the window and the bottom of the scene, and then we subtract the height of dialog window. Lastly, we add 10 pixels to place the new GameObject just below the top border of the dialog window.
  • Finally, we call the  scene.make.text method to create the new GameObject. For the style of the new Text GameObject, we pass the  wordWrap property. The  wordWrap property will force the text to stay within the width you pass, by inserting the remaining text on a new line. Phaser does this by looking at the all the spaces within the string and for each one, if the length of the string at that point will go over the word wrap width then it will insert a new line character.

Now, we can test the new  setText method. In  game.js, add the following code at the bottom of the  create function:

If you refresh the game, you should see the next text displayed:


If you replace the  'Testing this' text with a longer string, you can see the word wrap keeps the text inside the dialog window. Update the following line  this.sys.dialogModal.setText('Testing this.'); to be:

If you refresh the game, you should see the new text:


Animating the text

With the dialog window now displaying the text, we are going to add an option to the dialog window that will allow for the text to animate onto the screen. To do this, we are going to take the text that is passed to the  setText method as an argument, and then we will create an array of each character in that string. Then, we are going to set up an event that will slowly display each character of the array.

Now, in  dialog_model.js update the  setText method to match the following code:

The code snippet above is pretty small, but there is a lot going on, so let’s review what we just added:

  • First, we added an additional parameter to the  setText method called  animate. This parameter will allow for you to specify if the text will be animated or not.
  • Then, we reset the properties that are used for animating the text.
    • We take the text that is passed in as an argument, and we use the  split method to create an array of all the characters in that string. We then store this array in the  dialog property.
    • The  eventCounter property is used for displaying each character in the  dialog property.
    • Lastly, we check to see if the  timedEvent property exists and we remove it if it does.
  • Next, we create a variable called  tempText. If the  animate argument was set to true, then we set the  tempText variable to be an empty string, and if the  animate argument was set to false, then we set  tempText to equal the  text argument. We then call the  _setText method and pass it the  tempText variable.
  • Lastly, we check if the  animate argument was set to true, and if it was then we create a new Phaser Time Event and store it in the  timedEvent property.
    • Phaser 3 has a new Clock class, and every state in Phaser 3 has one of these classes tied to it. These clocks allow you to add events to them, and when you add these events you can specify when and how often you would like that event to trigger by passing an Object with that information.
    • The  delay property is used to specify the delay in milliseconds until this event will trigger.
    • The  callback property allows for you to specify which function will be called when the event is triggered. We set this to the  _animateText method, which we haven’t added yet. This method will be used for displaying the text that is stored in the  dialog property.
    • The  callbackScope property allows for you specify the scope in which the callback function will be called. For this event, we passed the scope of the plugin.
    • Finally, the  loop property is used to specify if you want the event to keep triggering at the specified delay. When you set a loop to repeat, you can call the  remove method which will kill the event.

Now that the event is in place, let’s add the  _animateText method. In  dialog_model.js add the following code below the  setText method:

Every time the  _animateText method is called we do the following:

  • We increment the  eventCounter property by one.
  • We update the current text of our Text GameObject by calling the  setText method, and we pass the current text value plus the next character in our  dialog array.
  • Finally, we check to see if we printed out each character in our  dialog array by checking if the  eventCounter property is equal to the length of the array, and if it is then we call the  remove method on our event.

Finally, to test the new animated text, we just need to update the  this.sys.dialogModal.setText('...'); line in  game.js to be:

Now, if you refresh your game you should see the same text appear in the dialog window, but it should appear one character at a time.


Additional Cleanup

Before we wrap up, let’s add a few lines of code to our plugin that will run when the close button is clicked, and when the scene is shut down. This code will be used to kill the Phaser Time Event if it is running, and it will destroy the Text GameObject if it exists.

First, in  dialog_plugin.js add the following code to the  shutdown method:

Then, in the  _createCloseModalButton method add the following code to the  pointerdown event listener function:

Conclusion

With those last pieces of code in place, that brings this tutorial to a close. In summary, this tutorial showed you how to create a plugin for Phaser 3.

I hope you enjoyed this tutorial and found it helpful. If you have any questions, or suggestions on what we should cover next, let us know in the comments below.

Published by

Scott Westover

Scott Westover is a Fullstack Developer who loves coding, writing tutorials, participating in game jams, and learning about new technologies. When he is not coding, he is either spending time with his wife and two kids or is reading a great book. Scott is looking to form connections and share his knowledge. Connect with Scott on Linkedin.

Share this article

1
Leave a Reply

avatar
1 Comment threads
0 Thread replies
1 Followers
 
Most reacted comment
Hottest comment thread
1 Comment authors
jojo Recent comment authors
newest oldest most voted
jojo
Guest
jojo

plugins.installLocal is not a function unfortunately the example code is already outdated, the code below works with phaser 3.11 class MyPlugin extends Phaser.Plugins.ScenePlugin { constructor(scene, pluginManager) { super(scene, pluginManager); } boot() { var eventEmitter = this.systems.events; eventEmitter.on(‘destroy’, this.destroy, this); } // Called when a Scene shuts down, it may then come back again later // (which will invoke the ‘start’ event) but should be considered dormant. shutdown() { if (this.timedEvent) this.timedEvent.remove(); if (this.text) this.text.destroy(); } // called when a Scene is destroyed by the Scene Manager destroy() { this.shutdown(); this.scene = undefined; } // Initialize the dialog modal init(opts) {… Read more »