Aller au contenu

Déplacements 1/2

Déplacement par pixels

Déplacement X et Y

 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
class Game extends Phaser.Scene {

  create() {
    this.speed = 4;
    this.cursors = this.input.keyboard.createCursorKeys();
    this.player = this.add.circle(400, 200, 10, 0xFFFF00);
  }

  update() {
    this.move();
  }

  move() {
    // Mouvement horizontal
    if (this.cursors.left.isDown) {
      this.player.x -= this.speed;
    } else if (this.cursors.right.isDown) {
      this.player.x += this.speed;
    }

    // Mouvement vertical
    if (this.cursors.up.isDown) {
      this.player.y -= this.speed;
    } else if (this.cursors.down.isDown) {
      this.player.y += this.speed;
    }
  }

}

La vitesse par pixel

Avec cette méthode de déplacement, la vitesse est dépendante du taux de rafraichissement (frameRate)! Si on a un frameRate de 30 au lieu de 60, le personne va se déplacer 2x moins rapidement.

Contraintes de déplacement

Maintenant, limitons le déplacement à l'intérieur du canvas.

 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
class Game extends Phaser.Scene {

  create() {
    this.speed = 4;
    this.cursors = this.input.keyboard.createCursorKeys();
    this.player = this.add.circle(400, 200, 10, 0xFFFF00);
  }

  update() {
    this.move();
    this.setConstraint();
  }

  move() {
    // Mouvement horizontal
    if (this.cursors.left.isDown) {
      this.player.x -= this.speed;
    } else if (this.cursors.right.isDown) {
      this.player.x += this.speed;
    }

    // Mouvement vertical
    if (this.cursors.up.isDown) {
      this.player.y -= this.speed;
    } else if (this.cursors.down.isDown) {
      this.player.y += this.speed;
    }
  }

  setConstraint() {
    const halfWidth = this.player.radius;
    const halfHeight = this.player.radius;

    // Contraintes horizontales
    if (this.player.x < halfWidth) {
      this.player.x = halfWidth;
    } else if (this.player.x > config.width - halfWidth) {
      this.player.x = config.width - halfWidth;
    }

    // Contraintes verticales
    if (this.player.y < halfHeight) {
      this.player.y = halfHeight;
    } else if (this.player.y > config.height - halfHeight) {
      this.player.y = config.height - halfHeight;
    }
  }

}

Téléportation cyclique (wrap-around)

La téléportation cyclique est le concept de base du jeu Asteroids 1979.

 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
class Game extends Phaser.Scene {

  create() {
    this.speed = 4;
    this.cursors = this.input.keyboard.createCursorKeys();
    this.player = this.add.circle(400, 200, 10, 0xFFFF00);
  }

  update() {
    this.move();
    this.wrapAround();
  }

  move() {
    // Mouvement horizontal
    if (this.cursors.left.isDown) {
      this.player.x -= this.speed;
    } else if (this.cursors.right.isDown) {
      this.player.x += this.speed;
    }

    // Mouvement vertical
    if (this.cursors.up.isDown) {
      this.player.y -= this.speed;
    } else if (this.cursors.down.isDown) {
      this.player.y += this.speed;
    }
  }

  wrapAround(){
    // Wrap-around horizontal
    if (this.player.x < 0) {
      this.player.x = config.width;
    } else if (this.player.x > config.width) {
      this.player.x = 0;
    }

    // Wrap-around verticale
    if (this.player.y < 0) {
      this.player.y = config.height;
    } else if (this.player.y > config.height) {
      this.player.y = 0;
    }
  }

}

Déplacement par physique

Pour activer la physique dans notre jeu vidéo, il suffit d'ajouter le code ci-dessous.

init.js
const config = {
  width: 800,
  height: 400,
  scene: Example,
  physics: {
    default: "arcade",
    arcade: {
      debug: true
    }
  }
};

Pour qu'un élément soit soumis au système de physique, il faut l'ajouter avec la commande this.physics.add() :

Méthode A
class Example extends Phaser.Scene {
  preload () {
    this.load.image('lemming', './assets/sprites/lemming.png');
  }

