簡體   English   中英

在畫布javascript中使用drawImage制作動畫時.png閃爍

[英].png flickers while animating using drawImage in canvas javascript

我在 Javascript 中使用 Canvas 創建了一個簡單的游戲,玩家必須通過跳過或縮小來避開障礙物。 動畫向左移動的障礙物圖像以某種間隔閃爍。 間隔與 gameSpeed 變量並行增加,這讓我想知道這是否與 spawnObstacle() 或 update() 函數有關。 我這么認為的另一個原因是,繪制的 playerImg.png 和繪制的 Score/Highscore 都不會閃爍。 我嘗試了幾種在網上找到的不同解決方案,但似乎都沒有解決我的問題。

下面是我的 index.js 和 index.html:

import {registerNewHighscore, makeList} from "./globalHs.js";

const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');

const newGameBtn = document.getElementById("newGame");
const seeInstrBtn = document.getElementById("instrBtn");
const seeHsBtn = document.getElementById("hsBtn");
const goBackBtn = document.getElementById("backBtn");

let score;
let highscore;
let scoreText;
let highscoreText;
let player;
let gravity;
let obstacles = [];
let gameSpeed;
let keyPressed;
let isKeyPressed = false
let active = true;
let rotation = 0;

ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;

function createImage(path){
    let image = new Image();
    image.src = path;
    return image;
}

const obsImg = createImage("img/chatbubble.png");
const rockImg = createImage("img/rock.png");
const roadblockImg = createImage("img/roadblock.png");
const playerImg = createImage("img/logoPlayer.png");

newGameBtn.addEventListener('click', function() {
    document.getElementById("newGame").style.display = "none";
    document.getElementById("header").style.display = "none";
    document.getElementById("instr").style.display = "none";       
    document.getElementById("main").style.display = "block";
    document.getElementById("instrBtn").style.display = "none";
    document.getElementById("hsBtn").style.display = "none";
    document.getElementById("hsBoard").style.display = "none";
    start();
});

seeInstrBtn.addEventListener('click', function(){
    document.getElementById("header").style.display = "none";
    document.getElementById("instrBtn").style.display = "none";
    document.getElementById("newGame").style.display = "none";
    document.getElementById("instr").style.display = "block";
    document.getElementById("instr").style.visibility = "visible";
    document.getElementById("backBtn").style.display = "block";
    document.getElementById("hsBtn").style.display = "none";
    document.getElementById("hsBoard").style.display = "none";
    document.getElementById("backBtn").style.top = "50%";
});

seeHsBtn.addEventListener('click', function(){
    document.getElementById("header").style.display = "none";
    document.getElementById("hsBtn").style.display = "none";
    document.getElementById("newGame").style.display = "none";
    document.getElementById("instrBtn").style.display = "none";
    document.getElementById("instr").style.display = "none";
    document.getElementById("hsBoard").style.display = "block";
    document.getElementById("backBtn").style.display = "block";
    document.getElementById("backBtn").style.top = "70%";
    makeList();
});

goBackBtn.addEventListener('click', function() {
    goBack();
});

function goBack() {
    document.getElementById("backBtn").style.display = "none";
    document.getElementById("instr").style.display = "none";
    document.getElementById("header").style.display = "block";
    document.getElementById("newGame").style.display = "block";
    document.getElementById("instrBtn").style.display = "block";
    document.getElementById("hsBtn").style.display = "block";
    document.getElementById("hsBoard").style.display = "none";
};

document.addEventListener('keydown', function(evt) {
  if (isKeyPressed) return;
  
  isKeyPressed = true;
  keyPressed = evt.code;

});
document.addEventListener('keyup', function(evt) {
    if (evt.code !== keyPressed) return; // only respond to the key already pressed
    
    isKeyPressed = false;
    keyPressed = null;
});

function randomIntInRange (min, max){
    return Math.round(Math.random() * (max - min) + min);
}

