JavaScript游戏开发(3)(笔记)

news2024/11/30 7:40:08

文章目录

  • 七、支持移动设备的横向卷轴游戏
    • 准备
    • 7.1 角色的简单移动
    • 7.2 背景
    • 7.3 加入敌人与帧数控制
    • 7.4 碰撞、计分、重新开始
    • 7.5 手机格式
    • 7.6 全屏模式
    • 7.7 存在的问题
  • 附录

素材可以去一位大佬放在github的源码中直接下,见附录。

七、支持移动设备的横向卷轴游戏

使用前面我们所学习的部分,组合成为一个游戏。

是否玩过《疯狂喷气机》(手游)这类游戏,该部分试着做一个类似与它的简单的横板游戏。

准备

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>JavaScript 2D Game</title>
    <link rel="stylesheet" href="./stylte.css">
</head>

<body>
    <canvas id="canvas1"></canvas>
    <img src="./player.png" id="playerImage" alt="playerImage">
    <img src="./backgroundImage.png" id="backgroundImage" alt="backgroundImage">
    <img src="./worm.png" id="enemyImage" alt="enemyImage">

    <script src="./script.js"></script>

    <script src="./script.js"></script>
</body>

</html>

css

body{
    background: black;
}

#canvas1{
    position: absolute;
    top:50%;
    left: 50%;
    transform: translate(-50%,-50%);
    border: 5px solid white;
}

#playerImage,#backgroundImage,#enemyImage{
    display: none;
}

JavaScript

window.addEventListener('DOMContentLoaded',function(){
    const canvas = this.document.getElementById('canvas1');
    const ctx = canvas.getContext('2d');
    canvas.width = 800;
    canvas.height = 720;
    let enemies = [];


    // 输入处理
    class InputHandler{
       
    }

    class Player{


    }

    class Background{

    }

    class Enemy{

    }


    function handleEnemies(){

    }


    function displayStatusText(){

    }



    function animate(){

        requestAnimationFrame(animate);
    }

    animate();

});

7.1 角色的简单移动

我们通过如下代码控制角色移动

window.addEventListener('DOMContentLoaded',function(){
    const canvas = this.document.getElementById('canvas1');
    const ctx = canvas.getContext('2d');
    canvas.width = 800;
    canvas.height = 720;
	let enemies = [];

    // 输入处理
    class InputHandler{
        constructor(){
            this.keys = [];
            // 加入上、下、左、右按键,此处使用 indexof保证键唯一,就无视本次输入
            window.addEventListener('keydown',e=>{
                if ((e.key === 'ArrowDown' ||
                     e.key === 'ArrowUp' ||
                     e.key === 'ArrowLeft' ||
                     e.key === 'ArrowRight') &&
                    this.keys.indexOf(e.key) === -1) {
                    this.keys.push(e.key)
                }
                console.log(e.key,this.keys);
            });

            // 移除按键
            window.addEventListener('keyup',e=>{
                if( e.key === 'ArrowDown' ||
                    e.key === 'ArrowUp' ||
                    e.key === 'ArrowLeft' ||
                    e.key === 'ArrowRight'){
                    this.keys.splice(this.keys.indexOf(e.key), 1)
                }
                console.log(e.key,this.keys);
            });
        }
    }

    class Player{
        constructor(gameWidth,gameHeight){
            this.gameWidth = gameWidth;
            this.gameHeight = gameHeight;
            this.width = 200;
            this.height = 200;
            this.x = 0;
            this.y = this.gameHeight - this.height;
            this.image  = playerImage;
            this.frameX = 0;
            this.frameY = 0;
            
            // 速度
            // x轴
            this.speedX = 0;
            // y轴
            this.speedY = 0;

            // 重量
            this.weight = 1;
        }
        draw(context){
            context.fillStyle = 'white';
            context.fillRect(this.x,this.y,this.width,this.height);
            context.drawImage(this.image,this.frameX*this.width,this.frameY*this.height,this.width,this.height,this.x,this.y,this.width,this.height);
        }

        update(input){
            // 检测X轴按键
            if(input.keys.indexOf('ArrowRight') > -1){
                this.speedX = 5;
            }
            else if(input.keys.indexOf('ArrowLeft') > -1){
                this.speedX = -5;
            }
            else{
                this.speedX = 0;
            }

            // 检测Y轴按键,且只能从地面上起跳
            if(input.keys.indexOf('ArrowUp') > -1 && this.onGround()){
                this.speedY = -32;
            }

        
            this.x = this.x + this.speedX;
            this.y = this.y + this.speedY;

            // 避免出界
            if(this.x < 0){
                this.x = 0
            }
            else if(this.x > this.gameWidth - this.width){
                this.x = this.gameWidth - this.width;
            }

            
            // 跳跃限制
            if(!this.onGround()){
                this.speedY += this.weight;
                this.frameY = 1;
            }
            else{
                this.speedY = 0;
                this.frameY = 0;
            }

            // 避免陷入地面
            if(this.y > this.gameHeight - this.height){
                this.y = this.gameHeight - this.height;
            }
        }
        onGround(){
            return this.y >= this.gameHeight - this.height;
        }
    }

    class Background{

    }

    class Enemy{

    }


    function handleEnemies(){

    }


    function displayStatusText(){

    }


    const input = new InputHandler();
    const player = new Player(canvas.width,canvas.height);

    function animate(){
        ctx.clearRect(0,0,canvas.width,canvas.height);
        player.draw(ctx);
        player.update(input);
        requestAnimationFrame(animate);
    }

    animate();

});

