Fork me on GitHub

JS写小游戏(一):游戏框架

前言

  前一阵发现一个不错的网站,都是一些用html5+css+js写的小游戏,于是打算学习一番,写下这个系列博客主要是为了加深理解,当然也有一些个人感悟,如果英文好可以直接Click Here.

概述

  一般,小游戏都要关注两个问题:刷新和交互。因为游戏一般是动态的,所以需要不断刷新。JavaScript是单线程,如果用C语言写过贪吃蛇之类的小游戏,应该知道,单线程一般是挂起一个时间来达到动态效果。比如C语言的Sleep(),JS的setInterval()等。但是js还有一种更高性能的方法requestAnimationFrame。可以在网上找些资料进行学习,在此不做赘述。另一个就是交互,即用户需要通过鼠标、键盘控制游戏,从编程角度来书就是要添加对应事件的监听器。

  以下,正式开始。

HTML5

  先创建一个canvas画布:

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4 <meta charset="UTF-8">
 5 <title>Rembound.com Example</title>
 6 <script type="text/javascript" src="script.js"></script>
 7 </head>
 8 <body>
 9 <canvas id="viewport" width="640" height="480"></canvas>
10 </body>
11 </html> 

JS

  添加以下基本代码,代码详情可以看注释,最好单步调试加深理解:

//窗口加载完成后调用
window.onload = function() {
    // 获取画布及context(上下文)
    var canvas = document.getElementById("viewport"); 
    var context = canvas.getContext("2d");
 
    // 记录时间帧,这个最好通过单步调试来理解
    var lastframe = 0;
    var fpstime = 0;
    var framecount = 0;
    var fps = 0;
 
    // 初始化游戏,添加鼠标的监听事件
    function init() {
        canvas.addEventListener("mousemove", onMouseMove);
        canvas.addEventListener("mousedown", onMouseDown);
        canvas.addEventListener("mouseup", onMouseUp);
        canvas.addEventListener("mouseout", onMouseOut);
 
        // 进入游戏主循环
        main(0);
    }
 
    // 主循环
    function main(tframe) {
        // 结束时继续调用main函数
        window.requestAnimationFrame(main);
 
        // 更新游戏
        update(tframe);
        render();
    }
 
    // 更新游戏状态,计算已经过去了的时间
    function update(tframe) {
        var dt = (tframe - lastframe) / 1000;
        lastframe = tframe;
 
        //更新帧数计数器
        updateFps(dt);
    }
 
    function updateFps(dt) {
        if (fpstime > 0.25) {
            //计算帧数
            fps = Math.round(framecount / fpstime);
 
            //重置时间
            fpstime = 0;
            framecount = 0;
        }
 
        //增加帧时间、帧数
        fpstime += dt;
        framecount++;
    }
 
    // 渲染(更新画布)
    function render() {
        drawFrame();
    }
 
    // 
    function drawFrame() {
        // 背景、边界
        context.fillStyle = "#d0d0d0";
        context.fillRect(0, 0, canvas.width, canvas.height);
        context.fillStyle = "#e8eaec";
        context.fillRect(1, 1, canvas.width-2, canvas.height-2);
 
        // 标题头
        context.fillStyle = "#303030";
        context.fillRect(0, 0, canvas.width, 65);
 
        // 标题
        context.fillStyle = "#ffffff";
        context.font = "24px Verdana";
        context.fillText("HTML5 Canvas Basic Framework - Rembound.com", 10, 30);
 
        // 显示帧数
        context.fillStyle = "#ffffff";
        context.font = "12px Verdana";
        context.fillText("Fps: " + fps, 13, 50);
    }
 
    //鼠标监听
    function onMouseMove(e) {}
    function onMouseDown(e) {}
    function onMouseUp(e) {}
    function onMouseOut(e) {}
 
    // 获取鼠标位置
    function getMousePos(canvas, e) {
        var rect = canvas.getBoundingClientRect();
        return {
            x: Math.round((e.clientX - rect.left)/(rect.right - rect.left)*canvas.width),
            y: Math.round((e.clientY - rect.top)/(rect.bottom - rect.top)*canvas.height)
        };
    }
 
    // 游戏入口
    init();
};

  效果:

  

添加游戏元素

  以上就是一个通用的游戏框架了,虽然它在不断刷新,但是没什么直观感受,以下建立一个简单游戏来感受一下:

 ......  
    var framecount = 0;
    var fps = 0;
 
    // 游戏平面
    var level = {
        x: 1,
        y: 65,
        width: canvas.width - 2,
        height: canvas.height - 66
    };
    
    // 小方块
    var square = {
        x: 0,
        y: 0,
        width: 0,
        height: 0,
        xdir: 0,
        ydir: 0,
        speed: 0
    }
    
    // 分数
    var score = 0;

    // 初始化游戏,添加鼠标的监听事件
    function init() {
....

  在init()函数中添加:

....
        canvas.addEventListener("mouseout", onMouseOut);
         // 初始化方块
        square.width = 100;
        square.height = 100;
        square.x = level.x + (level.width - square.width) / 2;
        square.y = level.y + (level.height - square.height) / 2;
        square.xdir = 1;
        square.ydir = 1;
        square.speed = 200;
        
        // 初始化分数
        score = 0;
    
        // 进入游戏主循环
        main(0);
....

  在update()函数中更新方块

.... 
       //更新帧数计数器
        updateFps(dt);

       // 基于时间移动方块
        square.x += dt * square.speed * square.xdir;
        square.y += dt * square.speed * square.ydir;
        
        // 处理碰撞
        if (square.x <= level.x) {
            // Left edge
            square.xdir = 1;
            square.x = level.x;
        } else if (square.x + square.width >= level.x + level.width) {
            // Right edge
            square.xdir = -1;
            square.x = level.x + level.width - square.width;
        }

        if (square.y <= level.y) {
            // Top edge
            square.ydir = 1;
            square.y = level.y;
        } else if (square.y + square.height >= level.y + level.height) {
            // Bottom edge
            square.ydir = -1;
            square.y = level.y + level.height - square.height;
        }    
...

  render()函数中还要渲染方块

....
        // 绘制方块
        context.fillStyle = "#ff8080";
        context.fillRect(square.x, square.y, square.width, square.height);
        
        // 绘制内部
        context.fillStyle = "#ffffff";
        context.font = "38px Verdana";
        var textdim = context.measureText(score);
        context.fillText(score, square.x+(square.width-textdim.width)/2, square.y+65);
...

  添加鼠标事件

    function onMouseDown(e) {
        // 获取鼠标位置
        var pos = getMousePos(canvas, e);
        
        // 检查是否碰到了方块
        if (pos.x >= square.x && pos.x < square.x + square.width &&
            pos.y >= square.y && pos.y < square.y + square.height) {
            
            // 增加分数
            score += 1;
            
            // 增加速度
            square.speed *= 1.1;
            
            // 随机给一个新的位置
            square.x = Math.floor(Math.random()*(level.x+level.width-square.width));
            square.y = Math.floor(Math.random()*(level.y+level.height-square.height));
            
            // 随机方向
            square.xdir = Math.floor(Math.random() * 2) * 2 - 1;
            square.ydir = Math.floor(Math.random() * 2) * 2 - 1;
        }
    }

效果

  

 

  完整源代码:Click Here

  注:这只是众多小游戏合集中的一个,今后会继续添加。

 

posted @ 2016-11-15 23:13  赵裕(vimerzhao)  阅读(12000)  评论(0编辑  收藏  举报