class Player{
    constructor (x, y, r, w, h, playerImg){
        this.playerImg = playerImg;
        this.x = x;
        this.y = y;
        this.r = r;
        this.w = r*2;
        this.h = r*2;

        this.dy = 0;
        this.jumpForce = 18;
        this.originalRad = r;
        this.grounded = false;
        this.jumpTimer = 0;
        /* this.newRotation = 0; */
    }

    animate () {
        if (['Space', 'KeyW'].includes(keyPressed)) {
             this.jump();
        } else{
            this.jumpTimer = 0;
        }

        if (['ShiftLeft', 'KeyS'].includes(keyPressed)){
            /* this.newRotation = rotation * 2; */
            this.r = this.originalRad / 2;
            this.w = this.originalRad;
            this.h = this.originalRad;
        } else {
            /* this.newRotation = rotation; */
            this.r = this.originalRad;
            this.w = this.r * 2;
            this.h = this.r * 2;
        }

        this.y += this.dy;

        if (this.y + this.r < canvas.height){
            this.dy += gravity;
            this.grounded = false;
        } else{
            this.dy = 0;
            this.grounded = true;
            this.y = canvas.height - this.r;
        }
    
        this.draw();
    }
    
    jump () {
        if (this.r != this.originalRad) return;

        if (this.grounded && this.jumpTimer == 0){
            this.jumpTimer = 1.5;
            this.dy = -this.jumpForce;
        } else if (this.jumpTimer > 0 && this.jumpTimer < 15){
            this.jumpTimer++;
            this.dy = -this.jumpForce - (this.jumpTimer / 50);
        } 
    }

    draw () {
        ctx.translate(this.x, this.y);
        ctx.rotate(rotation);
        
        ctx.translate(-(this.x), -(this.y));
        ctx.drawImage(this.playerImg, (this.x-this.r), (this.y-this.r), this.w, this.h);
        ctx.setTransform(1, 0, 0, 1, 0, 0);
    }
}

class Obstacle {
    constructor (x, y, w, h, obsImg){
        this.obsImg = obsImg;
        this.x = x,
        this.y = y,
        this.w = w;
        this.h = h;

        this.dx = -gameSpeed;
        obsImg.width = this.w;
        obsImg.height = this.h;
    }

    update (){
        this.x += this.dx;
        this.dx = -gameSpeed;
    }

    draw () {
        ctx.beginPath();
        ctx.fillStyle = "rgba(0, 0, 0, 0)";
        ctx.fillRect(this.x, this.y, this.w, this.h,);
        ctx.drawImage(this.obsImg, this.x, this.y, this.w*1.1, this.h);
        ctx.closePath();
    }

    /////// CIRCLE
    /*draw () {
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.r, 0, (2 * Math.PI), false)
        ctx.fillStyle = this.c;
        ctx.fill();
        ctx.closePath();
    }*/

    /////// ELLIPSE
    /*draw () {
        ctx.beginPath();
        ctx.ellipse(this.x, this.y, this.radX, this.radY, 0, 0, 2 * Math.PI);
        ctx.fillStyle = this.c;
        ctx.fill();
        ctx.stroke();
    }*/
}

class Rock {
    constructor (x, y, w, h, rockImg){
        this.rockImg = rockImg;
        this.x = x,
        this.y = y,
        this.w = w;
        this.h = h;

        this.dx = -gameSpeed;
        rockImg.width = this.w;
        rockImg.height = this.h;
    }

    update (){
        this.x += this.dx;
        this.dx = -gameSpeed;
    }

    draw () {
        ctx.beginPath();
        ctx.fillStyle = "rgba(0, 0, 0, 0)";
        ctx.fillRect(this.x+20, this.y+40, this.w-20, this.h-40);
        ctx.drawImage(this.rockImg, this.x-20, this.y, this.w*1.5, this.h*1.5);
        ctx.closePath();
    }
}