  create () {
    // this.sprite = this.add.image(400, 300, 'lemming');
    this.sprite = this.physics.add.image(400, 300, 'lemming');
  }

  update () {}
}

Sinon, on peu aussi ajouter un élément existant avec la commande this.physics.add.existing() :

Méthode B
class Example extends Phaser.Scene {
  preload () {
    this.load.image('lemming', './assets/sprites/lemming.png');
  }

  create () {
    this.sprite = this.add.image(400, 300, 'lemming');
    this.physics.add.existing(this.sprite);
  }

  update () {}
}

Ainsi, on pourra déplacer notre image avec un vecteur de vitesse 😱 !

Vecteur de vitesse ?

Un vecteur de vitesse, ou simplement vélocité, est une façon simple de représenter une direction et une vitesse (magnitude).

Dans l'exemple ci-dessous, le trait vert représente la vélocité du personnage.

Déplacement X, Y et ... V

En utilisant le système de physique, on peut déplacer n'importe quel objet en lui assignant une vélocité.

Contrairement au déplacement par pixels, cette méthode est davantage relative au temps plutôt qu'au frameRate.

Un déplacement de 200 pour un vecteur signifie que l’objet se déplacera de 200 pixels en 1 seconde1.

Lorsqu'on ajoute un objet au système de physique, une propriété body est créée. C’est cette propriété qui permet de manipuler les caractéristiques physiques de l’objet, comme la vélocité, l’accélération, la gravité, etc.

Voici comment on assigne une vélocité :

this.monBonhomme.body.setVelocityX(200);
this.monBonhomme.body.setVelocityY(200);
class Example extends Phaser.Scene {
  preload() {}

  create() {
    this.speed = 200;
    this.cursors = this.input.keyboard.createCursorKeys();
    this.player = this.add.circle(400, 200, 10, 0xcccc00);
    this.physics.add.existing(this.player);
  }

  update() {
    this.move();
  }

  move() {
    // Mouvement horizontal
    if (this.cursors.left.isDown) {
      this.player.body.setVelocityX(-this.speed);
    } else if (this.cursors.right.isDown) {
      this.player.body.setVelocityX(this.speed);
    } else {
      this.player.body.setVelocityX(0);
    }

    // Mouvement vertical
    if (this.cursors.up.isDown) {
      this.player.body.setVelocityY(-this.speed);
    } else if (this.cursors.down.isDown) {
      this.player.body.setVelocityY(this.speed);
    } else {
      this.player.body.setVelocityY(0);
    }
  }
}

Contrainte des rebords

Grâce au système de physique, il est très facile d’empêcher un objet de sortir des limites du jeu en utilisant la fonction setCollideWorldBounds().

Cette méthode permet de faire en sorte que l’objet se heurte aux bords du monde défini et reste à l’intérieur des limites.

class Example extends Phaser.Scene {
  preload() {}

  create() {
    this.speed = 200;
    this.cursors = this.input.keyboard.createCursorKeys();
    this.player = this.add.circle(400, 200, 10, 0xcccc00);
    this.physics.add.existing(this.player);
    this.player.body.setCollideWorldBounds();
  }

  update() {
    this.move();
  }

  // ...
}

Téléportation cyclique avec physique

C'est bien plus facile avec la physique ! Il suffit d'ajouter la commande this.physics.world.wrap().

La fonction wrap() recoit deux paramètres :

  • gameObject (obligatoire) : Il s’agit de l’objet (ou des objets) que tu veux faire “wrap”. Cela peut être un objet individuel ou un tableau d’objets.
  • padding (facultatif) : C’est une marge en pixels ajoutée autour des limites du monde avant que l’objet ne soit “wrap”. Par exemple, si tu donnes une valeur de 24, l’objet doit être 24 pixels en dehors des limites avant d’être repositionné.
class Example extends Phaser.Scene {
  preload() {}

  create() {
    this.speed = 200;
    this.cursors = this.input.keyboard.createCursorKeys();
    this.player = this.add.circle(400, 200, 10, 0xcccc00);
    this.physics.add.existing(this.player);
    this.player.body.setCollideWorldBounds(false); // Facultatif, mais clair
  }

