MI PYTHON | Phaser JS Tutorial Two | Extending Classes and Sprite Movement

Overview:

This lesson focuses on sprite movement within Phaser. The Phaser game engine gives us different choices for movement styles. We have keyboard / joystick and mouse follow type movement today. We will focus on these two movement methods below. Before we do this we will also start organizing our game objects into a class based structure. This will help us in scaling up the game down the road.

Extending Phaser Classes:

In order to better keep track of our game objects. We are going to implement an Object Oriented class based structure. This will help us to keep track of all our game objects and instance’s in the future. Lets start with the below code for our GameObj class.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class GameObj extends Phaser.Physics.Arcade.Sprite  {  

    constructor(scene,name,description,image, x, y) {
    super(scene);    
    this.scene = scene;
    this.name = name;
    this.description = description;
    ///   SET INTERACTIVE() FOR COLLISIONS
    this.avatar = this.scene.physics.add.image(x,y, image).setInteractive();
    this.x = x;
    this.y = y;
    }

    status() {
      console.log(this.name);
      console.log(this.description);
    }
}

It’s ok if you don’t completely understand what’s happening here. It will become clearer as we move along. In the above code example.

  • Invoke our class GameObj extending from Phaser.Physics.Arcade.Sprite.
  • Initialize our GameObj class with the game scene, name, description, sprite image, x position, and y position as arguments.
  • The
    1
    super
    keyword is used to call the constructor of its parent class to access the parent’s properties and methods. We pass super into our class constructor to give our class access to the physics and other methods of Phaser.
  • This.scene is our game scene within Phaser.
  • This.name gives each instance an individual name.
  • this.description gives each instance and individual description.
  • this.avatar defines our GameObj’s sprite to display within the Phaser game canvas. The physics process’s are actually running on this object within our class instance.
  • this.x defines the instance’s avatar’s x coordinate position on the canvas
  • this.y defines the instance’s avatar’s y coordinate position on the canvas
  • Lastly we have a status() method that displays the instance’s name and description.

After we initialize our GameObj class ,lets make a seperate class for our Player. In our Player class we are going to include a few more methods then our GameObj class. Most importantly, our movement methods for the Player.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
 class Player extends Phaser.Physics.Arcade.Sprite  {  

    constructor(scene,name,description,image, x, y) {
    super(scene);    
    this.scene = scene;
    this.name = name;
    this.description = description;
    ///  SET INTERACTIVE FOR COLLISIONS
    this.avatar = this.scene.physics.add.image(x,y, image).setInteractive();
    this.x = x;
    this.y = y;
    }

    status() {
      console.log(this.name);
      console.log(this.description);
    }

    keyBoardMovement() {
      let cursors = this.scene.input.keyboard.createCursorKeys();

      if (cursors.left.isDown)
      {
        console.log("LEFT");
        this.avatar.x -= 10;
      }
      else if (cursors.right.isDown)
      {
        console.log("RIGHT");
        this.avatar.x += 10;
      }

      if (cursors.up.isDown)
      {
        console.log("UP");
        this.avatar.y -= 10;
      }
      else if (cursors.down.isDown)
      {
      console.log("DOWN");
      this.avatar.y += 10;
      }

    }

    pointerMovement(x,y) {

      if (this.scene.input.activePointer.isDown)
      {
          this.avatar.x = x
          this.avatar.y = y
 
      }

      else
      {

      }



    }


}

Movement:

In the above code example. We invoke our Player class exactly as we did the GameObj class. The only difference being the added methods of keyBoardMovement() and pointerMovement().

  • Within our keyBoardMovement() method we are using if statements to check to see if this.scene.input.keyboard.createCursorKeys() is down. If the arrow keys are pressed our Player.avatar sprite will move in that particular direction on the Phaser canvas. Incremented by 10 pixels.
  • The pointerMovement() method allows us to move our Player.avatar sprite around with mouse input. We are checking if this.scene.input.activePointer.isDown = true. If the previous is true then we move the Player to that x and y position on the canvas instantly. If you will notice in the next step. The Player does move instantly where we click and can “Teleport” around the game screen. We will address that shortly.

