开维游戏引擎实例:飞机大战

开维游戏引擎(Kaiwei Engine)是基于js设计的跨平台游戏引擎。内核c++编写,v8引擎封装游戏函数,Assembly实现htm跨平台高效运行。下面以"飞机大战"小游戏为例,演示实现过程。

代码如下:

javascript 复制代码
// 飞机大战小游戏

game.init()
game.setFPS(10);

var window = game.getWindow(); // 获取资源对象
var texture = game.getResource().getTexture("img/logo.png"); // 获取纹理数据对象
window.setIcon(texture); // 设置主游戏窗口图标
window.setTitle("开维游戏引擎-飞机大战小游戏"); // 设置主游戏窗口标题

// Audio音乐类,设置背景音乐
var audio = new Audio();
audio.playMusic("sound/bg.ogg"); // 播放背景音乐,循环播放

new AirplaneBattle();

game.run();
class Airplane {
    // 飞机
    sprite;
    // 用户的飞机
    userAirPlane;
    // 子弹
    bullets = [];
    // 是否是敌方飞机
    isEnemy;
    heart = 3;
    heartList = [];
    // 速度
    speed = 3;
    // 子弹速度
    bulletSpeed = 10;
    // 创建子弹间隔
    createBulletInterval = 2000;
    // 创建子弹时间
    createBulletTime = 0;
    scene;

    // 获取飞机对象
    getSprite = function () {
        return this.sprite;
    }

    // 获取子弹
    getBullets = function () {
        return this.bullets;
    }

    constructor(x, y, w, h, enemy,userAirPlane) {
        this.scene = GlobalVariable.scene;
        this.bullets = [];
        this.isEnemy = enemy;
        if (userAirPlane){
            this.userAirPlane = userAirPlane;
        }

        let cache_ = game.getResource();
        let texture;
        if (enemy){
            texture = cache_.getTexture("shmup_obj/enemy02.png");
        }else {
            texture = cache_.getTexture("shmup_obj/airplane_01_64x64.png");
            this.heartUpdate()
        }
        const node = new Sprite();
        node.setTexture(texture);
        node.setSize(w, h);
        node.setPosition(x, y);
        node.setColor(1, 1, 1,1);
        this.scene.addNode(node);
        this.sprite = node;

        // 创建子弹
        this.createBullets(x, y);
        // 更新事件
        node.upDate(()=>{
            if(GlobalVariable.gameOver){
                return;
            }
            let w = game.getWindow().getWidth();
            let h = game.getWindow().getHeight();

            let x;
            let y;

            let sprite = this.sprite;
            if (sprite) {
                let pos = Util.getPosition(sprite);
                x = pos.x;
                y = pos.y;
            }

            // 创建子弹
            let now = new Date().getTime();
            let createBulletTime = this.createBulletTime;
            let bulletInterval = this.createBulletInterval;

            if (createBulletTime) {
                // 敌人创建子弹
                if (this.isEnemy) {
                    if (now - createBulletTime > bulletInterval && y <= h && sprite) {
                        this.createBullets(x, y);
                    }
                } else {
                    // 创建子弹
                    if (now - createBulletTime > bulletInterval/3 && sprite) {
                        this.createBullets(x, y);
                    }
                }
            }

            // 敌方飞机移动
            if (this.isEnemy && this.sprite) {
                this.sprite.setPosition(x, y + this.speed);
                if (y > h + 10) {
                    this.resetPos();
                }
            }
            // 子弹碰撞
            this.bulletCrash();
            // 飞机碰撞
            this.crashAirplane();

            // 子弹移动
            let bullets = this.bullets;
            if (bullets && bullets.length > 0) {
                for (let i = 0; i < bullets.length; i++) {
                    let bullet = bullets[i];
                    let pos = Util.getPosition(bullet);
                    let bulletX = pos.x;
                    let bulletY = pos.y;
                    if (this.isEnemy) {
                        if(bulletY < h + 80 && bulletY > 0) {
                            bullet.setPosition(bulletX, bulletY + 2 * this.bulletSpeed);
                        }
                    } else {
                        if (bulletY > -80 && bulletY < h ){
                            bullet.setPosition(bulletX, bulletY - 4 * this.bulletSpeed);
                        }
                    }
                }
            }

            if (GlobalVariable.gameOver) {
                //this.gameEndLogic();
                return;
            }
        })

    }