如下,我们完成了通过箭头移动角色

在这里插入图片描述

7.2 背景

	class Background{
        constructor(gameWidth, gameHeight) {
            this.gameWidth = gameWidth;
            this.gameHeight = gameHeight;
            this.image = backgroundImage;
            this.x = 0;
            this.y = 0;
            this.width = 2400;
            this.height = 720;
            this.speed = 7;
        }
        draw(context) {
            context.drawImage(this.image, this.x, this.y, this.width, this.height);
            context.drawImage(this.image, this.x + this.width, this.y, this.width, this.height);
        }

        update() {
            this.x -= this.speed
            if (this.x < 0 - this.width){
                this.x = 0;
            } 
        }
    }

	function animate(){
        ctx.clearRect(0,0,canvas.width,canvas.height);
        background.draw(ctx);
        background.update();
        player.draw(ctx);
        player.update(input);
        requestAnimationFrame(animate);
    }

7.3 加入敌人与帧数控制

笔者修改了视频的一些代码,并修改了player中的一些代码

window.addEventListener('DOMContentLoaded',function(){
    const canvas = this.document.getElementById('canvas1');
    const ctx = canvas.getContext('2d');
    canvas.width = 800;
    canvas.height = 720;

    let enemies = [];

    // 输入处理
    class InputHandler{
        constructor(){
            this.keys = [];
            // 加入上、下、左、右按键,此处使用 indexof保证键唯一,就无视本次输入
            window.addEventListener('keydown',e=>{
                if ((e.key === 'ArrowDown' ||
                     e.key === 'ArrowUp' ||
                     e.key === 'ArrowLeft' ||
                     e.key === 'ArrowRight') &&
                    this.keys.indexOf(e.key) === -1) {
                    this.keys.push(e.key)
                }
            });

            // 移除按键
            window.addEventListener('keyup',e=>{
                if( e.key === 'ArrowDown' ||
                    e.key === 'ArrowUp' ||
                    e.key === 'ArrowLeft' ||
                    e.key === 'ArrowRight'){
                    this.keys.splice(this.keys.indexOf(e.key), 1)
                }
            });
        }
    }

    class Player{
        constructor(gameWidth,gameHeight){
            this.gameWidth = gameWidth;
            this.gameHeight = gameHeight;
            this.width = 200;
            this.height = 200;
            this.x = 0;
            this.y = this.gameHeight - this.height;
            this.image  = playerImage;
            this.frameX = 0;
            this.frameY = 0;
            this.maxFrame = 8;
            // 速度
            // x轴
            this.speedX = 0;
            // y轴
            this.speedY = 0;

            // 重量
            this.weight = 1;

            //动画20帧
            this.fps = 20;
            this.frameTimer = 0;
            this.frameInterval = 1000/this.fps;
        }
        draw(context){
            context.drawImage(this.image,this.frameX*this.width,this.frameY*this.height,this.width,this.height,this.x,this.y,this.width,this.height);
        }

        update(input,deltaTime){
            // 检测X轴按键
            if(input.keys.indexOf('ArrowRight') > -1){
                this.speedX = 5;
            }
            else if(input.keys.indexOf('ArrowLeft') > -1){
                this.speedX = -5;
            }
            else{
                this.speedX = 0;
            }

            // 检测Y轴按键,且只能从地面上起跳
            if(input.keys.indexOf('ArrowUp') > -1 && this.onGround()){
                this.speedY = -32;
                this.frameY = 1;
                this.frameX = 0;
                this.maxFrame = 5;
                this.y = this.y + this.speedY;
            }

            if(this.frameTimer > this.frameInterval){
                if(this.frameX >= this.maxFrame){
                    this.frameX = 0;
                }
                else{
                    this.frameX++;
                }
                this.frameTimer = 0;
            }
            else{
                this.frameTimer += deltaTime; 
            }
        
            this.x = this.x + this.speedX;

            // 避免出界
            if(this.x < 0){
                this.x = 0
            }
            else if(this.x > this.gameWidth - this.width){
                this.x = this.gameWidth - this.width;
            }

            
            // 跳跃限制
            if(!this.onGround()){
                this.speedY += this.weight;
                this.y = this.y + this.speedY;
                if(this.onGround()){
                    this.y = this.gameHeight - this.height;
                    this.speedY = 0;
                    this.frameY = 0;
                    this.maxFrame = 8;
                }
            }

        }

        // 是否在地面
        onGround(){
            return this.y >= this.gameHeight - this.height;
        }
    }

    class Background{
        constructor(gameWidth, gameHeight) {
            this.gameWidth = gameWidth;
            this.gameHeight = gameHeight;
            this.image = backgroundImage;
            this.x = 0;
            this.y = 0;
            this.width = 2400;
            this.height = 720;
            this.speed = 7;
        }
        draw(context) {
            context.drawImage(this.image, this.x, this.y, this.width, this.height);
            context.drawImage(this.image, this.x + this.width, this.y, this.width, this.height);
        }

        update() {
            this.x -= this.speed
            if (this.x < 0 - this.width){
                this.x = 0;
            } 
        }
    }

    class Enemy{
        constructor(gameWidth, gameHeight) {
            this.gameWidth = gameWidth;
            this.gameHeight = gameHeight;
            this.width = 160;
            this.height = 119;


            this.image = enemyImage;

            this.x = this.gameWidth;
            this.y = this.gameHeight - this.height;

            this.frameX = 0;
            this.maxFrame = 5;

            this.speed = 8;

            // 敌人动画20帧
            this.fps = 20;
            this.frameTimer = 0;
            this.frameInterval = 1000/this.fps;

            this.markedForDeletion = false;
        }
        draw(context) {
            context.drawImage(this.image, this.frameX * this.width, 0, this.width, this.height, this.x, this.y, this.width, this.height)
        }
        update(deltaTime) {
            if(this.frameTimer > this.frameInterval){
                if(this.frameX >= this.maxFrame){
                    this.frameX = 0;
                }
                else{
                    this.frameX++;
                }
                this.frameTimer = 0;
            }
            else{
                this.frameTimer += deltaTime; 
            }

            if(this.x < 0 - this.width){
                this.markedForDeletion = true;
            }

            this.x -= this.speed;
        }
    }


    function handleEnemies(deltaTime){
        if(enemyTimer > enemyInterval + randomEnemyInterval){
            enemies.push(new Enemy(canvas.width,canvas.height));
            randomEnemyInterval = Math.random()*1000 + 500;
            enemyTimer = 0;
        }
        else{
            enemyTimer += deltaTime;
        }
        let flag = false;
        enemies.forEach(e => {
            e.draw(ctx);
            e.update(deltaTime);
            if(!flag && e.markedForDeletion){
                flag = true;
            }
        })
        if(flag){
            enemies = enemies.filter(e=>!e.markedForDeletion);
        }
    }


    function displayStatusText(){
        
    }


    const input = new InputHandler();
    const player = new Player(canvas.width,canvas.height);
    const background = new Background(canvas.weight,canvas.height);

    let lastTime = 0;
    let enemyTimer = 0;
    let enemyInterval = 2000;
    // 让敌人刷出时间不可预测
    let randomEnemyInterval = Math.random()*1000 + 500;

    // 60帧,游戏画面的更新帧
    let frameTimer = 0;
    let frameInterval = 1000/60;


    function animate(timeStamp){
        const deltaTime = timeStamp - lastTime;
        lastTime = timeStamp; 
        frameTimer += deltaTime; 
        if(frameTimer > frameInterval){
            ctx.clearRect(0,0,canvas.width,canvas.height);

            background.draw(ctx);
            // background.update();
    
            handleEnemies(deltaTime);
            player.draw(ctx);
            player.update(input,deltaTime);
            frameTimer = 0;
        }
        requestAnimationFrame(animate);
    }

    animate(0);

});

