0

so i am creating a scratching game where the user is supposed to scratch the cover the clear the area to reveal what is under the cover. the first part is done but i am unable to restart the game when he wants to play again. how can i bring back the cover when the user click the play again button?

i am trying to do this:

  create() {
    super.create();
    this.createTexture();
    this.scratchOff();
    this.Buybuttons();
    this.createInfoContainer();
    this.scratchAll();
    this.playAgainFct();
    this.board = this.make.image({key: BOARD,add: false});
    this.add.image(...this.screenCenter, BOARD).setScale(0.75);
    this.ticketValues = this.add.text(10, 50);
  }

createTexture() {
    this.coverImage = this.textures.get(SKY_IMAGE_BLACK).getSourceImage();
    this.coverHelperCanvas = this.textures.createCanvas("coverTexture",this.coverImage.width,this.coverImage.height);
    this.coverHelperCanvas.draw(0, 0, this.coverImage);
    this.coverHelperCanvas.context.globalCompositeOperation = "destination-out";
    this.canvasCollection = this.coverHelperCanvas;
  }

  scratchOff() {

    this.precent = this.add.text(10, 10, "");

    let cover = this.add.image(this.screenCenter[0] - 171, this.screenCenter[1], "coverTexture").setInteractive().setScale(0.76).setDepth(1);
    
    this.add.image(this.screenCenter[0] - 171, this.screenCenter[1], SKY_IMAGE).setScale(0.76).setDepth(0);

    if (cover.on("pointerdown", this.startDrawing, this))
      if (cover.on("pointerup", this.stopDrawing, this))
        if (cover.on("pointermove", this.clearCover, this))
          this.checkTimer = this.time.addEvent({
            delay: 250,
            loop: true,
            callback: this.checkPercent,
            callbackScope: this,
          });
  }

  startDrawing() {
    this.isPointerDown = true;
  }

  stopDrawing() {
    this.isPointerDown = false;
  }

  clearCover(e, x, y) {
    if (this.isPointerDown) {
      let radius = 50;
      this.coverHelperCanvas.context.beginPath();
      this.coverHelperCanvas.context.arc(x, y, radius, 0, Math.PI * 2, false);
      this.coverHelperCanvas.context.fill();
      this.coverHelperCanvas.update();
      this.onScratchParticles();
    }
  }

 checkPercent() {
    let full = this.coverImage.width * this.coverImage.height;
    let { data } = this.coverHelperCanvas.context.getImageData(0,0,this.coverImage.width,this.coverImage.height);
    let current = data.filter((v, i) => (i + 1) % 4 == 0 && v > 0).length;
    let percentage = ((current / full) * 100).toFixed(2);
    this.precent.setText(`Cover Percent: ${percentage}%`);

    if (percentage <= 50) {
      this.coverHelperCanvas.context.clearRect(0,0,this.coverImage.width,this.coverImage.height);
      this.coverHelperCanvas.update();
      this.destroyParticles();
      this.checkTimer.remove();
    }
  }

playAgainFct() {
    const menuPosition = [this.screenCenter[0] - 190, this.screenCenter[1] + 280];
  
    const playAgain = new CustomButton(this, ...menuPosition, "button2", "button3", "Play Again");
    this.add.existing(playAgain);
    playAgain.setInteractive();
  
    playAgain.on(Phaser.Input.Events.GAMEOBJECT_POINTER_DOWN, () => {
      this.createTexture();
      this.scratchOff();
    });
  }

