user-components-super-class.rst 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. .. include:: ../_header.rst
  2. A base class for your components
  3. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. In the previous section, we explored how you can implement behaviors by listening to Phaser events. There, we implemented an ``EventComponent`` base class for all the components.
  5. The components concept provided by |PhaserEditor|_ is flexible. However, we think it is a good idea to propose a way of implementing the components.
  6. For this reason, we created a **UserComponent** class that you can use as the base class for all your components. This class is included in the `phasereditor2d-scripts-core <https://github.com/PhaserEditor2D/phasereditor2d-scripts-core>`_ library and should be included in your project if you created it with a |PhaserEditor|_ project template.
  7. `Learn more about script libraries <./script-node-libraries.html>`_
  8. If you are not using script libraries, you have the option of telling the editor to generate the **UserComponent** class and it will create a file and "write the code for you".
  9. The procedure is simple, just open the context menu of the User Components Editor. In the **Resources** menu, there are options for creating the ``UserComponent.js`` files in different formats:
  10. .. image:: ../images/scene-editor-user-components-create-base-class-file-04012021.webp
  11. :alt: Context menu for creating the UserComponent.js file.
  12. The options are:
  13. * **Create UserComponent.js**: creates a ``UserComponent.js`` file with JavaScript code.
  14. * **Create UserComponent.ts**: creates a ``UserComponent.ts`` file with TypeScript code.
  15. * **Create UserComponent.js (ES Modules)**: creates a ``UserComponent.js`` file with a JavaScript file, using the ES module exporting rules.
  16. * **Create UserComponent.ts (ES Module)**: creates a ``UserComponent.ts`` file with TypeScript code, using the ES module exporting rules.
  17. If the file exists, the editor asks if you confirm replacing it.
  18. You can do this operation just once unless you mess up the file content and want to reset it.
  19. Once the file is created, you can use the **UserComponent** class as the super-class of your components.
  20. Inside the UserComponent class
  21. ``````````````````````````````
  22. In the `Implementing behaviors with the Phaser events <./user-components-start-update-methods.html>`_ section, we explain how a component can register listeners to the Phaser events, for implementing a particular behavior. The **UserComponent** class does the same. It listens for Phaser events and calls special methods that could be overridden in derived classes. These are the methods present by the **UserComponent** class:
  23. .. code::
  24. class UserComponent {
  25. constructor(gameObject) {
  26. // registers the event listeners and call the methods
  27. }
  28. awake() { }
  29. start() { }
  30. update() { }
  31. destroy() { }
  32. }
  33. It registers the event listeners in the constructor:
  34. .. code::
  35. constructor(gameObject) {
  36. this.scene = gameObject.scene;
  37. const listenAwake =
  38. this.awake !== UserComponent.prototype.awake;
  39. const listenStart =
  40. this.start !== UserComponent.prototype.start;
  41. const listenUpdate =
  42. this.update !== UserComponent.prototype.update;
  43. const listenDestroy =
  44. this.destroy !== UserComponent.prototype.destroy;
  45. if (listenAwake) {
  46. this.scene.events.once("scene-awake", this.awake, this);
  47. }
  48. if (listenStart) {
  49. this.scene.events.once(
  50. Phaser.Scenes.Events.UPDATE, this.start, this);
  51. }
  52. if (listenUpdate) {
  53. this.scene.events.on(
  54. Phaser.Scenes.Events.UPDATE, this.update, this);
  55. }
  56. if (listenStart || listenUpdate || listenDestroy) {
  57. gameObject.on(Phaser.GameObjects.Events.DESTROY, () => {
  58. this.scene.events.off(
  59. Phaser.Scenes.Events.UPDATE, this.start, this);
  60. this.scene.events.off(
  61. Phaser.Scenes.Events.UPDATE, this.update, this);
  62. if (listenDestroy) {
  63. this.destroy();
  64. }
  65. });
  66. }
  67. }
  68. But let's go step by step. First, it checks what events to listen to. Notice that the methods **awake()**, **start()**, etc..., are empty. So, if the component instance doesn't provide a different implementation for one of these methods, then it means it can skip calling that method. Then the first thing is to know what methods to call:
  69. .. code::
  70. const listenAwake =
  71. this.awake !== UserComponent.prototype.awake;
  72. const listenStart =
  73. this.start !== UserComponent.prototype.start;
  74. const listenUpdate =
  75. this.update !== UserComponent.prototype.update;
  76. const listenDestroy =
  77. this.destroy !== UserComponent.prototype.destroy;
  78. In the following lines, it adds the listeners to the Phaser events, but only if it is needed:
  79. .. code::
  80. ...
  81. if (listenStart) {
  82. this.scene.events.once(Phaser.Scenes.Events.UPDATE, this.start, this);
  83. }
  84. if (listenUpdate) {
  85. this.scene.events.on(Phaser.Scenes.Events.UPDATE, this.update, this);
  86. }
  87. ...
  88. At the end of the constructor, it registers a listener to the game object's destroy event and removes all the listeners. It does it to avoid calling a method if the object is not active.
  89. When you create a new component, you can update it by just implementing the **update()** method:
  90. .. code::
  91. class RotateObject extends UserComponent {
  92. constructor(gameObject) {
  93. super(gameObject);
  94. }
  95. ...
  96. update() {
  97. // this method is called when the scene
  98. // emits the UPDATE event
  99. this.gameObject.angle += 1;
  100. }
  101. }
  102. The **UserComponent** class is "good enough" for many cases, but you can modify it or use a completely different one. Or use the Phaser events directly in your components.