1

I search and I don't find anything to do quest in Phaser3. I want to do a quest like (it's example).

Go talk to 'Jerry'.

Take a sword and give it to 'Jerry'.

When you finish to talk it unlock a door or other, but I need to know how can I check if he talk to the PNJ and how to set a quest simply

I found rexquestplugin but I do'nt know how to use it and there is no website or other it talk about RexQuest

My code for now if you need to know something about the game:

class SceneStart extends Phaser.Scene {

  constructor() {
    super({key: 'sceneStart'})
  }

  //Chargement des images
  preload() {
    this.load.plugin('rexquestplugin', 'https://raw.githubusercontent.com/rexrainbow/phaser3-rex-notes/master/dist/rexquestplugin.min.js', true);

    this.load.image("player", "javascript/assets/player.png");
    this.load.image("run1", "javascript/assets/run1.png");
    this.load.image("run2", "javascript/assets/run2.png");
    this.load.image("playerLeftRun1", "javascript/assets/playerLeftRun1.png");
    this.load.image("playerLeftRun2", "javascript/assets/playerLeftRun2.png");
    this.load.image("door", "javascript/assets/doors.png");
    this.load.image("wall", "javascript/assets/walls.png");
    /** 
    this.load.image("fireStart1", "javascript/assets/fireStart1.png");
    this.load.image("fireStart2", "javascript/assets/fireStart2.png");
    this.load.image("fireStart3", "javascript/assets/fireStart3.png");
    */
  }

  create() {
    cursor = this.input.keyboard.createCursorKeys(); //touches des fleches
    platforms = this.physics.add.staticGroup();

    //Les animations 
    this.anims.create({
      key : "playerWalkUp",
      frames : [
        {key : "run1"},
        {key : "run2"}],
      frameRate : 7,
      repeat : 0
    })

    this.anims.create({
      key : "playerWalkLeft",
      frames : [
        {key : "playerLeftRun1"},
        {key : "playerLeftRun2"}],
      frameRate : 7,
      repeat : 0
    })

    /**
     * this.anims.create({
     * key : "fireMouvement",
     * frames : [
     * {key : "fireStart1"},
     * {key : "fireStart2"},
     * {key : "fireStart3"}],
     * framerate : 7,
     * repeat : -1
     * })
     */

    player = this.physics.add.sprite((w / 2), h, "player"); //joueur
    player.setScale(1, 1);
    player.body.setSize(30, 35);
    
    wall1 = this.add.sprite(200, 146, "wall");
    wall1.setScale(0.3);

    wall2 = this.add.sprite(200, 445, "wall");
    wall2.setFlip(false, true);
    wall2.setScale(0.3);

    wall3 = this.add.sprite((w - 200), 146, "wall");
    wall3.setScale(0.3);

    wall4 = this.add.sprite((w - 200), 445, "wall");
    wall4.setFlip(false, true);
    wall4.setScale(0.3);

    doorStart = this.physics.add.staticSprite((w / 2), 28, "door"); //Porte principale
    doorStart.setScale(0.3);
    doorStart.body.setSize(300, 55);
    doorStart.body.setOffset(-56, 472);
    doorStart.rotation += -20.42;

    platforms.add(wall1);
    platforms.add(wall2);
    platforms.add(wall3);
    platforms.add(wall4);
    this.physics.add.collider(platforms, player); //collision
    player.setCollideWorldBounds(true); //collision avec la bordure
    
    //Fonction de collision qui éxecute le code dedans quand la fonctions est appelé
    function collision() {
      this.scene.start("labyrintheStart");
    }
    this.physics.add.collider(player, doorStart, collision, undefined, this); 
  }

  update() {

    // Tous les mouvement sont controler par ce code
  
    if (cursor.left.isDown){
      player.setVelocityX(-200); //vitesse de deplacements
      player.anims.play("playerWalkLeft", true); //animations du personnage
      player.setFlip(false, false); //oriantation de l'image
    } else if (cursor.right.isDown){
      player.setVelocityX(200);
      player.anims.play("playerWalkLeft", true);
      player.setFlip(true, false);
    } else if (cursor.up.isDown){
      player.setVelocityY(-200);
      player.anims.play("playerWalkUp", true);
      player.setFlip(false, false);
    } else if (cursor.down.isDown){
      player.setVelocityY(200);
      player.anims.play("playerWalkUp", true);
      player.setFlip(false, true);
    } else {
      player.setVelocity(0);
      player.setTexture("player");
    }

    if ((cursor.left.isDown && cursor.up.isDown) || (cursor.left.isDown && cursor.right.isDown) || (cursor.left.isDown && cursor.down.isDown) || (cursor.right.isDown && cursor.up.isDown) || (cursor.right.isDown && cursor.down.isDown)){
      player.setVelocity(0);
      player.setTexture("player");
    }

  //--------
  }
}