在这里插入图片描述

7.4 碰撞、计分、重新开始

我们碰撞盒采用圆形,来做简单的碰撞检测

window.addEventListener('DOMContentLoaded',function(){
    const canvas = this.document.getElementById('canvas1');
    const ctx = canvas.getContext('2d');
    canvas.width = 800;
    canvas.height = 720;

    let enemies = [];

    // 输入处理
    class InputHandler{
        constructor(){
            this.keys = [];
            // 加入上、下、左、右按键,此处使用 indexof保证键唯一,就无视本次输入
            window.addEventListener('keydown',e=>{
                if ((e.key === 'ArrowDown' ||
                     e.key === 'ArrowUp' ||
                     e.key === 'ArrowLeft' ||
                     e.key === 'ArrowRight') &&
                    this.keys.indexOf(e.key) === -1) {
                    this.keys.push(e.key)
                }
                else if(e.key === 'Enter' && gameOver){
                    gameReStart();
                }
            });

            // 移除按键
            window.addEventListener('keyup',e=>{
                if( e.key === 'ArrowDown' ||
                    e.key === 'ArrowUp' ||
                    e.key === 'ArrowLeft' ||
                    e.key === 'ArrowRight'){
                    this.keys.splice(this.keys.indexOf(e.key), 1)
                }
            });
        }
    }

    class Player{
        constructor(gameWidth,gameHeight){
            this.gameWidth = gameWidth;
            this.gameHeight = gameHeight;
            this.width = 200;
            this.height = 200;
            this.x = 0;
            this.y = this.gameHeight - this.height;
            this.image  = playerImage;
            this.frameX = 0;
            this.frameY = 0;
            this.maxFrame = 8;
            // 速度
            // x轴
            this.speedX = 0;
            // y轴
            this.speedY = 0;

            // 重量
            this.weight = 1;

            //动画20帧
            this.fps = 20;
            this.frameTimer = 0;
            this.frameInterval = 1000/this.fps;
        }
        draw(context){
            context.strokeStyle = 'white';
            context.strokeRect(this.x,this.y,this.width,this.height);
            context.beginPath();
            context.arc(this.x + this.width/2,this.y+this.height/2,this.width/2,0,Math.PI*2);
            context.stroke();

            context.drawImage(this.image,this.frameX*this.width,this.frameY*this.height,this.width,this.height,this.x,this.y,this.width,this.height);
        }

        update(input,deltaTime){
            // 碰撞检测
            enemies.forEach(e=>{
                const dx = (e.x + e.width/2) - (this.x + this.width/2);
                const dy = (e.y + e.height/2) - (this.y + this.height/2);
                const distance = Math.sqrt(dx*dx + dy*dy);
                if(distance < e.width/2 + this.width/2){
                    gameOver = true;
                }
            });


            // 检测X轴按键
            if(input.keys.indexOf('ArrowRight') > -1){
                this.speedX = 5;
            }
            else if(input.keys.indexOf('ArrowLeft') > -1){
                this.speedX = -5;
            }
            else{
                this.speedX = 0;
            }

            // 检测Y轴按键,且只能从地面上起跳
            if(input.keys.indexOf('ArrowUp') > -1 && this.onGround()){
                this.speedY = -32;
                this.frameY = 1;
                this.frameX = 0;
                this.maxFrame = 5;
                this.y = this.y + this.speedY;
            }

            if(this.frameTimer > this.frameInterval){
                if(this.frameX >= this.maxFrame){
                    this.frameX = 0;
                }
                else{
                    this.frameX++;
                }
                this.frameTimer = 0;
            }
            else{
                this.frameTimer += deltaTime; 
            }
        
            this.x = this.x + this.speedX;

            // 避免出界
            if(this.x < 0){
                this.x = 0
            }
            else if(this.x > this.gameWidth - this.width){
                this.x = this.gameWidth - this.width;
            }

            
            // 跳跃限制
            if(!this.onGround()){
                this.speedY += this.weight;
                this.y = this.y + this.speedY;
                if(this.onGround()){
                    this.y = this.gameHeight - this.height;
                    this.speedY = 0;
                    this.frameY = 0;
                    this.maxFrame = 8;
                }
            }

        }

        // 是否在地面
        onGround(){
            return this.y >= this.gameHeight - this.height;
        }

        restart(){
            this.x = 0;
            this.y = this.gameHeight - this.height;
            this.frameInterval = 0;
            this.maxFrame = 8;
            this.frameY = 0;
        }
    }

    class Background{
        constructor(gameWidth, gameHeight) {
            this.gameWidth = gameWidth;
            this.gameHeight = gameHeight;
            this.image = backgroundImage;
            this.x = 0;
            this.y = 0;
            this.width = 2400;
            this.height = 720;
            this.speed = 5;
        }
        draw(context) {
            context.drawImage(this.image, this.x, this.y, this.width, this.height);
            context.drawImage(this.image, this.x + this.width, this.y, this.width, this.height);
        }

        update() {
            this.x -= this.speed
            if (this.x < 0 - this.width){
                this.x = 0;
            } 
        }

        restart(){
            this.x = 0;
        }
    }

    class Enemy{
        constructor(gameWidth, gameHeight) {
            this.gameWidth = gameWidth;
            this.gameHeight = gameHeight;
            this.width = 160;
            this.height = 119;


            this.image = enemyImage;

            this.x = this.gameWidth;
            this.y = this.gameHeight - this.height;

            this.frameX = 0;
            this.maxFrame = 5;

            this.speed = 8;

            // 敌人动画20帧
            this.fps = 20;
            this.frameTimer = 0;
            this.frameInterval = 1000/this.fps;

            this.markedForDeletion = false;
        }
        draw(context) {
            context.strokeStyle = 'white';
            context.strokeRect(this.x,this.y,this.width,this.height);
            context.beginPath();
            context.arc(this.x + this.width/2,this.y+this.height/2,this.width/2,0,Math.PI*2);
            context.stroke();
            
            context.drawImage(this.image, this.frameX * this.width, 0, this.width, this.height, this.x, this.y, this.width, this.height)
        }
        update(deltaTime) {
            if(this.frameTimer > this.frameInterval){
                if(this.frameX >= this.maxFrame){
                    this.frameX = 0;
                }
                else{
                    this.frameX++;
                }
                this.frameTimer = 0;
            }
            else{
                this.frameTimer += deltaTime; 
            }

            if(this.x < 0 - this.width){
                this.markedForDeletion = true;
                score++;
            }

            this.x -= this.speed;
        }

    }


    function handleEnemies(deltaTime){
        if(enemyTimer > enemyInterval + randomEnemyInterval){
            enemies.push(new Enemy(canvas.width,canvas.height));
            randomEnemyInterval = Math.random()*1000 + 500;
            enemyTimer = 0;
        }
        else{
            enemyTimer += deltaTime;
        }
        let flag = false;
        enemies.forEach(e => {
            e.draw(ctx);
            e.update(deltaTime);
            if(!flag && e.markedForDeletion){
                flag = true;
            }
        })
        if(flag){
            enemies = enemies.filter(e=>!e.markedForDeletion);
        }
    }


    const input = new InputHandler();
    const player = new Player(canvas.width,canvas.height);
    const background = new Background(canvas.weight,canvas.height);

    let lastTime = 0;
    let enemyTimer = 0;
    let enemyInterval = 2000;
    // 让敌人刷出时间不可预测
    let randomEnemyInterval = Math.random()*1000 + 500;

    // 60帧,游戏画面的更新帧
    let frameTimer = 0;
    let frameInterval = 1000/60;

    let score = 0;
    let gameOver = false;


    function displayStatusText(context){
        context.textAlign = 'left';
        context.fillStyle = 'black';
        context.font = '40px Helvetica';
        context.fillText('score:'+score,20,50);
        context.fillStyle = 'white';
        context.font = '40px Helvetica';
        context.fillText('score:'+score,22,52);
        if(gameOver){
            context.textAlign = 'center';
            context.fillStyle = 'black';
            context.fillText('Game Over,press "Enter" to restart!',canvas.width/2,200);
            context.fillStyle = 'white';
            context.fillText('Game Over,press "Enter" to restart!',canvas.width/2,200);
        }
    }



    function animate(timeStamp){
        const deltaTime = timeStamp - lastTime;
        lastTime = timeStamp; 
        frameTimer += deltaTime; 
        if(frameTimer > frameInterval){
            ctx.clearRect(0,0,canvas.width,canvas.height);

            background.draw(ctx);
            background.update();
    
            handleEnemies(deltaTime);
            player.draw(ctx);
            player.update(input,deltaTime);
            displayStatusText(ctx);
            frameTimer = 0;
        }
        if(!gameOver){
            requestAnimationFrame(animate);
        }
    }

    animate(0);

    function gameReStart(){
        player.restart();
        background.restart();

        score = 0;
        enemies = [];
        gameOver = false;

        frameTimer = 0;
        enemyTimer = 0;
        lastTime = 0;

        randomEnemyInterval = Math.random()*1000 + 500;
        animate(0);
    }
});



在这里插入图片描述

7.5 手机格式

我们进入浏览器的开发者模式,将浏览器设置为手机。

*{
    margin: 0;
    padding:0;
    box-sizing: border-box;
}
body{
    background: black;
}

#canvas1{
    position: absolute;
    top:50%;
    left: 50%;
    transform: translate(-50%,-50%);
    border: 5px solid white;
    max-width: 100%;
    max-height: 100%;
}

#playerImage,#backgroundImage,#enemyImage{
    display: none;
}

在这里插入图片描述
输入的指令如下


			this.touchY = ''; // Y 轴滑动
            this.touchThreshold = 30 ;// 超过30认为滑动
            window.addEventListener('keydown', e => {
                if ((e.key === 'ArrowDown' ||
                        e.key === 'ArrowUp' ||
                        e.key === 'ArrowLeft' ||
                        e.key === 'ArrowRight') &&
                    this.keys.indexOf(e.key) === -1) {
                    this.keys.push(e.key);
                }else if(e.key==='Enter'&&gameOver) restartGame()
            })
            // 手指、指针起始位置
            window.addEventListener('touchstart',e=>{
                this.touchY=e.changedTouches[0].pageY;
            })
            // 手指、指针移动中
            window.addEventListener('touchmove',e=>{
                const swipeDistance=e.changedTouches[0].pageY-this.touchY;
                if(swipeDistance<-this.touchThreshold && this.keys.indexOf('swipe up')===-1) {
                	this.keys.push('swipe up');
                }
                else if(swipeDistance>this.touchThreshold && this.keys.indexOf('swipe down')===-1) {
                    this.keys.push('swipe down');
                    if(gameOver) restartGame();
                }
            }) 
            // 手指、指针移动结束
            window.addEventListener('touchend',e=>{
                console.log(this.keys);
                this.keys.splice(this.keys.indexOf('swipe up'),1);
                this.keys.splice(this.keys.indexOf('swipe down'),1);
            }) 