i am using the playagainFct to call the createTexture and scratchOff function again but when i do that it give an error that says ('Cannot read properties of null (reading 'draw') at Scratch.createTexture'')

1 Answers1

1

The problem is, that your are creating always a new texture, with the same name/key. This is not allowed. (btw.: You overlooked the first error. The first error is always the most important, for problem solving)

As the documentation says, "... The unique string-based key of the Texture. ..." (link to the documentation)

createTexture() {
    this.coverImage = this.textures.get(SKY_IMAGE_BLACK).getSourceImage();
    // check if texture exists, if so delete
    let texture = this.textures.get('coverTexture');
    if(texture){
        this.textures.remove(texture);
    }
    this.coverHelperCanvas = this.textures.createCanvas("coverTexture",this.coverImage.width,this.coverImage.height);
    this.coverHelperCanvas.draw(0, 0, this.coverImage);
    this.coverHelperCanvas.context.globalCompositeOperation = "destination-out";
    this.canvasCollection = this.coverHelperCanvas;
}

OR you could only create a texture, if there is no texture

createTexture() {
    this.coverImage = this.textures.get(SKY_IMAGE_BLACK).getSourceImage();
    // check if texture exists, if so delete
    let texture = this.textures.get('coverTexture');
    if(!texture){
        this.coverHelperCanvas = this.textures.createCanvas("coverTexture", this.coverImage.width, this.coverImage.height);
    }
    this.coverHelperCanvas.draw(0, 0, this.coverImage);
    this.coverHelperCanvas.context.globalCompositeOperation = "destination-out";
    this.canvasCollection = this.coverHelperCanvas;
}

Update:

Disclaimer: I usually don't do this, clean up code of questions, but just to solve this problem in a timely fashion, here is a complete solution for the problem. I remove all the wrong and unnecessary parts(for the demo). Use this demo as basis to fix your code.

document.body.style = 'margin:0;';

const SKY_IMAGE_BLACK = 'coverImage';

const SKY_IMAGE = 'sky';
const BOARD = 'board';

class ScratchScene extends Phaser.Scene {
    constructor() {
        super('ScratchScene');
    }

    preload() {
        this.load.image(SKY_IMAGE_BLACK, 'https://placehold.co/200x50/ff0000/FFFFFF.png?text=coverImage&font=montserrat');
        this.load.image(SKY_IMAGE, 'https://placehold.co/200x50/0000ff/FFFFFF.png?text=coverImage&font=montserrat');
        this.load.image(BOARD, 'https://placehold.co/536x164/cdcdcd/FFFFFF.png?text=board&font=montserrat');
    }

    create() {
        this.screenCenter = [ config.width/ 2, config.height/ 2];

        this.createTexture();
        this.scratchOff();

        this.playAgainFct();
        this.board = this.make.image({ key: BOARD, add: false });
        this.add.image(...this.screenCenter, BOARD);
    }

    createTexture() {
        this.coverImage = this.textures.get(SKY_IMAGE_BLACK).getSourceImage();
        
        let texture = this.textures.get('coverTexture');
        if(!texture || !this.coverHelperCanvas){
            this.textures.remove(texture);
            this.coverHelperCanvas = this.textures.createCanvas('coverTexture', this.coverImage.width, this.coverImage.height);
        }

        //reset the default-value for the second run
        this.coverHelperCanvas.context.globalCompositeOperation = "source-over";

        this.coverHelperCanvas.draw(0, 0, this.coverImage);
        this.coverHelperCanvas.context.globalCompositeOperation = "destination-out";

    }

    scratchOff() {
        this.precent = this.add.text(10, 10, "")
            .setDepth(100)
            .setColor('#000000');

        let cover = this.add.image(this.screenCenter[0] - 171, this.screenCenter[1], 'coverTexture').setInteractive().setScale(0.76).setDepth(1);

        cover.on("pointermove", this.clearCover, this);

        this.checkTimer = this.time.addEvent({
            delay: 250,
            loop: true,
            callback: this.checkPercent,
            callbackScope: this,
        });
    }

    clearCover(e, x, y) {
        let radius = 10;
        this.coverHelperCanvas.context.beginPath();
        this.coverHelperCanvas.context.arc(x, y, radius, 0, Math.PI * 2, false);
        this.coverHelperCanvas.context.fill();
        this.coverHelperCanvas.update();
    }

    checkPercent() {
        let full = this.coverImage.width * this.coverImage.height;
        let { data } = this.coverHelperCanvas.context.getImageData(0, 0, this.coverImage.width, this.coverImage.height);
        let current = data.filter((v, i) => (i + 1) % 4 == 0 && v > 0).length;
        let percentage = ((current / full) * 100).toFixed(2);
        this.precent.setText(`Cover Percent: ${percentage}%`);

        if (percentage <= 50) {
            this.coverHelperCanvas.context.clearRect(0, 0, this.coverImage.width, this.coverImage.height);
            this.coverHelperCanvas.update();
        }
    }

    playAgainFct() {
        const menuPosition = [this.screenCenter[0] + 150, this.screenCenter[1] ];

        const playAgain = this.add.rectangle(...menuPosition, 100, 40, 0xf0000ff)
            .setDepth(100)
            .setInteractive();
        let label = this.add.text(0,0, 'Play again')
             .setDepth(101);
             
        Phaser.Display.Align.In.Center(label, playAgain)

        playAgain.on(Phaser.Input.Events.GAMEOBJECT_POINTER_DOWN, () => {
            this.createTexture();
        });
    }
}

var config = {
    type: Phaser.AUTO,
    width: 536,
    height: 163,
    scene: [ScratchScene]
};

new Phaser.Game(config);
console.clear();
<script src="https://cdn.jsdelivr.net/npm/phaser/dist/phaser.min.js"></script>
winner_joiner
  • 12,173
  • 4
  • 36
  • 61
  • the methods you showed are not working – georgio bejjani Aug 22 '23 at 22:13
  • it seems there is an error when i am calling the function createcontext and it's returning this error "Cannot read properties of null (reading 'isGLTexture')" – georgio bejjani Aug 22 '23 at 22:14
  • There is no function `createcontext` in the code you shared. I don't understand the problem. **Tipp:** If you follow the guidelines of stackoverflow on how to ask questions, you will get fast an answer. _( [how to ask a good question](https://stackoverflow.com/help/how-to-ask))_ – winner_joiner Aug 23 '23 at 04:34
  • Im sorry i mean the createTexture function ( typo error ) – georgio bejjani Aug 23 '23 at 06:32
  • 1
    I updated my question, this should solve your problem, or atleast it will help you solve your problem. – winner_joiner Aug 23 '23 at 07:46
  • @georgiobejjani if my answer solved your question, please consider accepting it with the green checkmark by the voting arrows. – winner_joiner Aug 31 '23 at 06:59