首页 前端知识 NewMoonDog 影子狗 基于JavaScript的跑酷游戏,复制就能玩

NewMoonDog 影子狗 基于JavaScript的跑酷游戏,复制就能玩

2024-09-10 23:09:42 前端知识 前端哥 227 384 我要收藏

这是一款横版跑酷类游戏,应为是JavaScript的所以不需要其他复杂的配置和环境,点击就能运行

线上试玩:http://longsong.games/newmoon

文末有代码地址

操作介绍:

↑ 跳跃 ↓ 坐下 跳砍 ← 往后跑 → 向前跑 enter 无敌翻滚 d 显示外形 q 重新开始游戏 energy 能量


 

 下面是项目的目录:

其中主要的几个:

main.js:

import { Player } from './player.js';
import { InputHandle } from './input.js';
import { Background} from './background.js';
import { FlyingEnemy, ClimbEnemy, GroundEnemy} from './enemies.js';
import { UI } from './UI.js';

window.addEventListener('load', function(){
    const canvas = document.getElementById('canvas1');
    const ctx = canvas.getContext('2d');
    canvas.width = 900;
    canvas.height = 500;
    let enemies = [];
    let score = 0;
    class Game{
        constructor(width, height){
            this.width = width;
            this.height = height;
            this.groundMargin = 40;
            this.speed = 0;
            this.maxSpeed = 5;
            this.background = new Background(this);
            this.player = new Player(this);
            this.input = new InputHandle(this);
            this.UI = new UI(this);
            this.enemies = [];
            this.particles = [];
            this.collisions = [];
            this.floatingMessages = [];
            this.maxParticles = 200;
            this.enemyTimer = 0;
            this.enemyInterval = 1000;
            this.debug = true;
            this.score = 0;
            this.energy = 0;
            this.time = 0;
            this.winningScore = 40;
            this.maxTime = 300000;
            this.fontColor = 'black';
            this.player.currentState = this.player.state[0];
            this.player.currentState.enter();
            this.lives = 5;
            this.game = false;
            this.pan = 1;
        }
        update(deltaTime){
            this.time += deltaTime;
            if (this.time > this.maxTime) this.gameOver = true;
            if (this.energy >= 0 ) this.energy += 1;
            if (this.energy < 0 && this.pan == 1 )this.energy += 1;
            this.background.update();
            this.player.update(this.input.keys, deltaTime);
            if(this.enemyTimer > this.enemyInterval){
                this.addEnemy();
                this.enemyTimer =0;
                
            }
            else {
                this.enemyTimer += deltaTime;
            }
            //handleEnemies
            this.enemies.forEach(enemy =>{
                enemy.update(deltaTime);
            })
            //handle message
            this.floatingMessages.forEach(message =>{
                message.update(deltaTime);
            })
            //handle partyicles
            this.particles.forEach((particle, index) =>{
                particle.update();
            });
            if (this.particles.length > this.maxParticles) {
                this.particles.length = this.maxParticles;
            }
            this.collisions.forEach((collision, index) => {
                collision.update(deltaTime);

            });
            this.collisions = this.collisions.filter(collision => !collision.markedForDeletion);
            this.particles = this.particles.filter(particle => !particle.markedForDeletion);
            this.enemies = this.enemies.filter(enemy => !enemy.markedForDeletion);
            this.floatingMessages = this.floatingMessages.filter(message => !message.markedForDeletion);
        }
        
        draw(context){

            this.background.draw(context);
            this.player.draw(context);
            this.enemies.forEach(enemy =>{
                enemy.draw(context);
            });
            this.particles.forEach(particle => {
                particle.draw(context);
            });
            this.collisions.forEach(collision => {
                collision.draw(context);
            this.floatingMessages.forEach(message =>{
                 message.draw(context);
            })
            });
            this.UI.draw(context);
        }
        addEnemy(){
            if (this.speed > 0 && Math.random() < 0.5) this.enemies.push(new GroundEnemy(this));
            else if (this.speed > 0 ) this.enemies.push(new ClimbEnemy(this));
            this.enemies.push(new FlyingEnemy(this));
            console.log(this.enemies);
        }
        restartGame(){
            this.player.restart();
            this.background.restart();
            this.enemies = [];
            this.gameOver = false;
            this.score = 0;
            this.energy = 0;
            this.lives = 5;
            this.time = 0;
            animate(0);
        }
    }

    const game = new Game(canvas.width, canvas.height);
    let lastTime = 0;
    

    function animate(timeStamp){
        const deltaTime = timeStamp - lastTime;
        lastTime = timeStamp;
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        game.update(deltaTime);
        game.draw(ctx);
        if (!game.gameOver) requestAnimationFrame(animate);
    }
    animate(0);
})


 