In the below code. Lets Instantiate our class objects after our class initialization.

For both our Player and GameObj instances we will define them with the below arguments:

  • “this” is the game scene that the instance is spawned into references the Phaser game object.
  • A unique name in a string.
  • a unique description in a string.
  • The key to the image we want for our objects avatar
  • The object avatar’s X position on the canvas.
  • The object avatar’s Y position on the canvas.

Organize our code and run the scene.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
const BLACK = "#1c2333"
const RED = ""
const WHITE = ""

const config = {
    type: Phaser.AUTO,
    width: 400,
    height: 600,
    backgroundColor: '#1c2333',
    physics: {
        default: 'arcade',
        arcade: {
            gravity: {
                y: 0
            },
            debug: true
        }
    },
    scene: {
        preload: preload,
        create: create,
        update: update
    }
};

const game = new Phaser.Game(config);


function preload() {
  this.sprite1 = this.load.image('sprite1', 'assets/repl.png');
   
}

function create() {


 class Player extends Phaser.Physics.Arcade.Sprite  {  

    constructor(scene,name,description,image, x, y) {
    super(scene);    
    this.scene = scene;
    this.name = name;
    this.description = description;
    ///  SET INTERACTIVE FOR COLLISIONS
    this.avatar = this.scene.physics.add.image(x,y, image).setInteractive();
    this.x = x;
    this.y = y;
    }

    status() {
      console.log("STATUS");
    }

    keyBoardMovement() {
      let cursors = this.scene.input.keyboard.createCursorKeys();

      if (cursors.left.isDown)
      {
        console.log("LEFT");
        this.avatar.x -= 10;
      }
      else if (cursors.right.isDown)
      {
        console.log("RIGHT");
        this.avatar.x += 10;
      }

      if (cursors.up.isDown)
      {
        console.log("UP");
        this.avatar.y -= 10;
      }
      else if (cursors.down.isDown)
      {
      console.log("DOWN");
      this.avatar.y += 10;
      }

    }

    pointerMovement(x,y) {

      if (this.scene.input.activePointer.isDown)
      {
          this.avatar.x = x
          this.avatar.y = y
 
      }

      else
      {

      }
    }
  }


class GameObj extends Phaser.Physics.Arcade.Sprite  {  

    constructor(scene,name,description,image, x, y) {
    super(scene);    
    this.scene = scene;
    this.name = name;
    this.description = description;
    ///   SET INTERACTIVE() FOR COLLISIONS
    this.avatar = this.scene.physics.add.image(x,y, image).setInteractive();
    this.x = x;
    this.y = y;
    }

    status() {
      console.log("STATUS");
    }

}

player = new Player(this,"PLAYER","PLAYER DESC","sprite1",100,100);
object = new GameObj(this,"GAME OBJECT","GAME OBJECT DESC","sprite1",100,100);
object2 = new GameObj(this,"GAME OBJECT 2","GAME OBJECT 2 DESC","sprite1",400,400);

}

function update() {

  const mousePosition = {
  x:this.input.activePointer.x,
  y:this.input.activePointer.y,

  }
 
  console.log(mousePosition.x);
  console.log(mousePosition.y);
 
  player.keyBoardMovement()
  player.pointerMovement(mousePosition.x,mousePosition.y)


}

After running the above code you should see that the Player object that we instanced is controllable with the mouse upon left click being true. The Player avatar sprite instantly goes to that particular x / y position though. This may or may not be the movement behavior you want in your game. In this case though we are going to use the Phaser built in method of physics.moveToObject(). Handily, Phaser calculates everything we need. Both for smooth mouse and touch screen movement. We can use this method for both the Player’s movement towards the mouse as well as other Game Objects movement towards the player or other waypoints. Lets add that method below in the update method of our Phaser project.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function update() {

  const mousePosition = {
  x:this.input.activePointer.x,
  y:this.input.activePointer.y,

  }
 
  console.log(mousePosition.x);
  console.log(mousePosition.y);
 
  player.keyBoardMovement()
  //player.pointerMovement(mousePosition.x,mousePosition.y)

  ///   PLAYER SMOOTH MOUSE MOVMENT
  this.physics.moveToObject(player.avatar, mousePosition, 400);

  ///    FOLLOW BEHAVIOR
  this.physics.moveToObject(object.avatar, player.avatar, 200);

}

