开维游戏引擎实例:贪吃蛇

0 阅读3分钟

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

下载源码,打开gmp工程后,运行即可出现界面:

image.png

代码如下:

// 贪吃蛇游戏

// 初始化游戏
game.initSize(800,800) // 设置分辨率
game.setFPS(10); // 设置帧率

// 设置窗口log和标题
var texture = game.getResource().getTexture("logo.png"); // 另一种获取纹理数据对象
var window = game.getWindow(); //获取游戏窗口
window.setIcon(texture); // 设置窗口图标
window.setTitle("贪吃蛇小游戏");

// 申请游戏类
new Snake();

// 设置键盘回调函数
game.setKeyCallBack((key,action)=>{
    if ((key == Snake.KEY_W || key == Snake.KEY_UP) && Snake.type != "down"){
        Snake.type = "up";
    }
    else if ((key == Snake.KEY_S || key == Snake.KEY_BOTTOM) && Snake.type != "up"){
        Snake.type =  "down";
    }
    else if ((key == Snake.KEY_A || key == Snake.KEY_LEFT) && Snake.type != "right"){
        Snake.type =  "left";
    }
    else if ((key == Snake.KEY_D || key == Snake.KEY_RIGHT) && Snake.type != "left"){
        Snake.type =  "right";
    }
    //log("key "+key+" action "+action+" type "+Snake.type);
});

//  游戏运行
game.run();

class Snake{
    // 移动距离
    static stepSize = 34;
    static updateTime = new Date().getTime();
    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 refreshInterval=200;
    // 默认移动方向
    static type = "right";
    static gameOver = false;
    static score = 0;
    static score_text;
    static snake_ = [];
    static jp_button;

    static cache_ = game.getResource();
    static scene;
    constructor(){
        Snake.init();
    }

    // 函数功能:初始化界面
    static init(){
        this.scene = Util.bj();

        this.createSnakeButton(100,100)
        this.createJp(200,200)

        let w = game.getWindow().getWidth();
        this.score_text = Util.newText(w-50,20,""+this.score,30,20);

        // 重新开始按钮
        Util.newSprite({
            x: 10,
            y: 5,
            width: 80,
            height: 40,
            texture: 'zaicitiaozhan.png',
            clickCb: ()=>{
                Snake.restart();
            }
        })


        this.scene.upDate((time)=>{
            let s;
            if(this.snake_ && !this.gameOver){
                if(this.gameOver){
                    this.gameEndLogic();
                    return;
                }
                const etime = new Date().getTime();
                const st = etime - this.updateTime;
                let number = this.refreshInterval - 10 * this.score <= 100 ? 100 : this.refreshInterval - 10 * this.score;
                if(st >= number){
                    this.updateTime = etime;

                    const oldPositions = [];
                    for(let i = 0; i < this.snake_.length; i++){
                        s = this.snake_[i];
                        if(s){
                            oldPositions.push(Util.getPosition(s));
                        }
                    }

                    this.changePosition(this.snake_[0],this.type);
                    if(this.gameOver){
                        Util.newText(w/2-50,20,"游戏结束",85,30);
                        return;
                    }else{
                        // 替换位置
                        for(let i = 1; i<this.snake_.length && this.snake_.length > 1 ; i++){
                            s = this.snake_[i];
                            if(s){
                                const img = this.cache_.getTexture("leibg.png");
                                const pos = oldPositions[i - 1];
                                s.setTexture(img);
                                s.setPosition(pos.x, pos.y);
                            }
                        }
                    }
                    if(this.score_text){
                        this.score_text.setText(""+this.score);
                    }
                }
            }
        });

    }

    // 函数功能:重新开始
    static restart(){
        this.gameOver = false;
        this.score = 0;
        this.snake_ = [];
        this.init();
    }

    static createSnakeButton(x,y,unshift){
        let button_ = Util.newSprite({
            x,y,
            texture:"lei0.png",
            width:34,
            height: 34,
        });

        if(unshift){
            this.snake_.unshift(button_)
        }else{
            this.snake_.push(button_)
        }
    }


