• html5游戏开发-零基础开发《圣诞老人送礼物》小游戏


    开言:

    以前lufy前辈写过叫“ HTML5游戏开发-零基础开发RPG游戏”的系列文章,在那里面我学习了他的引擎以及了解了游戏脚本。自从看了那几篇文章,我便对游戏开发有了基本的认识。今天我也以零基础为视点,为大家讲述如何开发一款简单的游戏。希望大家看了这篇文章,能使你对理解游戏开发有帮助。

    你可以先测试一下游戏:

    http://lufylegend.com/lufylegend_developers/yorhom_Christmas/index.html

    1,如何进行游戏开发

     

    1.1游戏开发思想

    本文依然要运用OOP思想(Object Oriented Programming,面向对象编程),毕竟它很重要,很方便。

    首先,为了让大家了解游戏开发的一些思想,我不妨说一下我对游戏开发的理解:

    在游戏开发中,不是需要一个角色就要去立刻手动建设一个角色的。倘若是一个不断变化的游戏,用手慢慢改,那还不得累死。因此我们就需要用到循环或者时间轴事件。具体用循环还是时间轴事件,要看你是如何设计游戏。循环不要说,我们主要说说时间轴事件。

    时间轴事件相当于一个循环,在它内部要执行的内容会不断地执行,也就是说是一个死循环。既然是死循环,那要改变游戏里的内容就会变得相当简单。只需要在外部更改界面上的属性,然后在时间轴事件内不断判断属性,执行变化即可。

    1.2类的使用

    另外还需要注意的是,游戏开发需要用到类。JavaScript定义类很简单,只用定义一个函数就行,属性用this来加。如下:

    function people(){
        this.name = "yorhom";
        this.age = "13";
    }

    类的作用很大,加入有界面上有3个角色,我们只用循环3次,每循环一次就用局部变量来实例化一个角色类即可。

    类操作里面比较重要一项的就是继承。比如说在lufylegend.js中,如果你的类继承自LSprite类,那就拥有show方法,到时候你在使用你的类时,只用更改属性,不用手动重绘就可以更新界面。除了方便以外,它还可以替你免去一些多余的代码。假如父类有个加鼠标事件的方法,而子类想要也,那就不需要在重新写一遍了,直接继承就行了。

    2,开始游戏开发

     

    2.1开发准备

    由于本次开发用到了lufylegend.js开源引擎,所以首先需要下载它。

    下载地址:http://lufylegend.com/lufylegend

    API 文档:http://lufylegend.com/lufylegend/api

    另外,由于是html5游戏,所以你需要一个支持html5的浏览器。当然,如果你已经有了这样的浏览器,那就直接开始吧。

    2.2开始编程

    首先来看一下Main.js。首先定义一些层变量:

    var backLayer,
    loadingLayer,
    logoLayer,
    sceneLayer,
    snowLayer,
    stageLayer,
    charaLayer,
    overLayer,
    gameoverLayer;

    另外一些闲杂变量:

    var point = 0,time = 1000*30;
    var showTime;
    var plopSound,backSound;
    var playerName;
    var pointText,timeText,resultText;

    还有几个散乱的家伙藏在角落里,也被我给找到贴出来:

    var snowingSpeed = 0;
    var snowingSpeedIndex = 20;
    var snowChildList = [];
    var canSnowing = true;
    var showChara = false;

    接下来初始化引擎

    init(50,"mylegend",600,400,main);
    LSystem.screen(LStage.FULL_SCREEN);

    init是引擎初始化函数,用法如下:

    ------------------------------------------------------------------------------

    init( 
        speed, 
        divid, 
        width, 
        height, 
        completeFunc, 
        type 
    )

     
     
     
     

    ■作用:

    库件初始化

     

    ■参数:

    speed:游戏速度设定

    divid:传入一个div的id,库件进行初始化的时候,会自动将canvas加入到此div内部

    游戏界面宽

    height:游戏界面高

    completeFunc:游戏初始化后,调用此函数

    type:当为null时,会先进行页面的onload操作,如果你的init函数调用是在onload之后,那么需要将此参数设为LEvent.INIT

    ------------------------------------------------------------------------------

    LSystem.screen是一调整屏幕大小的方法。如果参数写LStage.FULL_SCREEN说明调整为全屏。

    接下来是加载图片:

    var imglist = [];
    var imgData = [
        {path:"./js/gameLogo.js",type:"js"},
        {path:"./js/Charactor.js",type:"js"},
        {path:"./js/Stage.js",type:"js"},
        {name:"player",path:"./images/airplane.png"},
        {name:"logoback",path:"./images/logoback.jpg"},
        {name:"background",path:"./images/background.png"},
        {name:"house",path:"./images/house.png"},
        {name:"costume0",path:"./images/costume0.png"},
        {name:"costume1",path:"./images/costume1.png"},
        {name:"costume2",path:"./images/costume2.png"},
        {name:"costume3",path:"./images/costume3.png"},
        {name:"costume4",path:"./images/costume4.png"},
        {name:"costume5",path:"./images/costume5.png"},
        {name:"costume6",path:"./images/costume6.png"},
        {name:"costume7",path:"./images/costume7.png"}
    ];

    上面是加载图片列表,以下是加载时用的代码:

    //开始加载图片
    LLoadManage.load(
        imgData,
        function(progress){
            //绘制进度条
            loadingLayer.setProgress(progress);
        },
        function(result){
            imglist = result;
            removeChild(loadingLayer);
            loadingLayer = null;
            //初始化游戏
            gameInit();
            //加入开始界面
            addLogo();
        }
    );

    将以上代码写到main函数中,就可以实现加载图片了。LLoadManage类是lufylegend中一个加载图片的类,用它可以方便地加载图片。用法如下:

    ------------------------------------------------------------------------------

    load($list,$onupdate,$oncomplete)

     

    ■作用:

    读取文件组

     

    ■参数:

    $list:文件数组

    $onupdate:读取中调用函数,一般用来显示游戏进度

    $oncomplete:全部文件读取完成后调用函数

     

    ■详细说明:

    这个函数可以接收一个数组,然后加载数组里的所有文件

    ------------------------------------------------------------------------------

    这样做的好处是可以在使用图片时更方便,只用写path对应的name上去就行了。

    整个main函数代码如下:

    function main(){
        //初始化加载层
        loadingLayer = new LoadingSample3();
        addChild(loadingLayer);
        //开始加载图片
        LLoadManage.load(
            imgData,
            function(progress){
                //绘制进度条
                loadingLayer.setProgress(progress);
            },
            function(result){
                imglist = result;
                removeChild(loadingLayer);
                loadingLayer = null;
                //初始化游戏
                gameInit();
                //加入开始界面
                addLogo();
            }
        );
        //加载声效音乐
        plopSound = new LSound();
        var plopUrl = "./sounds/plop.mp3";
        plopSound.load(plopUrl);
        //加载背景音乐
        backSound = new LSound();
        var backsoundUrl = "./sounds/back_music.mp3";
        backSound.load(backsoundUrl);
    }

    在上面的代码中,我用LSound类加了背景音乐,这样一来顺便试一下新功能。看看gameInit里代码:

    function gameInit(){
        //初始化层
        initLayer();
        //加入时间轴事件
        backLayer.addEventListener(LEvent.ENTER_FRAME,onframe);
        //加入鼠标事件
        backLayer.addEventListener(LMouseEvent.MOUSE_DOWN,onmousedown);
    }

    initLayer和onmousedown中的代码:

    function onmousedown(event){
        //播放声效音乐
        plopSound.play();
        if(showChara == true && stageLayer.childList.length < 6){
            //加入障碍物
            addStage();
        }
    }
    function initLayer(){
        //加入底板层
        backLayer = new LSprite();
        addChild(backLayer);
        //加入图标层
        logoLayer = new LSprite();
        backLayer.addChild(logoLayer);
        //加入雪花层
        snowLayer = new LSprite();
        backLayer.addChild(snowLayer);
        //加入场景层
        sceneLayer = new LSprite();
        backLayer.addChild(sceneLayer);
        //加入礼物层
        stageLayer = new LSprite();
        backLayer.addChild(stageLayer);
        //加入人物层
        charaLayer = new LSprite();
        backLayer.addChild(charaLayer);
        //加入输出层
        overLayer = new LSprite();
        backLayer.addChild(overLayer);
        //加入游戏结束层
        gameoverLayer = new LSprite();
        backLayer.addChild(gameoverLayer);
    }

    以上代码都添加了注释,很容易看懂。有几个引擎中的类的用法提一下,LSprite用法:

    ------------------------------------------------------------------------------

    LSprite()

     
     

    ■作用:

    LSprite 类是基本显示列表构造块,一个可显示图形并且也可包含子项的显示列表节点。

     

    ■可用属性:

    type:类型

    x:坐标x

    y:坐标y

    scaleX:X坐标方向上的缩放比例

    scaleY:Y坐标方向上的缩放比例

    alpha:透明度

    rotate:旋转角度

    visible:是否可见,当设为false的时候,该LBitmap对象不可视,且内部所有处理都将停止

    childList:该对象的所有子项

    graphics:指定属于此 LSprite 的 LGraphics 对象,在此 LSprite 中可执行矢量绘图命令。

    box2dBody:结合box2dweb后,创建的body2d

    mask:遮罩

    filters:光晕效果,具体可参照LDropShadowFilter类的介绍

    ------------------------------------------------------------------------------

    addEventListener的用法:

    ------------------------------------------------------------------------------

    addEventListener(type,listener)

     
     

    ■作用:

    注册事件侦听器对象,以使侦听器能够接收事件通知。

     

    ■参数:

    type:事件的类型。

    listener:处理事件的侦听器函数。

    ------------------------------------------------------------------------------

    在加载函数中,我调用了addLogo,它是用来显示开场界面的。由于游戏本身很简单,所以要加一个很绚丽的开场界面。

    addLogo的代码如下:

    var logoText;
    var startBtn;
    function addLogo(){
        //加入背景
        var bitmapData = new LBitmapData(imglist["logoback"],0,0,1024,768);
        var bitmap = new LBitmap(bitmapData);
        bitmap.scaleX = 0.6;
        bitmap.scaleY = 0.6;
        logoLayer.addChild(bitmap);
        //加入文字
        addLogoText();
    }

    在其中我给背景添上图片,用到了LBitmapData和LBitmap。用法很多,大家可以自己去API里看看。这里就先不多说了。

    addLogoText里的代码:

    function addLogoText(){
        //大标题
        logoText = new LTextField();
        logoText.size = 50;
        logoText.color = "white";
        logoText.font = "HG行書体";
        logoText.text = "Christmas";
        logoText.stroke = true;
        logoText.lineWidth = 2;
        logoText.x = 50;
        logoText.y = 20;
        logoLayer.addChild(logoText);
        //加入滤镜效果
        var titleShadow = new LDropShadowFilter(5,45,"red");
        for(var i=0;i<2;i++){
            logoText.filters = [titleShadow];
            logoLayer.addChild(logoText);
        }
        //开始指示
        logoText = new LTextField();
        logoText.size = 30;
        logoText.color = "white";
        logoText.font = "HG行書体";
        logoText.text = "Tap to Start Game";
        logoText.x = 150;
        logoText.y = 190;
        logoLayer.addChild(logoText);
        //加入开始游戏事件
        logoLayer.addEventListener(LMouseEvent.MOUSE_UP,startGame);
        //加入滤镜效果
        var shadow = new LDropShadowFilter(5,45,"black",0);
        logoText.filters = [shadow];
    }

    界面运行出来后,得到了一个静态的结果,游戏嘛就得富有动态,于是我做了一个下雪效果。它在onframe函数中,也就是我们说的时间轴事件中:

    if(canSnowing == true){
        //加入雪花
        addSnow();
    }

    接着看addSnow函数:

    function addSnow(){
        snowLayer.graphics.clear();
        var snowx = Math.random()*(LStage.width-10)+10;
        var n = snowChildList.length;
        while(n--){
            var s = snowChildList[n];
            s.y += s.s;
            snowLayer.graphics.drawArc(2,"white",[s.x,s.y,2,0,2*Math.PI],true,"white");
        }
        snowChildList.push({x:snowx,y:0,s:10});
    }

    它实现的方法在上一篇文章中提到过,可以看看,这里就不多讲了:

    如何制作一款HTML5 RPG游戏引擎——第二篇,烟雨+飞雪效果

    http://blog.csdn.net/yorhomwang/article/details/8915020

    运行代码得到一个相当酷的界面,大家可以看一下:


    光有界面也不能叫游戏,接下来就是游戏主体部分。

    我们先前提到过类,现在就来用类实战一下。首先来看charactor人物类:

    function Charactor(data){
        base(this,LSprite,[]);
        //设定x和y坐标
        this.x = 0;
        this.y = 0;
        //设定模式
        this.mode = "right";
        this.speed = 5;
        //加入图片
        this.data = data;
        var list = LGlobal.divideCoordinate(227,158,1,1);
        var bitmapdata = new LBitmapData(imglist[this.data]);
        //加入动画
        this.anima = new LAnimation(this,bitmapdata,list);
        this.anima.setAction(0,1,0,false);
    }

    其中有一个LAnimation方法,它是lufylegend中播放动画的类,使用说明如下:

    ------------------------------------------------------------------------------

    LAnimation(layer,data,list)

     
     

    ■作用:

    实现简单动画的播放,原理是将一张大的图片,按照保存有坐标的二维数组保存的坐标来逐个显示。

     

    ■参数:

    layer:LSprite显示层

    data:LBitmapData对象

    list:一个存有坐标的2维数组

     

    ■详细说明:

    LAnimation类实现简单动画的播放,用于制作人物行走等效果非常方便

     

    ■可用属性:

    layer:动画显示时,LAnimation的父级层

    data:LBitmapData对象

    list:坐标数组。

    ------------------------------------------------------------------------------
    其他的就很容易懂了,Charactor类有个move方法,用于人物移动,如下:

    Charactor.prototype.move = function(){
        //当向右飞行时
        if(this.mode == "right" && this.x < LStage.width-149){
            this.anima.setAction(0,1,0,false);
            this.x += this.speed;
        }else{
            this.mode = "left";
        }
        //当向左飞行时
        if(this.mode == "left" && this.x > 0){
            this.anima.setAction(0,1,0,true);
            this.x -= this.speed;
        }else{
            this.mode = "right";
        }
    }

    这段代码可以使人物移动,将这段代码放在onframe中就可以实现让人物来回移动了。逻辑很简单,大家可以看看。

    接着就是实例化人物了。代码如下:

    function addChara(){
        oldMan = new Charactor("player");
        showChara = true;
        charaLayer.addChild(oldMan);
    }

    接着是onframe中的代码:

    if(showChara == true){
        //使人物动起来
        oldMan.move();
        //改变时间显示
        timeText.text = "Time:" + showTime;
        if(time>0){
            time -= 30000/(30000/50);
        }else{
            playerName = getName();
            gameOver();
        }
    }

    在这里我门判断时间是否为0,如果为0就游戏结束。当然,这是后话,这里只提一下。

    接下来看Stage类,这个很重要,大家一定要认真看哦!

    var stageSpeed = 5;
    function Stage(){
        base(this,LSprite,[]);
        //取出一个整数,使0<=index<=7成立
        var index = Math.floor(Math.random()*7);
        //将index的值取出对应的图片
        var bitmap = new LBitmap(new LBitmapData(imglist["costume"+index]));
        //定义礼物的模式
        this.mode = "";
        this.addChild(bitmap);
    }

    这是Stage类构造器。和Charactor差不多。主要是其方法:

    Stage.prototype.run = function(){
        //让礼物不断下降
        this.y += stageSpeed;
        //判断是否到达边缘
        if(this.y > LStage.height){
            this.mode = "die";
        }
        this.cheackHit();
    }
    Stage.prototype.cheackHit = function(){
        if(this.y > 170 && this.x > 132 - 33 && this.x < 166){
            this.mode = "die";
            point++;
            changeText();
        }else if(this.y > 170 && this.x > 293 - 33 && this.x < 330){
            this.mode = "die";
            point++;
            changeText();
        }else if(this.y > 178 && this.x > 475 - 33 && this.x < 508){
            this.mode = "die";
            point++;
            changeText();
        }
    }

    其实很好理解,在run中,我们让礼物向下移5格,虽然只移5格,但是如果是在onframe中调用,它将不断下降。为了判断礼物是否已经送到家,我用加入cheackHit方法。我们可以用判断坐标的方法来实现。每碰到一次就更改分数,并将mode设置为die,然后在onframe中判断mode,如果mode是"die"就移除这个对象。

    实例化Stage类:

    function addStage(){
        var stage = new Stage();
        if(oldMan.mode == "left"){
            stage.x = oldMan.x + 70;
        }else{
            stage.x = oldMan.x + 30;
        }
        stage.y = 30;
        stageLayer.addChild(stage);
        stageLayer.scaleX = 0.8;
        stageLayer.scaleY = 0.8;
    }

    onframe完整代码:

    function onframe(event){
        showTime = Math.floor(time/1000) + "s";
        if(canSnowing == true){
            //加入雪花
            addSnow();
        }
        if(backSound.playing == false){
            //播放背景音乐
            backSound.play();
        }
        if(showChara == true){
            //使人物动起来
            oldMan.move();
            //改变时间显示
            timeText.text = "Time:" + showTime;
            if(time>0){
                time -= 30000/(30000/50);
            }else{
                playerName = getName();
                gameOver();
            }
        }
        for(var key in stageLayer.childList){
            //使用Stage中run函数,让障碍物动起来
            stageLayer.childList[key].run();
            if(stageLayer.childList[key].mode == "die"){ 
                //移除该成员
                stageLayer.removeChild(stageLayer.childList[key]);
            }
        }
    }

    首先我们新加了一个遍历方法,遍历LSprite成员而获取每对象的状态,每遇见一个mode是die的就将它移除。

    接下来是加入分数以及时间的函数,没有任何逻辑。大家慢慢看就能看懂的。微笑

    function addText(){
        //加入分数文字
        pointText = new LTextField(); 
        pointText.size = 15;
        pointText.x = 10;
        pointText.y = 340;
        pointText.color = "white";
        pointText.text = "Point:" + point;
        pointText.font = "HG行書体";
        overLayer.addChild(pointText);
        //加入时间文字
        timeText = new LTextField(); 
        timeText.size = 15;
        timeText.x = 10;
        timeText.y = LStage.height - 30;
        timeText.color = "white";
        timeText.text = "Time:" + showTime;
        timeText.font = "HG行書体";
        overLayer.addChild(timeText);
        //加入滤镜
        var shadow = new LDropShadowFilter(0,45,"white",0);
        overLayer.filters = [shadow];
    }
    function changeText(){
        pointText.text = "Point:" + point;
    }

    以下是游戏结束调用的函数,同样是很简单:

    function gameOver(){
        backLayer.die();
        //绘制成绩板
        gameoverLayer.graphics.drawRect(2,"dimgray",[0,0,400,300],true,"lightgray");
        gameoverLayer.x = 100;
        gameoverLayer.y = 50;
        gameoverLayer.scaleX = 0.5,
        gameoverLayer.scaleY = 0.5,
        gameoverLayer.alpha = 0.5,
        gameoverLayer.rotate = 50;
        var shadow = new LDropShadowFilter(5,45,"black",0);
        gameoverLayer.filters = [shadow];
        //通过缓动显示成绩板
        LTweenLite.to(gameoverLayer,1,{
            alpha:0.7,
            scaleX:1,
            scaleY:1,
            rotate:0,
            ease:Back.easeInOut,
            onComplete:resultFont
        });
    }
    function resultFont(){
        var resultArr = ["GAME OVER","Tap to Restart Game","分数:"+point,"评价:"+playerName];
        for(var i=0;i<resultArr.length;i++){
            //公有属性
            resultText = new LTextField();
            resultText.weight = "bold";
            resultText.text = resultArr[i];
            //私有有属性
            if(i==0){
                resultText.size = 30;
                  resultText.color = "white";
                resultText.font = "HG行書体";
                resultText.x = 70;
                resultText.y = 20;
            }else if(i==1){
                resultText.size = 15;
                  resultText.color = "white";
                resultText.font = "HG行書体";
                resultText.x = 105;
                resultText.y = 60;
            }else{
                resultText.size = 20;
                  resultText.color = "white";
                resultText.font = "HG行書体";
                resultText.x = 35;
                resultText.y = 100 + (i-1)*32;    
            }
            gameoverLayer.addChild(resultText);
        }
        //加入鼠标事件
        backLayer.addEventListener(LMouseEvent.MOUSE_DOWN,function(){
            //变量清空
            point = 0;
            time = 1000*30;
            showChara = false;
            //清空全局
            backLayer.removeAllChild();
            removeChild(backLayer);
            //游戏重开
            gameInit();
            startGame()
        });
    }

    重开游戏的函数:

    function startGame(){
        //清空画布
        logoLayer.die();
        logoLayer.removeAllChild();
        canSnowing = false;
        //加入背景
        var backBitmapdata = new LBitmapData(imglist["background"],0,0,480,360);
        var backBitmap = new LBitmap(backBitmapdata);
        backBitmap.scaleX = 1.4;
        backBitmap.scaleY = 1.4;
        sceneLayer.addChild(backBitmap);
        //加入房屋
        var houseBitmapdata = new LBitmapData(imglist["house"],0,0,480,228);
        var houseBitmap = new LBitmap(houseBitmapdata);
        houseBitmap.scaleX = 1.4;
        houseBitmap.y = 200;
        sceneLayer.addChild(houseBitmap);
        //加入人物
        addChara();
        //加入文字
        addText();
    }

    好了,运行一下代码:

    哈哈~~,还不错吧。

    3,源代码下载

    本次开发就到这里,想了解详细代码的朋友可以看看。

    下载地址:http://files.cnblogs.com/yorhom/Christmas.rar


    谢谢大家阅读本文。支持就是最大的鼓励。

  • 相关阅读:
    【转载】搞懂wince directshow Camera驱动不得不看的一篇文章.Initialization Sequence for Camera Drivers
    REAL210/S5PV210开发板价格表
    【原创】如何找回source insight context window?(作者:gooogleman)
    【网站】UCenter 与 DIscuz 通信失败的解决办法
    深入理解C语言指针的奥秘4
    Camera OV9650 VGA 模式输出寄存器配置表
    【转载】WinCE绝对好资料
    【震惊语录】至于你信不信,我反正信了。
    【求助】为升级gooogleman嵌入式联盟网站www.gooogleman.com做准备
    【原创】如何在wince5.0 中支持SQLCE3.5 CN——内含解决办法(作者:gooogleman)
  • 原文地址:https://www.cnblogs.com/jiangxiaobo/p/6004975.html
Copyright © 2020-2023  润新知