class LabyrintheStart extends Phaser.Scene {

  constructor() {
   super({key: 'labyrintheStart'});
  }
  preload() {
    this.load.plugin('rexquestplugin', 'https://raw.githubusercontent.com/rexrainbow/phaser3-rex-notes/master/dist/rexquestplugin.min.js', true);

    this.load.image("player", "javascript/assets/player.png");
    this.load.image("run1", "javascript/assets/run1.png");
    this.load.image("run2", "javascript/assets/run2.png");
    this.load.image("playerLeftRun1", "javascript/assets/playerLeftRun1.png");
    this.load.image("playerLeftRun2", "javascript/assets/playerLeftRun2.png");
    this.load.image("wall", "javascript/assets/walls.png"); 
    this.load.image("door", "javascript/assets/doors.png");
  }

  create() {
    cursor = this.input.keyboard.createCursorKeys();
    platforms = this.physics.add.staticGroup();


    this.anims.create({
      key : "playerWalkUp",
      frames : [
        {key : "run1"},
        {key : "run2"}],
      frameRate : 7,
      repeat : 0
    })
    
    this.anims.create({
      key : "playerWalkLeft",
      frames : [
        {key : "playerLeftRun1"},
        {key : "playerLeftRun2"}],
      frameRate : 7,
      repeat : 0
    })

    player = this.physics.add.sprite(34, h, "player");
    player.setScale(1, 1);
    player.body.setSize(30, 35);
        
    wall1 = this.add.sprite(9, h - 126, "wall");
    wall1.setScale(0.05);

    doorDroite = this.physics.add.staticSprite(w - 35, (h / 2) - 20, "door"); 
    doorDroite.setSize(18, 80);
    doorDroite.setScale(0.08);

    platforms.add(wall1);
    this.physics.add.collider(platforms, player);
    player.setCollideWorldBounds(true);

    function collisionDroite() {
      this.scene.start("labyrintheDeux");
    }
    
    this.physics.add.collider(player, doorDroite, collisionDroite, undefined, this);
  }

  update() {

      if (cursor.left.isDown){
        player.setVelocityX(-200);
        player.anims.play("playerWalkLeft", true);
        player.setFlip(false, false);
      } else if (cursor.right.isDown){
        player.setVelocityX(200);
        player.anims.play("playerWalkLeft", true);
        player.setFlip(true, false);
      } else if (cursor.up.isDown){
        player.setVelocityY(-200);
        player.anims.play("playerWalkUp", true);
        player.setFlip(false, false);
      } else if (cursor.down.isDown){
        player.setVelocityY(200);
        player.anims.play("playerWalkUp", true);
        player.setFlip(false, true);
      } else {
        player.setVelocity(0);
        player.setTexture("player");
      }
  
      if ((cursor.left.isDown && cursor.up.isDown) || (cursor.left.isDown && cursor.right.isDown) || (cursor.left.isDown && cursor.down.isDown) || (cursor.right.isDown && cursor.up.isDown) || (cursor.right.isDown && cursor.down.isDown)){
        player.setVelocity(0);
        player.setTexture("player");
      }
  }
}
/**
class LabyrintheDeux extends Phaser.Scene {

  constructor() {
    super({key: "labyrintheDeux"});
  }

  preload() {
    this.load.plugin('rexquestplugin', 'https://raw.githubusercontent.com/rexrainbow/phaser3-rex-notes/master/dist/rexquestplugin.min.js', true);

    this.load.image("player", "javascript/assets/player.png");
    this.load.image("run1", "javascript/assets/run1.png");
    this.load.image("run2", "javascript/assets/run2.png");
    this.load.image("playerLeftRun1", "javascript/assets/playerLeftRun1.png");
    this.load.image("playerLeftRun2", "javascript/assets/playerLeftRun2.png");
    this.load.image("wall", "javascript/assets/wall.png"); 
    this.load.image("doorStart", "javascript/assets/door.png");
  }

  create() {
    cursor = this.input.keyboard.createCursorKeys();
    platforms = this.physics.add.staticGroup();

    this.anims.create({
      key : "playerWalkUp",
      frames : [
        {key : "run1"},
        {key : "run2"}],
      frameRate : 7,
      repeat : 0
    })

    this.anims.create({
      key : "playerWalkLeft",
      frames : [
        {key : "playerLeftRun1"},
        {key : "playerLeftRun2"}],
      frameRate : 7,
      repeat : 0
    })

    player = this.physics.add.sprite((w - w) + 70, h, "player");
    player.setScale(1, 1);
    player.body.setSize(30, 35);
    
    wall = this.add.sprite((w - w) + 6, h - 126, "wall");

    door = this.physics.add.staticSprite((w / 2) - 20, 30, "door");
    door.rotation += 20.42;
    door.setSize(50, 10);

    platforms.add(wall);
    this.physics.add.collider(platforms, player);
    player.setCollideWorldBounds(true);
    
    function collision() {
      this.scene.start("");
    }
    
    this.physics.add.collider(player, door, collision, undefined, this);
  }

  update() {
  
      if (cursor.left.isDown){
        player.setVelocityX(-200);
        player.anims.play("playerWalkLeft", true);
        player.setFlip(false, false);
      } else if (cursor.right.isDown){
        player.setVelocityX(200);
        player.anims.play("playerWalkLeft", true);
        player.setFlip(true, false);
      } else if (cursor.up.isDown){
        player.setVelocityY(-200);
        player.anims.play("playerWalkUp", true);
        player.setFlip(false, false);
      } else if (cursor.down.isDown){
        player.setVelocityY(200);
        player.anims.play("playerWalkUp", true);
        player.setFlip(false, true);
      } else {
        player.setVelocity(0);
        player.setTexture("player");
      }
  
      if ((cursor.left.isDown && cursor.up.isDown) || (cursor.left.isDown && cursor.right.isDown) || (cursor.left.isDown && cursor.down.isDown) || (cursor.right.isDown && cursor.up.isDown) || (cursor.right.isDown && cursor.down.isDown)){
        player.setVelocity(0);
        player.setTexture("player");
      }
  }

}
*/