player.js

import { Sitting, Runing, Jumping, Falling, Rolling, Diving, Hit} from './playerStates.js';
import { CollisionAnimation } from './CollisionAnimation.js'
import { FloatingMessage } from './floatingMessages.js'

export class Player{
    constructor(game){
        this.game = game;
        this.width = 100;
        this.height = 91.3;
        this.x = 0;
        this.y = this.game.height - this.height - this.game.groundMargin;
        this.vy = 0;
        this.weight = 1;
        this.image = document.getElementById('player');
        this.frameX = 0;  //第几行图片
        this.frameY = 0;   //第几个状态
        this.maxFrame;
        this.fps = 20;        //帧数
        this.frameInterval = 500/this.fps;
        this.f = 0;
        this.speed = 0;
        this.maxSpeed = 10;
        this.state = [new Sitting(this.game), new Runing(this.game), new Jumping(this.game), new Falling(this.game), new Rolling(this.game), new Diving(this.game), new Hit(this.game)];
        this.currentState = null;
        
    }
    update(input, deltaTime){
        //this.x++;
        //horizon movement
        this.checkCollision();
        this.currentState.handleInput(input);
        this.x += this.speed;
        if (input.includes('ArrowRight') && this.currentState !== this.state[6]) this.speed = this.maxSpeed;
        else if (input.includes('ArrowLeft') && this.currentState !== this.state[6]) this.speed = -this.maxSpeed;
        else this.speed = 0;
        if (this.currentState === this.state[4]) this.game.energy -= 4;
        if (this.x <0) this.x = 0;
        if (this.x > this.game.width - this.width) this.x = this.game.width - this.width;
        //vertical movement
        this.y += this.vy;
        if (!this.onGround()) this.vy += this.weight;
        else this.vy = 0;
        //console.log(this.game.height - this.height);
        // sprite animation
        if (this.y > this.game.height - this.height - this.game.groundMargin) this.y = this.game.height - this.height - this.game.groundMargin;
        if (this.f > this.frameInterval){
            this.f = 0;
            if (this.frameX < this.maxFrame) this.frameX++;
            else this.frameX = 0;
        }
        else {
            this.f =this.f +  deltaTime;
            
        }
        
        //if (this.frameX < this.maxFrame) this.frameX++;
        //else this.frameX = 0;
    }
    restart(){
        this.x = 0;
        this.y = this.game.height - this.height - this.game.groundMargin;
        
    }
    draw(context){
        if (this.game.debug) context.strokeRect(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);
    }
    onGround(){
        return this.y >= this.game.height - this.height - this.game.groundMargin;
    }
    setState(state, speed){
        this.currentState = this.state[state];
        this.game.speed = this.game.maxSpeed * speed;
        this.currentState.enter();
    }
    checkCollision(){

        this.game.enemies.forEach(enemy => {

            if (enemy.x < this.x + this.width && 
                enemy.x + enemy.width > this.x &&
                enemy.y < this.y + this.height &&
                enemy.y + enemy.height > this.y
            ){
                enemy.markedForDeletion = true;
                this.game.collisions.push(new CollisionAnimation(this.game, enemy.x + enemy.width * 0.5,enemy.y + enemy.height * 0.5 ));
                if (this.currentState === this.state[4] || this.currentState ===this.state[5]){
                    this.game.score++;
                    this.game.energy += 100;
                    this.game.floatingMessages.push(new FloatingMessage('+1', enemy.x, enemy.y,150, 50));
                }
                else {
                    this.setState(6, 0);
                    this.game.score -=5 ;
                    this.game.lives --;
                    if (this.game.lives <= 0) this.game.gameOver = true;
                }
                //collosion detected
            } else {
                //no collision
            }
        });
    }
}

 

playerState.js

import { Dust,Fire,Splash } from './particles.js'

const state = { //状态
    SITTING: 0,
    RUNNING: 1,
    JUMPING: 2,
    FALLING: 3,
    ROLLING: 4,
    DIVING: 5,
    HIT: 6
}

class State {
    constructor(state, game){
        this.state = state;
        this.game = game;

    }
}

export class Sitting extends State {
    constructor(game){
        super('SITTING', game);

    }
    enter(){
        this.game.player.frameX = 0;
        this.game.player.frameY = 5;
        this.game.player.maxFrame = 4;
    }
    handleInput(input){
//对初始状态的输入
        if (input.includes('ArrowLeft') || input.includes('ArrowRight')){
            this.game.player.setState(state.RUNNING, 1);
        }
        else if (input.includes('ArrowUp')){
            this.game.player.setState(state.JUMPING, 1);
        }
        else if (input.includes('Enter') && this.game.energy >= 0){
            this.game.player.setState(state.ROLLING, 2);
        }

    }
}
export class Runing extends State { //奔跑
    constructor(game){
        super('RUNNING', game);

    }
    enter(){
        this.game.player.frameX = 0;
        this.game.player.frameY = 3;
        this.game.player.maxFrame = 8;
    }
    handleInput(input){
        if (input.includes('Enter')) {
            this.game.pan = 0;
        }
        else this.game.pan = 1;
        this.game.particles.unshift(new Dust(this.game, this.game.player.x + this.game.player.width * 0.7, this.game.player.y + this.game.player.height));
        if (input.includes('ArrowDown')){
            this.game.player.setState(state.SITTING, 0);
        }
        else if (input.includes('ArrowUp')){
            this.game.player.setState(state.JUMPING, 1);
        }else if (input.includes('Enter')&& this.game.energy >= 0){
            this.game.player.setState(state.ROLLING, 2);
        }
    }
}
export class Jumping extends State { //跳远

    constructor(game){
        super('JUMPING',game);
    }
    enter(){
        if (this.game.player.onGround()) this.game.player.vy -= 27;
        this.game.player.frameX = 0;
        this.game.player.frameY = 1;
        this.game.player.maxFrame = 6;
    }
    handleInput(input){
        if (input.includes('Enter')) {
            this.game.pan = 0;
        }
        else this.game.pan = 1;
        if (this.game.player.vy > this.game.player.weight){
            this.game.player.setState(state.FALLING, 1);
        }else if (input.includes('Enter') && this.game.energy >= 0){
            this.game.player.setState(state.ROLLING, 2);
        }else if (input.includes('ArrowDown')){
            this.game.player.setState(state.DIVING, 0);
        }
    }
}

export class Falling extends State { //掉落
    constructor(game){
        super('FALLING', game);
    }
    enter(){
        this.game.player.frameX = 0;
        this.game.player.frameY = 2;
        this.game.player.maxFrame = 6;
    }
    handleInput(input){
        if (input.includes('Enter')) {
            this.game.pan = 0;
        }
        else this.game.pan = 1;
        if (this.game.player.onGround()){
            this.game.player.setState(state.RUNNING, 1);
        }else if (input.includes('ArrowDown')){
            this.game.player.setState(state.DIVING, 0);
        }else if (input.includes('Enter') && this.game.energy >= 0){
            this.game.player.setState(state.ROLLING, 2);
        }
    }
}