判断时,只需要在执行处加入相应的标志即可。

同理,我们可以加入横向滑动操作,随着如果手指沿着X轴移动,我们可以认为X轴方向移动角色。X轴位移不为0则加入,为0则停止。

如果进入手机模式,滑动时,窗口也跟着滑动,可以试着加入如下代码

       function stopScroll() {
        var html = document.getElementsByTagName('html')[0];
        var body = document.getElementsByTagName('body')[0];
        var o = {};
        o.can = function () {
            html.style.overflow = "visible";
            html.style.height = "auto";
            body.style.overflow = "visible";
            body.style.height = "auto";
        },
            o.stop = function () {
                html.style.overflow = "hidden";
                html.style.height = "100%";
                body.style.overflow = "hidden";
                body.style.height = "100%";
            }
        return o;
    }
    const scroll = stopScroll();
    scroll.stop();  

7.6 全屏模式

#fullScreenButton{
    position: absolute;
    font-size: 20px;
    padding: 10px;
    top: 10px;
    left: 50%;
    transform: translateX(-50%);
}
<!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>JavaScript 2D Game</title>
    <link rel="stylesheet" href="./stylte.css">
</head>

<body>
    <canvas id="canvas1"></canvas>
    <img src="./player.png" id="playerImage" alt="playerImage">
    <img src="./backgroundImage.png" id="backgroundImage" alt="backgroundImage">
    <img src="./worm.png" id="enemyImage" alt="enemyImage">
    <button id="fullScreenButton">Toggle Fullscreen</button>

    <script src="./script.js"></script>
