Is there any other way to track the progress of erasing pixels in Phaser?
P粉928591383
2023-08-14 20:29:20
<p>So I'm trying to create a scratch card game where the user needs to scratch the card to reveal what's underneath. I want to check if the user has scratched 70% of the canvas to reveal it all. I'm trying to use phaser to implement this functionality, but it doesn't work. </p>
<p>I tried calculating imagedata to get an array of pixels, but it returns an array of all zeros. I'm using the calculateScratchRatio function in the code below.</p>
<pre class="brush:php;toolbar:false;">import BaseScene from "./BaseScene";
const SKY_IMAGE = "sky";
const SKY_IMAGE_BLACK = "skyblack";
const BOARD = "board";
const HEADER_ACT = "header_act";
const KEY_BRUSH = "brush";
const BGGAME = "bg-game";
export default class Scratch extends BaseScene {
constructor(config) {
super("Scratch", { ...config });
this.config = config;
this.isDown = false;
this.renderTexture = null;
this.brush = null;
this.erasedPixels = 0;
this.screenCenter = [config.width / 2, config.height / 2];
}
create() {
super.create();
this.cover = this.make.image({
key: SKY_IMAGE_BLACK,
add: false,
});
this.board = this.make.image({
key: BOARD,
add: false,
});
this.ScratchOff();
this.add.image(...this.screenCenter, BOARD).setScale(0.7);
console.log(this.board.getBounds());
const headerinfo = this.add
.image(this.screenCenter[0] - 160, 130, "header_act")
.setScale(0.7);
let helloWorld = this.add
.text(0, 0, "Hello World")
.setFont("20px Arial")
.setColor("#ffffff");
const container = this.add.container(headerinfo.x, headerinfo.y);
container.add(helloWorld);
}
ScratchOff() {
this.add
.image(this.screenCenter[0] - 160, this.screenCenter[1], SKY_IMAGE)
.setScale(0.7);
this.cover.setOrigin(0, 0);
const width = this.cover.width;
const height = this.cover.height;
console.log(width, height);
const rt = this.add.renderTexture(
this.screenCenter[0] - 160,
this.screenCenter[1],
width * 0.7,
height * 0.71
);
this.isRenderTextureErased = false;
this.erasureThreshold = 0.99;
rt.setOrigin(0.5, 0.5);
rt.draw(this.cover); //, width * 0.5, height * 0.5)
rt.setInteractive();
rt.on(Phaser.Input.Events.POINTER_DOWN, this.handlePointerDown, this);
rt.on(Phaser.Input.Events.POINTER_MOVE, this.handlePointerMove, this);
rt.on(Phaser.Input.Events.POINTER_UP, () => (this.isDown = false));
this.brush = this.make.image({
key: KEY_BRUSH,
add: false,
});
this.renderTexture = rt;
}
handlePointerDown(pointer) {
this.isDown = true;
this.handlePointerMove(pointer);
}
handlePointerMove(pointer) {
if (!this.isDown) {
return;
}
const x = pointer.x - this.renderTexture.x this.renderTexture.width * 0.5;
const y =
pointer.y - this.renderTexture.y this.renderTexture.height * 0.5;
this.renderTexture.erase(this.brush, x, y);
const result = this.calculateScratchRatio(x, y);
console.log("result", result);
}calculateScratchRatio(x, y) {
const texture = this.textures.get(SKY_IMAGE_BLACK);
console.log(texture);
if (!texture) {
console.error(`Texture with key '${SKY_IMAGE_BLACK}' not found.`);
return 0;
}
console.log(texture);
const canvas = document.createElement("canvas");
canvas.width = texture.source[0].width;
console.log("canvas.width", canvas.width);
canvas.height = texture.source[0].height;
const context = canvas.getContext("2d");
context.drawImage(texture.source[0].image, 0, 0);
const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
const pixels = imageData.data;
console.log(imageData, pixels);
let erasedCount = 0;
for (let i = 3; i < pixels.length; i = 4) {
const alpha = pixels[i 3];
if (alpha < 128) {
erasedCount ;
}
}
const totalPixels = canvas.width * canvas.height;
const scratchRatio = (erasedCount / totalPixels) * 100;
return Math.round(scratchRatio);
}
}</pre>
<p><br /></p>
There's a lot in your code and it's hard to get it to work.
The best thing to do is to publish a mini runnable code like mentioned herehere.
However, a simple and quick solution is to use canvasTexture
to create the cover texture so that you can access the context directly from this object.
Here is a short demonstration of how I would do it:
(Based on the concept of this answer)
document.body.style = 'margin:0;';
class ScratchScene extends Phaser.Scene {
constructor() {
super('ScratchScene')
}
create(){
let helperGraphics = this.make.graphics({x:0, y: 0, add: false});
helperGraphics.fillStyle(0xff0000);
helperGraphics.fillRect(0, 0, 200, 50 );
helperGraphics.generateTexture('cover', 200, 50);
let coverImage = this.textures.get('cover').getSourceImage()
this.coverHelperCanvas = this.textures.createCanvas('coverTexture', 200, 50)
this.coverHelperCanvas.draw(0, 0, coverImage)
this.coverHelperCanvas.context.globalCompositeOperation = 'destination-out'
this.precent = this.add.text(10 , 10, '' );
this.add.text( config.width/2, config.height / 2, 'YOU WON')
.setOrigin(.5)
.setFontSize(20);
let cover = this.add.image(config.width/2, config.height / 2, 'coverTexture')
.setInteractive();
cover.on('pointermove', this.clearCover, this);
this.checkPercent();
}
checkPercent(){
let full = 200 * 50;
let { data } = this.coverHelperCanvas.context.getImageData(0, 0,200, 50);
let current = data.filter((v,i) => ((i + 1) % 4 == 0) && v > 0).length;
this.precent.setText(`Cover Percent: ${ (current / full * 100).toFixed(2) }%` );
}
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();
this.checkPercent();
}
}
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>
Like +0
P粉928591383