class Roadblock {
    constructor (x, y, w, h, roadblockImg){
        this.roadblockImg = roadblockImg;
        this.x = x,
        this.y = y,
        this.w = w;
        this.h = h;

        this.dx = -gameSpeed;
        roadblockImg.width = this.w;
        roadblockImg.height = this.h;
    }

    update (){
        this.x += this.dx;
        this.dx = -gameSpeed;
    }

    draw () {
        ctx.beginPath();
        ctx.fillStyle = "rgba(0, 0, 0, 0)";
        ctx.fillRect(this.x, this.y+15, this.w, this.h,);
        ctx.drawImage(this.roadblockImg, this.x, this.y, this.w, this.h*1.15);
        ctx.closePath();
    }
}

class Text{
    constructor(t, x, y, a, c, s){
        this.t = t;
        this.x = x;
        this.y = y;
        this.a = a;
        this.c = c;
        this.s = s;
    }

    draw () {
        ctx.beginPath();
        ctx.fillStyle = this.c;
        ctx.font = this.s + "px";
        ctx.textAlign = this.a;
        ctx.fillText(this.t, this.x, this.y);
        ctx.closePath();
    }
}

function getDistance(player, obstacle) {
    var distX = Math.abs(player.x - (obstacle.x + obstacle.w / 2));
    var distY = Math.abs(player.y - (obstacle.y + obstacle.h / 2));

    if (distX > (obstacle.w / 2 + player.r)) { return false; }
    if (distY > (obstacle.h / 2 + player.r)) { return false; }

    if (distX <= (obstacle.w)) { return true; }
    if (distY <= (obstacle.h)) { return true; }

    var dx = distX - obstacle.w / 2;
    var dy = distY - obstacle.h / 2;
    return (dx * dx + dy * dy <= (player.r*player.r));
}

let initialSpawnTimer = 200; 
let spawnTimer = initialSpawnTimer;

function spawnObstacle (){
    let sizeX;
    let sizeY;
    let type = randomIntInRange(0, 2);
    let obstacle = new Obstacle(
        canvas.width + sizeX, 
        canvas.height - sizeX, 
        sizeX,
        sizeY,
        obsImg
        );
        
        if (type == 0){
            sizeX = randomIntInRange(100, 160);
            sizeY = sizeX / 2;
            obstacle = new Rock(
                canvas.width + sizeX, 
                canvas.height - sizeY, 
                sizeX,
                sizeY,
                rockImg  
                );  
            } else if (type == 1){
            sizeX = randomIntInRange(80, 160);
            sizeY = sizeX / 2;
            obstacle = new Obstacle(
                canvas.width + sizeX, 
                canvas.height - sizeX, 
                sizeX,
                sizeY,
                obsImg
                );
            obstacle.y -= player.originalRad  + randomIntInRange(-50, 150);
        } else if (type == 2){
            sizeX = 150;
            sizeY = sizeX / 2;
            obstacle = new Roadblock(
                canvas.width + sizeX, 
                canvas.height - sizeY, 
                sizeX,
                sizeY,
                roadblockImg  
            );
        }

    obstacles.push(obstacle);
}


function start () {
    ctx.canvas.width = window.innerWidth;
    ctx.canvas.height = window.innerHeight;
    
    ctx.font = "40px Courier New";

    active = true;

    gameSpeed = 6;
    gravity = 1;

    score = 0;
    highscore = 0;
    if (localStorage.getItem('highscore')){
        highscore = localStorage.getItem('highscore');
    }

    player = new Player(100, 0, 50, 100, 100, playerImg);

    scoreText = new Text("Score: " + score, 45, 45, "left", "#212121", "40");
    highscoreText = new Text("Highscore: " + highscore, 45,
    90, "left", "gold", "40");
    
    window.requestAnimationFrame(update);
}


let lastTime;