    // 血量变化
    bloodChange(){
        log(""+this.heart)
        if (this.isEnemy){
            return;
        }
        this.heart -= 1;
        if (this.heart <= 0){
            GlobalVariable.gameOver = true;
        }
        this.heartUpdate()

        if (GlobalVariable.gameOver) {
            let w = game.getWindow().getWidth();
            let h = game.getWindow().getHeight();
            Util.newText({
                text: "游戏结束",
                x: w/3 + 70,
                y: 10,
                width:90,
            });


            // 重新开始按钮
            Util.newSprite({
                x: w/2-73,
                y: h/2-26,
                width: 147,
                height: 53,
                texture: 'restart.png',
                clickCb: ()=>{
                    new AirplaneBattle()
                }
            })
        }
    }


    // 移除
    remove = function () {
        this.removeBullet();
        this.sprite = null;
    }

    // 移除子弹
    removeBullet() {
        let bullets1 = this.bullets;
        for (let bullets1Element of bullets1) {
            bullets1Element.setHide(true)
        }
        this.bullets = [];
    };

    // 改变位置
    resetPos() {
        let w = game.getWindow().getWidth();
        let h = game.getWindow().getHeight();

        if (this.sprite) {
            let x = Math.floor(Math.random() * (w - 100)) + 50;
            this.sprite.setHide(false);
            this.sprite.setPosition(x, -10);
        }
    };

    // 创建子弹
    createBullets(x, y) {
        let cache_ = game.getResource();

        if (!this.sprite) {
            return;
        }
        let bulletPng;
        if (this.isEnemy) {
            bulletPng = cache_.getTexture("shmup_obj/bullet_01_32x32.png");
            y = y + 60;
        } else {
            bulletPng = cache_.getTexture("shmup_obj/bullet_01_32x32_up.png");
            y = y - 80;
        }
        let bullet = new Sprite();
        bullet.setPosition(x, y);
        bullet.setTexture(bulletPng);
        bullet.setSize(64, 64);
        bullet.setColor(1,1,1,1);

        if(this.scene){
            this.scene.addNode(bullet);
            this.createBulletTime = new Date().getTime();
            this.bullets.push(bullet);
        }
    }

    // 飞机碰撞检测
    crashAirplane() {
        let enemy = this.isEnemy;
        let userAirPlane = this.userAirPlane;
        let sprite = this.sprite;
        if (enemy && sprite) {
            if(!userAirPlane){
                return;
            }

            let userSprite = userAirPlane.getSprite();

            // 飞机与飞机碰撞检测
            if (this.crash(sprite, userSprite)) {
                sprite.setHide(true);
                this.resetPos();
                userAirPlane.bloodChange()
                return;
            }

            // 子弹碰撞敌方飞机检测
            let bullets = userAirPlane.getBullets();
            if (bullets && bullets.length > 0) {
                for (let j = 0; j < bullets.length; j++) {
                    let bullet = bullets[j];
                    if (this.crash(bullet, sprite)) {
                        bullet.setHide(true);
                        sprite.setHide(true);
                        GlobalVariable.score +=  1;
                        GlobalVariable.scoreText.setText("分数:"+GlobalVariable.score);

                        bullets.splice(j, 1);
                        this.resetPos()
                        return;
                    }
                }
            }

            // 敌方子弹碰撞飞机检测
            let bullets2 = this.bullets;
            if (bullets2 && bullets2.length > 0) {
                for (let j = 0; j < bullets2.length; j++) {
                    let enemyBullet = bullets2[j];
                    if (this.crash(enemyBullet, userSprite)) {
                        bullets2.splice(j, 1);

                        enemyBullet.setHide(true);
                        userAirPlane.bloodChange()
                        return;
                    }
                }
            }
        }
    }

    // 子弹碰撞检测
    bulletCrash() {
        let enemy = this.isEnemy;
        let userAirPlane = this.userAirPlane;
        let sprite = this.sprite;
        if (enemy && sprite) {
            if(!userAirPlane){
                return;
            }
            let bullets = userAirPlane.getBullets();
            let bullets2 = this.bullets;
            if (bullets2 && bullets2.length > 0) {
                for (let i = 0; i < bullets2.length; i++) {
                    let enemyBullet = bullets2[i];
                    if (bullets && bullets.length > 0) {
                        for (let j = 0; j < bullets.length; j++) {
                            let bullet = bullets[j];
                            if (this.crash(bullet, enemyBullet)) {
                                bullet.setHide(true);
                                enemyBullet.setHide(true);
                                bullets2.splice(i, 1);
                                i--;
                                bullets.splice(j, 1);
                                j--;
                                break;
                            }
                        }
                    }
                }
            }
        }
    }

    // 碰撞检测
    crash(o1, o2) {
        if (o1 && o2) {
            if (Physics.rectRect(Util.getPosition(o1), Util.getPosition(o2))) {
                audio.playSound("sound/1.wav"); // 循环音效,例如按钮点击声、脚步声、爆炸声、技能音效
                return true;
            }
        }
        return false;
    }


    // 创建血量
    createHeart(x, y,full){
        const cache_ = game.getResource();
        let img;
        if (full){
            img = cache_.getTexture("full-heart.png");
        }else {
            img = cache_.getTexture("empty-heart.png");
        }
        const heart = new Sprite();
        // log("sprite start")
        // ObjUtil.printAllObjectMethods(heart);
        heart.setTexture(img);
        heart.setPosition(x, y);
        heart.setSize(24, 22);

        GlobalVariable.scene.addNode(heart);

        return heart;
    }

    // 血量
    heartUpdate(){
        let list = this.heartList;
        if(list && list.length === 0){
            for (let i = 0; i < this.heart; i++) {
                list.push(this.createHeart(10 + i * 32, 5,true));
            }
        }else {
            const cache_ = game.getResource();
            for (let i = 0; i < list.length; i++) {
                const h = list[i];
                let img;
                if (i < this.heart){
                    img = cache_.getTexture("full-heart.png");
                }else {
                    img = cache_.getTexture("empty-heart.png");
                }
                h.setTexture(img);
            }
        }
    }

}
class AirplaneBattle{
    // 敌方飞机
    enemys = [];

    // 主角飞机
    heroAirplane = null;

    constructor() {
        this.init()
    }

    init(){
        let w = game.getWindow().getWidth();
        let h = game.getWindow().getHeight();

        GlobalVariable.gameOver = false;
        Util.bj({picture: "mainbg.png"});

        this.heroAirplane = this.createHeroAirplane()
        this.createEnemy();

        // 分数
        GlobalVariable.score = 0;
        GlobalVariable.scoreText = Util.newText({
            x: w - 120,
            y: 10,
            text: "分数:"+GlobalVariable.score+"",
            width:300
        })

        // 更新事件
        GlobalVariable.scoreText.upDate(()=> {
            if (GlobalVariable.gameOver) {
                return;
            }
            // 难度升级
            if(GlobalVariable.score > 5 && this.enemys.length  == 1){
                this.createEnemy();
            }
        })

        game.setKeyCallBack((key,action)=>{
            log(key+"   "+action);
            let type = "";
            if (key == GlobalVariable.KEY_W || key == GlobalVariable.KEY_UP){
                type = "up";
            }
            if (key == GlobalVariable.KEY_S || key == GlobalVariable.KEY_BOTTOM){
                type =  "down";
            }
            if (key == GlobalVariable.KEY_A || key == GlobalVariable.KEY_LEFT){
                type =  "left";
            }
            if (key == GlobalVariable.KEY_D || key == GlobalVariable.KEY_RIGHT){
                type =  "right";
            }
            this.changePos(type);
        });


    }