var config = {
  type: Phaser.AUTO,
  width: window.innerWidth - 20,
  height: window.innerHeight - 100,
  backgroundColor: "#FFFFFF", //#FFFFFF
  physics: {
    default : "arcade",
    arcade : {
      debug : false,
    }
  },
  scene: [SceneStart, LabyrintheStart /**, LabyrintheDeux*/]
};

let game = new Phaser.Game(config);

var h = window.innerHeight;
var w = window.innerWidth;

var platforms;
var cursor;
var player;

var doorGauche;
var doorDroite;
var doorUp;
var doorStart;

var wall1;
var wall2;
var wall3;
var wall4;


// A rajouter plus tard
/**
 * 
    function collisionUp() {
      this.scene.start("labyrintheTrois");
    }
 * 
 * this.physics.add.collider(player, doorGauche, collisionUp, undefined, this); 
 * 
 * doorUp = this.physics.add.staticSprite((w / 2) - 20, 8, "door");
 * doorUp.setScale(0.08);
 * doorUp.setSize(80, 18);
 * doorUp.setOffset(55, 491);
 * doorUp.rotation += -20.42;
*/
pppery
  • 3,731
  • 22
  • 33
  • 46
Soralienne
  • 73
  • 4

1 Answers1

3

I never really used rexquestplugin, but is sounded interesting, so I checked it out.

Here the Documentation (it is not detailed, but might help clear up the code below)
The Demo on the Documentation Site helped me to understand the usage better, but it is not very clear.

I wrote a small demo app, with this plugin, to answer the question "how would I solve your question, with the plugin?"
(btw.: You can execute the snippet below)