function update (time) {
    if (lastTime != null) {
        const delta = time - lastTime;
    }
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    spawnTimer--;
    if (spawnTimer <= 0){
        spawnObstacle();
        spawnTimer = initialSpawnTimer - gameSpeed * 8;

        if (spawnTimer < 60){
            spawnTimer = randomIntInRange(40, 80);
        }
    }

    for (let i = 0; i < obstacles.length; i++){
        let o = obstacles[i];
        o.draw();
        o.update();

        if (o.x + o.y < 0){
            obstacles.splice(i, 1);
        }

        
        if (getDistance(player, o)) {
            active = false;
            obstacles = [];
            spawnTimer = initialSpawnTimer;
            gameSpeed = 6;    
            window.localStorage.setItem('highscore', highscore);
            score -= 1;
            highscore -= 1;
            if (score >= highscore){
                registerNewHighscore(highscore+1);
            }
            goBack();
        }
    }

    lastTime = time;
    if (active){
        window.requestAnimationFrame(update);
    }
    
    player.animate();
    
    score++;
    scoreText.t = "Score: " + score;
    scoreText.draw();


    if (score > highscore){
        highscore = score;
        highscoreText.t = "Highscore: " + highscore;
        
    }

    highscoreText.draw();
    
    
    rotation+=Math.PI/180 * 2 + gameSpeed * 0.01;
    gameSpeed += 0.002;
    
}
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Kindly Waiting Game V2</title>
    <link rel="stylesheet" href="styles.css" />
</head>
<body>
    <div class="world">
        <div id="header">The Kindly Game</div>
        <div id="newGame" onmouseover="this.style.backgroundColor = 'goldenrod'"
        onmouseout="this.style.backgroundColor= 'gold'">New Game</div>
        <div id="instrBtn" onmouseover="this.style.backgroundColor = 'goldenrod'" onmouseout="this.style.backgroundColor 
        = 'gold'">Instructions</div>
        <div id="hsBtn" onmouseover="this.style.backgroundColor = 'goldenrod'" onmouseout="this.style.backgroundColor 
        = 'gold'">Highscores</div>

        <div id="instr" style="display: none">Avoid the <br>chatbubbles, <br>roadblocks, <br>and rocks!<br><br>
            Instructions: <br>Space/W: Jump <br>ShiftLeft/S: Shrink</div>
        <div id="hsBoard" style="display: none">Highscores:</div>
        <div id="backBtn" onmouseover="this.style.backgroundColor = 'goldenrod'" onmouseout="this.style.backgroundColor
        = 'gold'">Back</div>

        <div id="main"></div>
        <div id="score"></div>

        <div id="cloudLarge"></div>
        <div id="cloudMedium"></div>
        <div id="cloudSmall"></div>
        <div id="cloudSmall2"></div>
        <div id="ufo"></div>
        <div id="airplane"></div>
        <div id="ye"></div>
    </div>

    <canvas id="game" width="640" height="400"></canvas>
    <script src="globalHs.js" type="module"></script>
    <script src="index.js" type="module"></script>
</body>
</html>

您的問題是由update()函數中的這段代碼引起的:

if (o.x + o.y < 0){
    obstacles.splice(i, 1);
}

現在雖然我不知道為什么要檢查障礙物的水平和垂直位置的總和的確切邏輯,但實際上你是在使用.splice()方法從數組中刪除一些東西。 由於這是在for 循環中發生的,因此您實際上是在修改數組的長度,而它仍可能在其上循環。

您可以通過從最后一個元素到第一個元素循環遍歷數組來解決此問題:

for (let i = obstacles.length-1; i>=0; i--) {
    let o = obstacles[i];
    o.draw();
    o.update();

    if (o.x + o.y < 0) {
        obstacles.splice(i, 1);
    }


    if (getDistance(player, o)) {
        active = false;
        obstacles = [];
        spawnTimer = initialSpawnTimer;
        gameSpeed = 6;
        window.localStorage.setItem('highscore', highscore);
        score -= 1;
        highscore -= 1;
        if (score >= highscore) {
            registerNewHighscore(highscore + 1);
        }
        goBack();
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM