HTML5 Canvas 射击类小游戏 平滑的移动 思路

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wf824284257/article/details/78991625

这篇博客主要讲了如何处理HTML5 Canvas 游戏中的角色移动问题。

笔者这几天做了一个 HTML5 Canvas 的射击类小游戏,嗯,名字叫做《DroppingBalls》,大概就是自己控制一个坦克在界面的最下面左右移动,然后上面会有怪向下移动,我们必须赶在怪完全掉下来之前将它击毙,否则我们会掉血。怪会越来越多越来越强,我们最后一定会死掉的,这没关系,因为我们的目前是在死之前拿更多的分数。

这个游戏目前还没有完全做完,也仅仅是练习作。游戏挂在笔者的个人网站上了,大家可以去看一下。

该博客中的代码仅为了表明思路,并不保证可以直接运行。若需要可以直接运行的代码,可以到该游戏页面 f12 查看:

http://dawufan.cn/HtmlGame/Game001

好了,今天的话题就是 如何实现这样的射击类小游戏的第一步,完美的控制己方坦克移动。

##主要技术

JS + Canvas

代码难度低,只要自己写过一遍就感觉非常非常简单了。代码仅是工具,但将许多代码组合在一起并让它们高效的工作,这就是一门艺术了。

##游戏分析

射击类游戏,比如坦克大战、打飞机等等,基本元素几乎相同。我们从最简单的入手来看一下。我们就以坦克举例。

首先得有玩家的坦克,然后玩家会通过键盘来控制自己的坦克移动,通过键盘控制射击,射击后会发出子弹,子弹在射出后会自动飞行,直到击中敌方坦克或者飞出地图后消失。敌方坦克的移动路线可以是随机的,也可以是设定好的。在《DroppingBalls》中,己方坦克、敌方坦克、子弹 全都是一个球Ball, 这样做的目的是因为相比于正方形等,两个圆形之间的距离是最好判断的,这将使【子弹是否能够击毁坦克】的判断非常容易。

游戏中,玩家通过wasd来控制移动,j来射击。

HTML页面中的Canvas标签id=CanvasGame, 宽=600,高=600

##分步骤实现 第1步

我们要画一个球(这里的球和圆是一个意思),表示己方坦克。

这个可以在w3school上自己看一下,这个可以做到之后就可以进行第二步了。

w3school的Canvas教程链接:

http://www.w3school.com.cn/html5/html_5_canvas.asp

##分步骤实现 第2步

实现己方坦克的粗糙的移动。这里我们只说向右移动,其他方向的类似。

经过第1步的学习,球可以画出来了,但是怎样实现球的移动呢?这里就涉及到动画的原理了,即球是不会动的,球的移动只是看起来在移动而已,实际上,我们要做的是不断的清空游戏画面,之后,在球的原本位置靠右一点点的位置画一个球,当我们每秒重复几十次这样的清空重画后,在视觉上,球就动起来了。

总结一下我们要做的事情: 捕获键盘按键‘D’时,球的x坐标增大,清空游戏画面,重新画出这个球。

这时为了方便各种变量的操作,我们本着面向对象的原则将球封装一下:

// Base class for myTank 、 enemy 、bullet .etc

function Ball(paraX,paraY,paraR,paraSpeed,paraColor,paraMap) {

    var ball = {
        x:paraX,
        y: paraY,
        r:paraR,
        speed:paraSpeed,
        color: paraColor,
        map: paraMap

    }

    return ball;

}

这里的speed、map可以先不用管。

通过这样的封装,我们很容易实现球的移动:

// Base class for myTank 、 enemy 、bullet .etc

function Ball(paraX,paraY,paraR,paraSpeed,paraColor,paraMap) {

    var ball = {
        x:paraX,
        y: paraY,
        r:paraR,
        speed:paraSpeed,
        color: paraColor,
        map: paraMap,
    }
    ball.move=function(){
        this.x+=this.speed;
    };

    return ball;

}

通过在ball这个对象里面增加move方法,我们想向右移动该球时,只需要调用ball.move()就可以了。

接下来我们要做的就是捕获键盘按键‘D’时,调用ball.move()

var myTank=new Ball(200,200,20,3,"red",map);//map先不用管
document.onkeydown = function (event) {
    var e = event || window.event || arguments.callee.caller.arguments[0];
    if (!e) return;
    console.log(String.fromCharCode(e.keyCode) + " down");
    switch (e.keyCode) {
        case "d".charCodeAt(0):
        case "D".charCodeAt(0):
            myTank.move();
            break;


    } // end switch

};// end onkeydown