export class Rolling extends State { //冲刺
    constructor(game){
        super('ROLLING', game);

    }
    enter(){
        this.game.player.frameX = 0;
        this.game.player.frameY = 6;
        this.game.player.maxFrame = 6;
    }
    handleInput(input){
        this.game.particles.unshift(new Fire(this.game, this.game.player.x + this.game.player.width * 0.5, this.game.player.y + this.game.player.height * 0.5));
        if (this.game.energy < 0){
            if (this.game.player.onGround()){

            this.game.player.setState(state.RUNNING, 1);

        } else if (!this.game.player.onGround()){
            this.game.player.setState(state.FALLING, 1);

        } else if ( input.includes('ArrowUp') && this.game.player.onGround()){
            this.game.player.vy -= 27;

        } else if (input.includes('ArrowDown') && !this.game.player.onGround()) {
            this.game.player.vy -= 27;

        }
        }else{
            if (!input.includes('Enter') && this.game.player.onGround()){

            this.game.player.setState(state.RUNNING, 1);

        } else if (!input.includes('Enter') && !this.game.player.onGround()){
            this.game.player.setState(state.FALLING, 1);

        } else if (input.includes('Enter') && input.includes('ArrowUp') && this.game.player.onGround()){
            this.game.player.vy -= 27;

        } else if (input.includes('ArrowDown') && !this.game.player.onGround()) {
            this.game.player.vy -= 27;

        }
        }

    }
}

export class Diving extends State { //急速下坠
    constructor(game){
        super('DIVING', game);

    }
    enter(){
        this.game.player.frameX = 0;
        this.game.player.frameY = 6;
        this.game.player.maxFrame = 6;
        this.game.player.vy = 15;
    }
    handleInput(input){
        if (input.includes('Enter')) {
            this.game.pan = 0;
        }
        else this.game.pan = 1;
        this.game.particles.unshift(new Fire(this.game, this.game.player.x + this.game.player.width * 0.5, this.game.player.y + this.game.player.height * 0.5));
        if (this.game.player.onGround()){
            this.game.player.setState(state.RUNNING, 1);
            for(let i = 0; i < 30;i++){
                this.game.particles.unshift(new Splash(this.game, this.game.player.x + this.game.player.width * 0.5, this.game.player.y + this.game.player.height * 0.5));
            }
            
        } else if (input.includes('Enter') && !this.game.player.onGround()){
            this.game.player.setState(state.ROLLING, 2);
        }
    }
}

export class Hit extends State { //眩晕
    constructor(game){
        super('HIT', game);
    }
    enter(){
        this.game.player.frameX = 0;
        this.game.player.frameY = 4;
        this.game.player.maxFrame = 10;
    }
    handleInput(input){
        if (this.game.player.frameX >= 10 && this.game.player.onGround() ){
            this.game.player.setState(state.RUNNING, 1);
        } else if (this.game.player.frameX >= 10 && !this.game.player.onGround()){
            this.game.player.setState(state.FALLING, 1);
        }
    }
}

 代码库:

https://github.com/longsongline/NewMoonDog/icon-default.png?t=N2N8https://github.com/longsongline/NewMoonDog/

哪里看不懂的可以在下面留言,或者私信问我

图片我都上传到自己的服务器上上了,所以代码库里没有图片

参考:十个小时教程 ,喜欢的可以自己跟着教程做出一个差不多样子的

https://www.youtube.com/watch?v=GFO_txvwK_c&list=WL&index=12

转载请注明出处或者链接地址:https://www.qianduange.cn//article/18069.html
标签
评论
发布的文章

jQuery 选择器

2024-05-12 00:05:34

cdn引入前端插件

2024-10-13 20:10:14

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!