    static createJp(x,y){
        let jp_button = Util.newSprite({
            x,y,
            texture:"phbjp.png",
            width:34,
            height: 34,
        });

        this.jp_button = jp_button;
    }

    static changePosition(node,direction){
        let w = game.getWindow().getWidth();
        let h = game.getWindow().getHeight();
        let position = Util.getPosition(node);
        let x = position.x;
        let y = position.y;
        //log("direction "+direction+" x "+x+" y "+y+ " w "+w +" h "+h);
        if(direction){
            let stepSize1 = this.stepSize;
            if(direction == "left"){
                x = x - stepSize1;
                if(x < -stepSize1){
                    x = w + x;
                }
            }
            if(direction == "right"){
                x = x + stepSize1;
                if(x > w){
                    x = w - x;
                }
            }
            if(direction == "up"){
                y = y - stepSize1;
                if(y < -stepSize1){
                    y = h + y;
                }
            }
            if(direction == "down"){
                y = y + stepSize1;
                if(y > h){
                    y = y - h;
                }
            }
            log(" x "+x+" y "+y);
            let newPosition = {x,y,width:position.width,height:position.height};
            const jpPosition = Util.getPosition(this.jp_button);
            
            // 吃奖牌
            if(Physics.rectRect(jpPosition,newPosition)){
                this.score = this.score + 1;
                let number = 50;
                const jp_x = Math.floor(Math.random() * w + number);
                const jp_y = Math.floor(Math.random() * h + number);
                this.jp_button.setPosition(jp_x > w- number?w-number : jp_x,jp_y>h-number?h-number:jp_y );
                this.createSnakeButton(x,y,true)
            }else{
                for(let i = 1; i < this.snake_.length; i++){
                    const s = this.snake_[i];
                    if(s){
                        // 头与身体碰撞
                        if(Physics.rectRect(Util.getPosition(s),newPosition)){
                            this.gameOver = true;
                            break;
                        }
                    }
                }
                if(!this.gameOver){
                    node.setPosition(x, y);
                }
            }
        }
    }

}
class Util{
    static scene;
    static bj=()=>{
        let scene = new Scene();
        this.scene = scene;
        game.pushScene(scene);

        const cache_ = game.getResource();

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

        let bg = cache_.getTexture("mainbg.png");
        const node = new Sprite();
        node.setTexture(bg);
        node.setSize(w,h);
        node.setPosition(0,0);
        node.setColor(1,1,1,1);
        scene.addNode(node);

        return scene;
    }

    static newSprite(options={}){
        let config = {
            x: 0,
            y: 0,
            width: 50,
            height: 30,
            clickCb: undefined,
            texture: "",
            ...options
        };
        if(!this.scene){
            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);
        this.scene.addNode(sprite);

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

    // 函数功能:设置字体和颜色
    static newText(x,y,text,width=50,height=30){
        if(!this.scene){
            return;
        }

        const lab = new Label();
        lab.setPosition(x, y);
        lab.setSize(width, height);
        lab.setFont("st.ttf", 20);
        lab.setText(text);
        lab.setTextColor(1.0,0.5,0.2,1.0); // 设置字体颜色
        lab.setColor(0,0,0,0);  // 标签背景颜色为黑色并透明
        this.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) {
        return rect1.x < rect2.x + rect2.width
            && rect1.x + rect1.width > rect2.x
            && rect1.y < rect2.y + rect2.height
            && rect1.y + rect1.height > rect2.y;
    }

    // 碰撞检测算法: 圆形 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;
    }
}

代码功能:

  1. main.js:游戏初始化,设置背景音乐等
  2. util.js:创建场景,设置文字等公用类
  3. snake.js:贪吃蛇移动代码
  4. physics.js:碰撞算法

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

image.png

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

贪吃蛇小游戏html版页面演示: www.ikaiwei.com/gamejs/exam…

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

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