// This is the Quest csv File as Array (just for the demo)
    // load the csv from a file, if it is bigger
    var questString = [
        ['type', 'key', 'next', 'end'],    // Line 1: HEADER
        ['q', 'Go talk to Jerry', '', ''], // Line 2: Quest1 Part1
        ['', '', 'Get Sword', ''],         // Line 3:   First Option for Quest1 Part1 (there could be several option per Quest)
        ['q', 'Get Sword', '', ''],        // Line 4: Quest1 Part2
        ['', '', 'Give it to Jerry', ''],  // Line 5:   First Option for Quest1 Part2
        ['q', 'Give it to Jerry', '', ''], // Line 6: Quest1 Part3
        ['','', 'DONE', '', ''],           // Line 7:   First Option for Quest1 Part3
        ['q', 'DONE', '', '1'],            // Line 8: QUEST End
    ].map(x => x.join(',')).join('\n');


    // This is the DemoScene
    // Just very simple way to define a Scene, without Class (just for the demo)
    var DemoScene = {
        preload() {
            // Load Plugin
            this.load.plugin('rexquestplugin', 'https://raw.githubusercontent.com/rexrainbow/phaser3-rex-notes/master/dist/rexquestplugin.min.js', true);
        },
        extend: {
            // Collision Callback for Jerry
            handleMeeting(player, jerry){
                if(player._currentQuest ){
                    // Get all options (in this case only one)
                    console.info(player._currentQuest.currentQuest)
                    let options = player._currentQuest.currentQuest.options;
                    if(options[0].next == 'Get Sword'){
                        this.sword.visible = true;
                        player._currentQuest.manager.getNextQuestion(options[0].next);
                    } else if(options[0].next == 'DONE'){
                        this.door.fillColor = 0x00ff00;
                        player._currentQuest.manager.getNextQuestion(options[0].next);
                    }
                }
            },
            // Collision Callback for Sword
            handleSword(player, sword){
                if(player._currentQuest ){
                    sword.destroy();
                    // Get all options (in this case only one)
                    let options = player._currentQuest.currentQuest.options;
                    if(options[0].next == 'Give it to Jerry'){
                        sword.visible = true;
                        player._currentQuest.manager.getNextQuestion(options[0].next);
                    }
                }
            },
            // Collision Check Callback for Sword
            checkSwordStatus(player, sword){
                // if the sword is visible collide
                return sword.visible;
            },
            // Collision Check Callback for door
            checkDoorStatus(player, door){
                // if the door is red collide
                return door.fillColor == 0xff0000;
            }
        },
        create() {
            // Player
            this.player = this.add.circle(30, 30, 10, 0xffffff).setOrigin(.5);
            this.cursor = this.input.keyboard.createCursorKeys();
            this.physics.add.existing(this.player);
            this.player.body.setCollideWorldBounds(true);
            this.player.body.setCircle(10);
            this.player.setDepth(2);

            // Jerry
            this.jerry = this.add.circle(160, 30, 10, 0x0000ff).setOrigin(.5);
            this.physics.add.existing(this.jerry, true);
            this.jerry.body.setCircle(10);

            // Sword
            this.sword = this.add.isotriangle(50, 100, 20, 40, false, 0xffe31f, 0xf2a022, 0xf8d80b).setOrigin(.5);
            this.physics.add.existing(this.sword, true);
            this.sword.visible = false;

            // Door
            this.door = this.add.rectangle(190, 0, 20, 200, 0xff0000).setOrigin(0);
            this.physics.add.existing(this.door, true);

            // colliders
            this.physics.add.collider(this.player, this.jerry, this.handleMeeting, undefined, this);
            this.physics.add.collider(this.player, this.sword, this.handleSword, this.checkSwordStatus, this);
            this.physics.add.collider(this.player, this.door, undefined, this.checkDoorStatus, this );

            // Quest List
            this.print = this.add.text(380, 180, '', { fontSize: '12px', align: 'right' }).setOrigin(1);

            // QUEST SETUP
            this.plugins.get('rexquestplugin').add({
                questions: questString,
                quest: true
            })
            // EVENT executes on new Quest/Question
            .on('quest', function (currentQuest, manager, quest) {
                // QUEST has ended
                if (currentQuest.end === 1) {
                    manager.setData('endAt', currentQuest.key);
                    manager.emit('complete', manager, quest);
                } else {
                    // NEXT Step in the Quest
                    if(this.player._currentQuest){ 
                        this.print.text = this.print.text + 'done\n';
                    }
                    this.print.text += `${currentQuest.key}...`;
                    this.player._currentQuest = { currentQuest, manager}
                }
            }, this)
            // Is emited from `on('quest', ... )`
            .on('complete', function (manager, quest) {
                delete this.player._currentQuest;
                this.print.text = this.print.text + 'done\n';
                this.print.text += `\nDoor is unlocked!`;
            }, this)
            // Get First Quest
            .getNextQuestion();
        },
        update() {
            // only player movement
            let body = this.player.body;
            if (this.cursor.left.isDown) {
                body.setVelocityX(-200); 
            } else if (this.cursor.right.isDown) {
                body.setVelocityX(200);
            } else if (this.cursor.up.isDown) {
                body.setVelocityY(-200);
            } else if (this.cursor.down.isDown) {
                body.setVelocityY(200);
            } else {
                body.setVelocity(0);
            }
        }
    }

    var config = {
        type: Phaser.AUTO,
        width: 400,
        height: 200,
        scene: [DemoScene],
        physics: {
            default : "arcade"
        }
    };

    var game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>

Player - Movement with cursor keys
blue: is Jerry
white: is player
yellow: is sword
red/gree: closed/open door

One part that was not really clear to me, was the csv for the quest, here is my interpretation, of the columns:

  • 'type' column
    • if set to 'q' it is a quest/question
    • if empty '' it is a option
  • 'key' column
    • if is set it is the name of the quest/question
    • if empty '' it is a option
  • 'next' column
    • if it is set this is the name of the next quest/question
    • if empty '' it is a quest
  • 'end' column
    • if is is set it ends the quest/question (in this example it has to be set to 1)
    • if empty '', it is not the last quest/question

Update:
Depending on the size off the quest's / game your are making, the framework ink https://www.inklestudios.com/ink/ might be an option. It is easy to write for, has incredible documentation, but it is a pretty big tool/library (and it is great for visual novels, or narrative driven games).

winner_joiner
  • 12,173
  • 4
  • 36
  • 61