  update() {
    this.move();
    this.wrapAround();
  }

  move() {
    // Mouvement horizontal
    if (this.cursors.left.isDown) {
      this.player.body.setVelocityX(-this.speed);
    } else if (this.cursors.right.isDown) {
      this.player.body.setVelocityX(this.speed);
    } else {
      this.player.body.setVelocityX(0);
    }

    // Mouvement vertical
    if (this.cursors.up.isDown) {
      this.player.body.setVelocityY(-this.speed);
    } else if (this.cursors.down.isDown) {
      this.player.body.setVelocityY(this.speed);
    } else {
      this.player.body.setVelocityY(0);
    }
  }

  wrapAround() {
    this.physics.world.wrap(this.player, 20);
  }
}

Obstacles

L'ajout d'obstacle est possible en utilisant la fonction collider() du système de physique.

Il est également possible de rendre un objet immobile avec la fonction objet.setImmovable(true) et possible de désactiver la gravité avec objet.body.setAllowGravity(false).

class Example extends Phaser.Scene {
  preload() {}

  create() {
    this.speed = 200;
    this.cursors = this.input.keyboard.createCursorKeys();
    this.player = this.add.circle(400, 200, 10, 0xcccc00);
    this.physics.add.existing(this.player);
    this.player.body.setCollideWorldBounds();

    // Ajout d’un obstacle
    this.obstacle = this.physics.add.rectangle(222, 195, 45, 160, 0xff0000);
    this.obstacle.body.setImmovable();

    // Activation de la collision
    this.physics.add.collider(this.player, this.obstacle);
  }

  update() {
    this.move();
  }

  // ...
}

Gravité

On peut très facilement activer un système de gravité. Voici 3 manières :

Gravité globale dans init.js
const config = {
  width: 800,
  height: 400,
  scene: Example,
  physics: {
    default: "arcade",
    arcade: {
      gravity: {
        x: 0,
        y: 300
      },
      debug: true
    }
  }
};
Gravité globale dans le create()
this.physics.world.gravity.y = 300;
Gravité sur un objet spécifique dans le create()
this.monBonhomme.body.setGravityY(300);
class Example extends Phaser.Scene {
  preload() {}

  create() {
    this.player = this.add.circle(400, 200, 10, 0xcccc00);
    this.physics.add.existing(this.player);

    // Active la gravité
    this.player.body.setGravityY(300);
  }

  update() {}
}

Rebond

Afin d'ajouter un comportement lors d'une collision avec un objet, on peut configurer la notion de rebond avec la commande setBounce().

Paramètre de la fonction setBounce : Un nombre qui définit l'élasticité du corps. La valeur est toujours positive et se situe dans la plage (0, 1).

Une valeur de 0,8 signifie que le corps peut rebondir avec environ 80 % de son énergie cinétique.

class Example extends Phaser.Scene {
  preload() {}

  create() {
    this.player = this.add.circle(400, 200, 10, 0xcccc00);
    this.physics.add.existing(this.player);
    this.player.body.setGravityY(300);
    this.player.body.setBounce(0.8);
  }

  update() {}
}

Friction

Voici les différentes fonction pour appliquer de la friction :

  • this.player.body.setDrag(0)
  • this.player.body.setDragX(0)
  • this.player.body.setDragY(0)

Accélération progressive

Voici les différentes fonction pour appliquer un accélération progressive :

  • this.player.body.setAcceleration(0)
  • this.player.body.setAccelerationX(0)
  • this.player.body.setAccelerationY(0)
  • this.player.body.setMaxVelocity(300, 300);

Exercices

Exercice - Phaser
Un voyage pixelisé

Exercice - Phaser
Un voyage plutôt physique

Exercice - Phaser
Ça commence par une feuille

Exercice - Phaser
Puis c'est l'automne

Exercice - Phaser
Peglin


  1. À condition qu’il n’y ait aucune autre force ou influence (comme la gravité ou la friction) qui affecte son mouvement