Drag and drop quiz

Hello everyone,

I’m trying to make drag and drop quiz using phaser 3.Indeed, I have three letters the user should drag and drop every letter in the appropriate place. For example, the user drag the letter B in the drop zone of the letter C, the letter B should not be dropped in the drop zone of letter C and it should be return in its place.My problem is that every letter still dropped in the three drop zones . I think I should use something with conditions if else but I don’t know how to do it with phaser…

Could you help me in this?

Thanks in advance,

It seems like a logic problem. Could you post the piece of code that check what place is under the letter?

1 Like

Thank you for your feedback, it seems I should use something with if else but I’dont know how to do that with phaser, this is my code :
create() {

     // Drag and  drop of the letter A
    this.DropzoneA = this.add.image(580, 220, "DropzoneA");
    this.DropzoneA.displayWidth = 130;
    this.DropzoneA.displayHeight = 130;
    // Drop Zone of letter A
    this.DropzoneA.setInteractive();
    this.DropzoneA.input.dropZone = true;
     // The object to be dropped
    this.letterA = this.add.image(100, 500, "letterA");
    this.letterA.displayWidth = 50;
    this.letterA.displayHeight = 90;
    this.letterA.setInteractive();
    this.input.setDraggable(this.letterA);
    this.input.on('dragstart', function (pointer, gameObject) {

        this.children.bringToTop(gameObject);

    }, this);

    this.input.on('drag', function (pointer, gameObject, dragX, dragY) {

        gameObject.x = dragX;
        gameObject.y = dragY;

    });

    this.input.on('dragenter', function (pointer, _gameObject, _dropZone) {

        zone.setTint(0x00ff00);


    });

    this.input.on('dragleave', function (pointer, _gameObject, _dropZone) {

        zone.clearTint();

    });

 

   this.input.on('drop', function (pointer, gameObject, dropZone) {
     

        gameObject.x = dropZone.x;
        gameObject.y = dropZone.y;
        gameObject.setScale(0.0);

        gameObject.input.enabled = false;

        zone.clearTint();


    });

    this.input.on('dragend', function (pointer, gameObject, dropped) {



        if (!dropped) {
            gameObject.x = gameObject.input.dragStartX;
            gameObject.y = gameObject.input.dragStartY;
        }


    });

    // Drag and the drop of letterB
    this.DropzoneB = this.add.image(400, 220, "DropzoneB");
    this.DropzoneB.displayWidth = 130;
    this.DropzoneB.displayHeight = 130;
    // Drop Zone 
    this.DropzoneB.setInteractive();
    this.DropzoneB.input.dropZone = true;

    this.letterB = this.add.image(150, 280, "letterB");
    this.letterB.displayWidth = 50;
    this.letterB.displayHeight = 90;
    this.letterB.setInteractive();
    this.input.setDraggable(this.letterB);
    this.input.on('dragstart', function (_pointer, gameObject) {

        this.children.bringToTop(gameObject);

    }, this);

    this.input.on('drag', function (_pointer, gameObject, dragX, dragY) {

        gameObject.x = dragX;
        gameObject.y = dragY;

    });

    this.input.on('dragenter', function (_pointer, _gameObject, _dropZone) {

        zone.setTint(0x00ff00);


    });

    this.input.on('dragleave', function (_pointer, _gameObject, _dropZone) {

        zone.clearTint();

    });

    this.input.on('drop', function (_pointer, gameObject, dropZone) {

        gameObject.x = dropZone.x;
        gameObject.y = dropZone.y;
        gameObject.setScale(0.0);

        gameObject.input.enabled = false;

        zone.clearTint();

    });

    this.input.on('dragend', function (_pointer, gameObject, dropped) {

        if (!dropped) {
            gameObject.x = gameObject.input.dragStartX;
            gameObject.y = gameObject.input.dragStartY;
        }

    });

// Drag and drop of the letter C
this.DropzoneC = this.add.image(580, 220, “DropzoneC”);
this.DropzoneC.displayWidth = 130;
this.DropzoneC.displayHeight = 130;
// Drop Zone of letter A
this.DropzoneC.setInteractive();
this.DropzoneC.input.dropZone = true;
// The object to be dropped
this.letterC = this.add.image(100, 500, “letterC”);
this.letterC.displayWidth = 50;
this.letterC.displayHeight = 90;
this.letterC.setInteractive();
this.input.setDraggable(this.letterC);
this.input.on(‘dragstart’, function (pointer, gameObject) {

        this.children.bringToTop(gameObject);

    }, this);

    this.input.on('drag', function (pointer, gameObject, dragX, dragY) {

        gameObject.x = dragX;
        gameObject.y = dragY;

    });

    this.input.on('dragenter', function (pointer, _gameObject, _dropZone) {

        zone.setTint(0x00ff00);


    });

this.input.on(‘dragleave’, function (pointer, _gameObject, _dropZone) {

        zone.clearTint();

    });

 

   this.input.on('drop', function (pointer, gameObject, dropZone) {
     

        gameObject.x = dropZone.x;
        gameObject.y = dropZone.y;
        gameObject.setScale(0.0);

        gameObject.input.enabled = false;

        zone.clearTint();


    });

    this.input.on('dragend', function (pointer, gameObject, dropped) {



        if (!dropped) {
            gameObject.x = gameObject.input.dragStartX;
            gameObject.y = gameObject.input.dragStartY;
        }


    });

}

Ok.
But, why don’t you use overlap to detect if the letter is over the specific drop zone?

Have a look at

1 Like

Hi @nihalouherrou,

on Drag and drop pages i’m mostly using;

  • Create zone/image/sprite as normal gameObject, put some data to check later,
  • Create draggable object with .setInteractive({ draggable: true }), also put some data to check
  • Listen on(Phaser.Input.Events.DRAG_END, callback) for draggable gameObject/s,

And at the end,

onDragEnd: function() { 
  //consider you haven't give a context so 
  // actual this here is draggable gameObject
  var scene = this.scene;
  var parent = this.parentContainer; // only if needed

  var hasDrop = Phaser.Geom.Rectangle.Area(
    Phaser.Geom.Rectangle.Intersection(
         this.getBounds(), scene.dropZone.getBounds())) > 0;


  if(hasDrop) {
     // here i'm also checking if the draggable's data 
     // is matching with scene.dropZone's data and 
     // and let user know about it's correct or not
  }
}

Inside onDragEnd function if you have multiple targets/dropzone’s then just make a for loop to see if your draggable gameObject actually Intersect with any of them =)

Thank you for your suggestion and examples. I will try to use overlap :grin:

Thank you for your feedback. Could you please give me an example how to do this with two dropzone using this syntaxe ?

Sure, it’s actually just a for loop and some statement checks.

checkOverlap: function(gameObjectA, gameObjectB) {
  return Phaser.Geom.Rectangle.Area(Phaser.Geom.Rectangle.Intersection(gameObjectA.getBounds(), gameObjectB.getBounds())) > 0;
},

Add this to your scene so if need you can use it later for something else.
And for onDragEnd, I’m assuming that you have multiple dropzones within an array let say, dropZoneList:

onDragEnd: function() {
  //consider you haven't give a context so 
  // actual this here is draggable gameObject
  var scene = this.scene;
  var parent = this.parentContainer; // only if needed
  var hit = false, wrongSound = false;

  for (let i = 0; i < dropZoneList.length; i++) {
    const element = dropZoneList[i];    
    let hasDrop = scene.checkOverlap(this, element);

    if (hasDrop) {
      // here i'm also checking if the draggable's data 
      // is matching with scene.dropZone's data and 
      // and let user know about it's correct or not
 
      // after here you can maybe check if the givin time, 
      // your draggable and dropzone has same datas or 
      // if draggable actually valid for dropZone

      // here again i'm assuming that u have `_data` attributes 
      // on draggable and dropzone
      if (this._data === element._data) {
        hit = true;


        break;
        // we are breaking the loop so 
        // it's not checking any other
        // dropzone with this draggable 
        // which already placed
      }
      else {
        wrongSound = false;
      }
    } 
    
    if (!hit) {
      // return your draggable back to it's place.


      if (wrongSound) {
        // play a wrong sound :)
      }
    }
  }
}

I hope this made your head clear =)

1 Like

Thank you !

I have a similar problem can someone help me?

I have 3 green cards ,3 red cards, 1 green dropzone and 1 red dropzone.
The green dropzone must accept only green cards and the red dropzone only red cards.
I created the draggables and the dropzones but i can’t make each dropzone accept only cards of the same color.

@thiagosiq I’m probably far too late, but ill post my solution incase anyone else runs into the same problem.
I would use dropZone.name and texture.key, e.g:

in preload:

this.load.image("redCard", "path/to/red/card");
this.load.image("blueCard", "path/to/blue/card");

when you create your dropZone set its name:

let redDropZone = this.add.zone(x, y, width, height).setRectangleDropZone(width, height);
redDropZone.name = "redZone";

let blueDropZone = this.add.zone(x, y, width, height).setRectangleDropZone(width, height);
blueDropZone.name = "blueZone";

and then in your drop function:

this.input.on("drop", function(pointer, gameObject, dropZone) {

            // if a red card is dropped on the redZone or a blue card dropped on a blueZone:
            if ((gameObject.texture.key === "redCard" && dropZone.name === "redZone") || (gameObject.texture.key === "blueCard" && dropZone.name === "blueZone")) {

                // your code here
            }
            // if the gameObject and zone don't match, return to original position
            else {
                gameObject.x = gameObject.input.dragStartX;
                gameObject.y = gameObject.input.dragStartY;
            }
});