    changePos(type){
        let airplane = this.heroAirplane;
        if (airplane == null) {
            return;
        }
        let sprite = airplane.getSprite();
        let position = Util.getPosition(sprite);
        switch (type){
            case "up":
                position.y -= 10;
                break;
            case "down":
                position.y += 10;
                break;
            case "left":
                position.x -= 10;
                break;
            case "right":
                position.x += 10;
                break;
        }
        sprite.setPosition(position.x,position.y);
    }

    getHeroAirplane() {
        return this.heroAirplane;
    }

    // 创建飞机
    createHeroAirplane() {
        let w = game.getWindow().getWidth();
        let h = game.getWindow().getHeight();
        let myAirplaneX = (w / 2)-50;
        let myAirplaneY = h - 80;
        return  new Airplane(myAirplaneX,myAirplaneY,64,64,false);
    }

    // 创建敌方飞机
    createEnemy(){
        let w = game.getWindow().getWidth();
        const x = Math.floor(Math.random() * (w - 100)) + 50;
        const enemyAirplane = new Airplane(x,0,64,64,true,this.heroAirplane);
        this.enemys.push(enemyAirplane);
    }

}
// 工具类:生成对象
class Util{

    // 函数功能:创建背景场景,并返回场景和背景节点
    static bj=(options={})=>{

        let w = game.getWindow().getWidth();
        let h = game.getWindow().getHeight();
        let config = {
            x: 0,
            y: 0,
            width: w,
            height: h,
            picture: "bg.jpg",
            ...options
        };

        // 创建场景
        let scene = new Scene();
        GlobalVariable.scene = scene;
        game.pushScene(scene);

        // 添加游戏背景图
        const cache_res = game.getResource();
        let bg = cache_res.getTexture(config.picture);
        const node = new Sprite();
        node.setTexture(bg);
        node.setSize(config.width,config.height);
        node.setPosition(config.x,config.y);
        node.setColor(1,1,1,1);
        scene.addNode(node);

        // 返回场景对象
        return {scene:scene,backgroundNode:node};
    }

    // 函数功能:创建精灵节点,并返回精灵
    static newSprite(options={}){
        let config = {
            x: 0,
            y: 0,
            width: 50,
            height: 30,
            clickCb: undefined,
            texture: "",
            ...options
        };
        if(!GlobalVariable.scene){
            //   log("scene is not exist");
            return;
        }
        const cache_ = game.getResource();
        let bg = cache_.getTexture(config.texture);
        let sprite = new Sprite();
        sprite.setTexture(bg);
        sprite.setSize(config.width, config.height);
        sprite.setPosition(config.x, config.y);
        GlobalVariable.scene.addNode(sprite);

        sprite.click(()=>{
            if (config.clickCb !== undefined && config.clickCb instanceof Function){
                config.clickCb();
            }
        });
        return sprite;
    }


    // 函数功能:创建文本节点,并返回文本对象
    static newText(options){

        // 如果场景不存在,返回
        if(!GlobalVariable.scene){
            return;
        }

        // 文本节点参数
        let config = {
            x: 0,
            y: 0,
            width: 50,
            height: 30,
            text: "",
            fontSize: 20,
            textColor: [1,0,0],
            ...options
        };

        // 打印日志,调试用
        log(JSON.stringify(config));

        // 设置lab标签
        const lab = new Label();
        lab.setPosition(config.x, config.y);
        lab.setSize(config.width, config.height);
        lab.setFont("font/st.ttf", config.fontSize);
        lab.setText(config.text);

        // 设置颜色
        if(config.textColor !== undefined && config.textColor.length === 3){
            let configColor = config.textColor;
            lab.setTextColor(configColor[0],configColor[1],configColor[2],1);
        }else {
            lab.setTextColor(1,0,0,1);
        }
        lab.setColor(1,1,1,0);
        GlobalVariable.scene.addNode(lab);
        return lab;
    }