</body>

</html>
function toggleFullScreen(){
        if(!document.fullscreenElement){
            canvas.requestFullscreen().then().catch(err=>{
                alert(`错误,切换全屏模式失败:${err.message}`)
            })
        }else{
            document.exitFullscreen()
        }
    }
    fullScreenButton.addEventListener('click',toggleFullScreen)

在这里插入图片描述

7.7 存在的问题

  1. 碰撞盒太大了,我们可能需要移动和缩小,来让判定更准确,或者玩起来更容易
  2. 没有很好的填充满屏幕,需要相应的js算法来帮助

其他笔者未解决问题:

  1. 如上方式,在重新开始后,游戏角色动作”变快“(时间间隔仍旧一样)。
  2. 此外,我们重新开始后,必然立即刷一只怪物
  3. 一些浏览器的页面再切换后,我们隔一段时间再返回,可以刷出更多怪物

考虑如果自己通过循环来计数,是否可以解决部分问题。

附录

[1]源-素材地址
[2]源-视频地址
[3]搬运视频地址(JavaScript 游戏开发)
[4]github-视频的素材以及源码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/41261.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

手摸手使用IDEA创建多模块(maven)项目

有了前面两篇手摸手打底&#xff0c;相信大家对IDEA创建项目和配置使用maven已经没有什么问题了。那么这篇文章阅读起来也会非常流畅。 对于IDEA来说&#xff0c;可以用拼接模块&#xff08;Module&#xff09;并引用的方式来“搭”一个项目&#xff08;Project&#xff09;。…