这样的话就实现了myTank的向右移动。但此时的移动仅仅是在内存完成的,我们需要将它画到Canvas上才能看到。

    function clear() {
        var ctx =document.getElementById('CanvasGame').getContext('2d');
        ctx.clearRect(0, 0, 600, 600);
    }
    function paintMyTank(){
        var ctx = document.getElementById('CanvasGame').getContext('2d');
        ctx.beginPath();
        ctx.fillStyle = myTank.color;
        ctx.arc(myTank.x,myTank.y, myTank.r, 0, 2*Math.PI);
        ctx.fill();
    }

有了这两个方法后,我们就可以在捕获键盘按键‘D’后调用了。局部代码如下:

switch (e.keyCode) {
        case "d".charCodeAt(0):
        case "D".charCodeAt(0):
            myTank.move();
            clear();
            paintMyTank();
            break;


    } // end switch

这样基本实现了myTank的向右移动了。

补全一下ball.move()及document.onkeydown()后,即可实现4个方向的移动。

##分步骤实现 第3步

整理代码,实现myTank的完美移动。

经过第二步可以实现非常粗糙的移动,即捕获方向按键后,更新myTank的坐标值,清空Canvas,重新画出myTank。 但很遗憾,真正的代码并不是这样写的。上面的代码仅仅是为了便于理解。第2步实现后,移动非常粗糙,且不能斜方向移动。而且代码难以整合。

所在这里我们要整理一下代码,同时解决这个移动的问题。

代码的结构上,我们希望每个变量、每个方法都有自己的域,让代码最大可能的受控,说白了,就是面向对象的方法,一层又一层的封装。这里我们将游戏的所有变量及方法封装到game对象里面。

function Game() {
    var game={};
    game.controlKeys=new ControlKeys(); // 控制按钮, 构造方法在下面
    game.models = {
        myTank: new MyTank(), //类似的方法实现myTank的构造
    };
    game.func = {}; //后面会补充
    game.event={};  //后面会补充
}

myTank的移动处理上,我们不希望每个键盘事件时刷新一次画面。而且js无法处理两个方向键同时按下的时候的斜方向移动,所以我们要换一种方式。我们设置一些标志变量,并将他们封装到controlKeys对象中:

//game.controlKeys
function ControlKeys() {
    var controlKeys = {
        //方向
        w: false,
        a: false,
        s: false,
        d: false,
    }
    return controlKeys;
}

键盘事件需要捕获onkeydown和onkeyup, down时,controlKeys对应的标志变为true;up时变为false;

游戏时钟设定为30毫秒, 即每30毫秒,我们根据controlKeys的标志值,若w为true则myTank.move(“w”),即向上移动;若d为true则myTank.move(“d”),即向右移动。这些判断之间相互不干涉,这样就实现了斜方向的移动。

game.event = {
    beginControl: function () {
        document.onkeydown = function (event) {
            var e = event || window.event || arguments.callee.caller.arguments[0];
            if (!e) return;
            console.log(String.fromCharCode(e.keyCode) + " down");
            switch (e.keyCode) {

                case "w".charCodeAt(0):
                case "W".charCodeAt(0):
                    game.controlKeys.w = true;
                    break;
                //a s d 类似
            } // end switch

        };// end onkeydown

        document.onkeyup = function (event) {
            var e = event || window.event || arguments.callee.caller.arguments[0];
            if (!e) return;
            console.log(String.fromCharCode(e.keyCode) + " up");
            switch (e.keyCode) {

                case "w".charCodeAt(0):
                case "W".charCodeAt(0):
                    game.controlKeys.w = false;
                    break;
                //a s d 类似
            } // end switch
        };//end onkeyup
    }
}

game.func = {
    //游戏时钟; 游戏周期;
    clock: function () {
        //处理myTank移动
        if (game.controlKeys.w) {
            game.models.myTank.move("w");
        }
        if (game.controlKeys.a) {
            game.models.myTank.move("a");
        }
        //s d 类似
        game.painter.paintGame();

    }//end game.func.clock()

}; //end game.func


game.event = {
      //开启游戏时钟,设定为30毫秒执行一次
       start: function () {
           setInterval(game.func.clock, 30);
           game.func.beginControl();
       },
}

这样,即可实现myTank的平滑的、8个方向的完美移动了。

##结语

《DroppingBalls》 游戏代码目前500行左右,虽然不大,但若想详细的在一篇博客讲完还是有些困难的。 这篇博客将myTank的完美移动的思路讲了一下,博客中所给代码上可能存在一些bug,因为这些代码是从游戏源码中抽出来的部分,仅仅为了表达思路,直接copy过去是不能运行的。

游戏链接会给到大家,大家可以直接f12看一下源码,这样更方便快捷:

http://dawufan.cn/HtmlGame/Game001
感谢。

猜你喜欢

转载自blog.csdn.net/wf824284257/article/details/78991625