    // 函数功能:获取节点位置和大小
    static getPosition(node){
        if (!node){
            return;
        }
        let x = node.getPosition().x;
        let y = node.getPosition().y;

        let width = node.getSize().x;
        let height = node.getSize().y;

        return {x:x, y:y, width:width, height:height};
    }

}
class Physics {
    // 碰撞检测算法: 矩形 vs 矩形
    static rectRect(rect1, rect2,rate1 = 1,rate2=1) {
        if(rate1!==1){
            rect1.x += (rate1-1)*rect1.width;
            rect1.y += (rate1-1)*rect1.height;
            rect1.width *= rate1;
            rect1.height *= rate1;
        }
        if(rate2!==1){
            rect2.x += (rate2-1)*rect2.width;
            rect2.y += (rate2-1)*rect2.height;
            rect2.width *= rate2;
            rect2.height *= rate2;
        }
        let b = rect1.x < rect2.x + rect2.width
            && rect1.x + rect1.width > rect2.x
            && rect1.y < rect2.y + rect2.height
            && rect1.y + rect1.height > rect2.y;
        return b;
    }

    // 碰撞检测算法: 圆形 vs 圆形
    static circleCircle(circle1, circle2) {
        const dx = circle1.x - circle2.x;
        const dy = circle1.y - circle2.y;
        const distance = Math.sqrt(dx * dx + dy * dy);
        return distance < circle1.radius + circle2.radius;
    }

    // 碰撞检测算法: 矩形 vs 圆形
    static rectCircle(rect, circle) {
        let testX = circle.x;
        let testY = circle.y;
        if (circle.x < rect.x) testX = rect.x; else if (circle.x > rect.x + rect.width) testX = rect.x + rect.width;
        if (circle.y < rect.y) testY = rect.y; else if (circle.y > rect.y + rect.height) testY = rect.y + rect.height;
        const distX = circle.x - testX;
        const distY = circle.y - testY;
        const distance = Math.sqrt(distX * distX + distY * distY);
        return distance <= circle.radius;
    }

}
// 全局类,全局变量和参数

class GlobalVariable{

    // 键盘键值
    static KEY_W = 87;
    static KEY_S = 83;
    static KEY_A = 65;
    static KEY_D = 68;
    static KEY_BOTTOM = 40;
    static KEY_UP = 38;
    static KEY_LEFT = 37;
    static KEY_RIGHT = 39;

    static gameOver;
    // 场景
    static scene;
    // 分数
    static score = 0;
    // 分数游戏对象
    static scoreText;
}

代码功能:

  1. main.js:游戏初始化,设置背景音乐等
  2. util.js:公共类,设置文字等
  3. Airplane.js:飞机子弹碰撞
  4. AirplaneBattle.js:飞机创建
  5. GlobalVariable.js:键盘码值
  6. physics.js:碰撞算法

开维游戏引擎代码简单,函数精简,尽量用极少的js脚本实现游戏功能。代码跨平台通用,一次编写,多端运行,可以直接导出生成exe或者html目录直接运行:

开维游戏引擎下载: www.ikaiwei.com/download/ga...

飞机大战页面演示: www.ikaiwei.com/gamejs/exam...

源码下载: github.com/ctrljshaha/...

gamejs.ikaiwei.com/#/Market

开发文档: www.ikaiwei.com/gamejs/api/...

相关推荐
SmalBox11 小时前
【节点】[LinearBlendSkinning节点]原理解析与实际应用
unity3d·游戏开发·图形学
SmalBox1 天前
【节点】[ComputeDeformation节点]原理解析与实际应用
unity3d·游戏开发·图形学
SmalBox2 天前
【节点】[TransformationMatrix节点]原理解析与实际应用
unity3d·游戏开发·图形学
SmalBox3 天前
【节点】[Matrix4x4节点]原理解析与实际应用
unity3d·游戏开发·图形学
SmalBox4 天前
【节点】[Matrix3x3节点]原理解析与实际应用
unity3d·游戏开发·图形学
SmalBox5 天前
【节点】[Matrix2x2节点]原理解析与实际应用
unity3d·游戏开发·图形学
SmalBox6 天前
【节点】[ShadowMask节点]原理解析与实际应用
unity3d·游戏开发·图形学
avi91116 天前
UE4-UE5虚幻引擎-前置学习三,优化,基础CPP
ue5·ue4·游戏开发·虚幻·游戏优化·游戏代码
SmalBox7 天前
【节点】[ReflectionProbe节点]原理解析与实际应用
unity3d·游戏开发·图形学