【全志T113-S3_100ask】15-2 linux系统gpio模拟spi驱动屏幕——ILI9341

【全志T113-S3_100ask】15-2 linux系统gpio模拟spi驱动屏幕——ILI9341背景&#xff08;一&#xff09;查阅参考文档&#xff08;二&#xff09;使能内核模块&#xff08;三&#xff09;修改设备树&#xff08;四&#xff09;测试&#xff08;五&#xff09;后语背景 在上一小…

nginx的安装与nginx降权+匹配php

安装nginx: 安装的插件的作用: 1.gcc 可以编译 C,C,Ada,Object C和Java等语言&#xff08;安装 nginx 需要先将官网下载的源码进行编译&#xff0c;编译依赖 gcc 环境&#xff09; 2.pcre pcre-devel pcre是一个perl库&#xff0c;包括perl兼容的正则表达式库&#xff0c;ngin…

样本于抽样分布(1)-基本概念

从理论上讲&#xff0c;只要对随机现象进行足够多次的试验&#xff0c;被研究的随机现象的规律 性就能清楚地呈现出来&#xff0e; 但实际上&#xff0c;试验的次数只能是有限的&#xff0c;有时甚至是很少 的&#xff0c;因为采集某些数据时&#xff0c;常要将研究的对象破坏&…

【Gradle】四、使用Gradle创建SringBoot微服务项目

使用Gradle创建SringBoot微服务项目一、 创建Springboot项目0、阿里云脚手架创建项目1、引入 t springboot2 、引入依赖3、执行 geradle bootRun 指令4、spring-boot-gradle-plugin 插件‘二、多模块项目1、settings.gradle2、build.gradle3、version.gradle4、test_service的配…

图论:自反与对称

图论1.自反与反自反2.对称与反对称3.传递与非传递1.自反与反自反 自反&#xff1a;相同顶点都在集合内。 反自反&#xff1a;相同顶点都不在集合内。 参考下图&#xff1a;有三部分&#xff0c;红色的自反&#xff0c;蓝色的反自反&#xff0c;以及白色的都不是。 例1&#…

Animation

1、Animation窗口 Window——>Animation——>Animation Animation窗口 直译就是动画窗口&#xff0c;它主要用于在Unity内部创建和修改动画&#xff0c;所有在场景中的对象都可以通过Animation窗口为其制作动画 原理&#xff1a; 制作动画时&#xff1a;记录在固定时间…

[Linux]-----进程信号

文章目录前言一、什么是信号我们是如何得知这些信号呢&#xff1f;我们知道对应的信号产生时&#xff0c;要做什么呢&#xff1f;二、进程信号前台进程和后台进程注意三、信号列表信号的捕捉四、信号产生前用户层产生信号的方式signal函数killraiseabort由软件条件产生信号硬件…

【Java】才疏学浅·小石Java问道之路

大家好&#xff0c;这里是喜爱编程&#xff0c;又热爱生活的小石~———— ————个人情报昵称&#xff1a;小石&#xff08;起源于姓氏啦~&#xff09;破壳日&#xff1a;4月12日身高&#xff1a;1 m ↑技术基础&#xff1a;c node.js mysql 爱好&#xff1a;上网冲浪 听…

【学习笔记45】JavaScript的分页效果

highlight: an-old-hope 一、分页效果 (一) 首次打开页面 从数组内截取部分数据展示调整页码信息为&#xff1a;当前页 / 总页码处理按钮 3.1 如果当前在第一页&#xff0c;禁用上一页按钮(添加类名disable)3.2 如果当前页在最后一页(当前页 总页码),禁用下一页按钮(添加类名…