In the above example. We are calling this.physics.moveToObject().

  • player.avatar is the first argument. This references our Players physics sprite object. This object moves to the next object.
  • mousePosition references our constant above that calculates the active pointers x and y position on the Phaser canvas.
  • 400 is the speed at which the object moves towards the target object.

We can also get a very easy to use follow behavior with this method. In our next declaration we pass the object.avatar as our source object and the player.avatar as the target object. Run the code. We should now see the game object following the player.

Final code should look like below.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
const BLACK = "#1c2333"
const RED = ""
const WHITE = ""

const config = {
    type: Phaser.AUTO,
    width: 400,
    height: 600,
    backgroundColor: '#1c2333',
    physics: {
        default: 'arcade',
        arcade: {
            gravity: {
                y: 0
            },
            debug: true
        }
    },
    scene: {
        preload: preload,
        create: create,
        update: update
    }
};

const game = new Phaser.Game(config);


function preload() {
  this.sprite1 = this.load.image('sprite1', 'assets/repl.png');
   
}

function create() {

 //input = this.input;

 class Player extends Phaser.Physics.Arcade.Sprite  {  

    constructor(scene,name,description,image, x, y) {
    super(scene);    
    this.scene = scene;
    this.name = name;
    this.description = description;
    ///  SET INTERACTIVE FOR COLLISIONS
    this.avatar = this.scene.physics.add.image(x,y, image).setInteractive();
    this.x = x;
    this.y = y;
    }

    status() {
      console.log("STATUS");
    }

    keyBoardMovement() {
      let cursors = this.scene.input.keyboard.createCursorKeys();

      if (cursors.left.isDown)
      {
        console.log("LEFT");
        this.avatar.x -= 10;
      }
      else if (cursors.right.isDown)
      {
        console.log("RIGHT");
        this.avatar.x += 10;
      }

      if (cursors.up.isDown)
      {
        console.log("UP");
        this.avatar.y -= 10;
      }
      else if (cursors.down.isDown)
      {
      console.log("DOWN");
      this.avatar.y += 10;
      }

    }

    pointerMovement(x,y) {

      if (this.scene.input.activePointer.isDown)
      {
          this.avatar.x = x
          this.avatar.y = y
 
      }

      else
      {

      }
    }
  }


class GameObj extends Phaser.Physics.Arcade.Sprite  {  

    constructor(scene,name,description,image, x, y) {
    super(scene);    
    this.scene = scene;
    this.name = name;
    this.description = description;
    ///   SET INTERACTIVE() FOR COLLISIONS
    this.avatar = this.scene.physics.add.image(x,y, image).setInteractive();
    this.x = x;
    this.y = y;
    }

    status() {
      console.log("STATUS");
    }

}

player = new Player(this,"PLAYER","PLAYER DESC","sprite1",100,100);
object = new GameObj(this,"GAME OBJECT","GAME OBJECT DESC","sprite1",100,100);
object2 = new GameObj(this,"GAME OBJECT 2","GAME OBJECT 2 DESC","sprite1",400,400);

}

function update() {

  const mousePosition = {
  x:this.input.activePointer.x,
  y:this.input.activePointer.y,

  }
 
  console.log(mousePosition.x);
  console.log(mousePosition.y);
 
  player.keyBoardMovement()
  //player.pointerMovement(mousePosition.x,mousePosition.y)

  ///   PLAYER SMOOTH MOUSE MOVMENT
  this.physics.moveToObject(player.avatar, mousePosition, 400);

  ///    FOLLOW BEHAVIOR
  this.physics.moveToObject(object.avatar, player.avatar, 200);

}

Repl for this section here:

https://replit.com/@MANDREWS85/phasertutorial2movementfinal#script.js

Check out MI PYTHON Phaser JS Tutorial 3 here.

Related posts