SpringCloud微服务实践之六 Feign最佳实践(抽取)

传统Feign面临的问题&#xff1a; 1、每个子项目都要写所要调用服务的pojo 2、每个子项目都要写所要调用服务的feign client客户端 优化思路&#xff1a;由提供服务服务的子项目统一归集代码&#xff0c;统一对外提供接口服务、Feign子项目统一管理服务远程调用、 将FeignClien…

【菜菜的sklearn课堂笔记】逻辑回归与评分卡-用逻辑回归制作评分卡-分箱

视频作者&#xff1a;菜菜TsaiTsai 链接&#xff1a;【技术干货】菜菜的机器学习sklearn【全85集】Python进阶_哔哩哔哩_bilibili 分训练集和测试集 from sklearn.model_selection import train_test_split X pd.DataFrame(X) y pd.DataFrame(y)Xtrain,Xtest,Ytrain,Ytest …

DSP-FIR滤波器设计

目录 Gibbs现象:用三角函数逼近间断点: Gibbs现象特点: 常见窗函数&#xff1a; 窗函数的主要频谱参数: 矩形窗(Rectangular)&#xff1a; 汉宁窗(Hanning)&#xff1a; 汉明窗(Hamming)&#xff1a; 布莱克曼窗(Blackman)&#xff1a; 窗函数之间的性能对比&#xff…

Script file ‘F:.....\pip-script.py‘ is not present 原因及解决办法

一 报错类型 二 原因 可能我们使用pip install --upgrade pip或者conda安装一下包时因为网络原因导致只是卸载旧版本而未安装。 三 解决策略 3.1 Anaconda 切换到你的anaconda安装目录并进入Scripts文件夹内(D:\Apps\anaconda3\Scripts) 运行以下代码&#xff1a; conda i…

【Kafka】Kafka基础架构及相关概念

文章目录前言一、Kafka基础知识二、Kafka分区副本参考前言 在以前的定义中&#xff0c;Kafka被定义为一个分布式的基于发布/订阅模式的消息队列&#xff08;Message Queue&#xff09;&#xff0c;主要应用于大数据实时处理领域&#xff0c;类似的产品主要有ActiveMQ、RabbitM…

flutter useRootNavigator属性的作用

useRootNavigator 用于确定是否将对话框推到距给定上下文最远或最接近给定上下文的Navigator 问题&#xff1a; 在使用showDatePicker的时候&#xff0c;android手机如果侧滑返回的时候&#xff0c;页面会关闭&#xff0c;showDatePicker弹出的dailog缺没有关闭。 使用如下…

【学习笔记42】操作DOM

操作DOM一、操作DOM1、步骤2、创建元素节点3、创建文本节点4、增加dom(添加到指定父节点的最后)5、增加dom(添加到指定父节点的最后)6、增加dom(添加到父节点的最前边)7、删除DOM8、修改某一个节点二、克隆DOM1、说明2、复制(克隆)一个LI三、获取元素尺寸(占地面积)四、获取浏览…

WordPress怎么禁止用户使用HTML标签,自动过滤HTML代码?

WordPress怎么禁止用户使用HTML标签&#xff0c;自动过滤HTML代码&#xff1f;出于安全考虑WordPress默认禁止角色为作者的用户写文章时直接添加HTML代码&#xff0c;包括读者留言时也是不允许的。如果想开放此限制&#xff0c;允许作者撰写文章和读者留言时添加HTML代码&#…

Java项目——博客系统(前后端交互)

项目展示 项目说明 使用servlet&#xff0c;实现前后端交互&#xff0c;通过jdbc和mysql数据库进行数据传输&#xff0c;使得可以将用户信息和博客列表存储在数据库中&#xff0c;实现真正的服务器&#xff0c;客户端&#xff0c;数据库三者的交互 项目代码 数据库 在sql数…

进阶的风控策略篇:如果筛选最佳策略帮我们锁定优质客群

在番茄风控往期的内容中&#xff0c;我们一直在跟大家介绍风控策略干货内容&#xff0c;相关内容包括&#xff1a; ①风控的拒绝捞回策略 ②多规则的策略筛选 ③策略的调优 ④策略的开发与应用 … 策略相关的内容可谓干货满满&#xff0c;比如关于策略开发与应用的内容上&#…