原文:How to make a simple HTML5 Canvas game

想要快速上手HTML5 Canvas小游戏开发?下面通过一个例子来进行手把手教学。(如果你怀疑我的资历, A Wizard's Lizard这个游戏的半数以上开发是由我完成的)

我们直接来看源码里的game.js,当然你也可以在线体验一下游戏先。

游戏截图

创建画布

// Create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 512;
canvas.height = 480;
document.body.appendChild(canvas);

首先我们需要创建一张画布作为游戏的舞台。这里通过JS代码而不是直接在HTML里写一个<canvas>元素目的是要说明代码创建也是很方便的。有了画布后就可以获得它的上下文来进行绘图了。然后我们还设置了画布大小,最后将其添加到页面上。

准备图片

// 背景图片
var bgReady = false;
var bgImage = new Image();
bgImage.onload = function () {
bgReady = true;
};
bgImage.src = "images/background.png";

游戏嘛少不了图片的,所以我们先加载一些图片先。简便起见,这里仅创建简单的图片对象,而不是专门写一个类或者Helper来做图片加载。bgReady 这个变量用来标识图片是否已经加载完成从而可以放心地使用了,因为如果在图片加载未完成情况下进行绘制是会报错的。

整个游戏中需要用到的三张图片:背景英雄怪物我们都用上面的方法来处理。

游戏对象

// 游戏对象
var hero = {
speed: 256, // 每秒移动的像素
x: 0,
y: 0
};
var monster = {
x: 0,
y: 0
};
var monstersCaught = 0;

现在定义一些对象将在后面用到。我们的英雄有一个speed属性用来控制他每秒移动多少像素。怪物游戏过程中不会移动,所以只有坐标属性就够了。monstersCaught 则用来存储怪物被捉住的次数。

处理用户的输入

// 处理按键
var keysDown = {}; addEventListener("keydown", function (e) {
keysDown[e.keyCode] = true;
}, false); addEventListener("keyup", function (e) {
delete keysDown[e.keyCode];
}, false);

现在开始处理用户的输入(对初次接触游戏开发的前端同学来说,这部分开始可能就需要一些脑力了)。在前端开发中,一般是用户触发了点击事件然后才去执行动画或发起异步请求之类的,但这里我们希望游戏的逻辑能够更加紧凑同时又要及时响应输入。所以我们就把用户的输入先保存下来而不是立即响应。

为此,我们用keysDown这个对象来保存用户按下的键值(keyCode),如果按下的键值在这个对象里,那么我们就做相应处理。

开始一轮游戏

// 当用户抓住一只怪物后开始新一轮游戏
var reset = function () {
hero.x = canvas.width / 2;
hero.y = canvas.height / 2; // 将新的怪物随机放置到界面上
monster.x = 32 + (Math.random() * (canvas.width - 64));
monster.y = 32 + (Math.random() * (canvas.height - 64));
};

reset 方法用于开始新一轮和游戏,在这个方法里我们将英雄放回画布中心同时将怪物放到一个随机的地方。

更新对象

// 更新游戏对象的属性
var update = function (modifier) {
if (38 in keysDown) { // 用户按的是↑
hero.y -= hero.speed * modifier;
}
if (40 in keysDown) { // 用户按的是↓
hero.y += hero.speed * modifier;
}
if (37 in keysDown) { // 用户按的是←
hero.x -= hero.speed * modifier;
}
if (39 in keysDown) { // 用户按的是→
hero.x += hero.speed * modifier;
} // 英雄与怪物碰到了么?
if (
hero.x <= (monster.x + 32)
&& monster.x <= (hero.x + 32)
&& hero.y <= (monster.y + 32)
&& monster.y <= (hero.y + 32)
) {
++monstersCaught;
reset();
}
};

这就是游戏中用于更新画面的update函数,会被规律地重复调用。首先它负责检查用户当前按住的是中方向键,然后将英雄往相应方向移动。

有点费脑力的或许是这个传入的modifier 变量。你可以在main 方法里看到它的来源,但这里还是有必要详细解释一下。它是基于1开始且随时间变化的一个因子。例如1秒过去了,它的值就是1,英雄的速度将会乘以1,也就是每秒移动256像素;如果半秒钟则它的值为0.5,英雄的速度就乘以0.5也就是说这半秒内英雄以正常速度一半的速度移动。理论上说因为这个update 方法被调用的非常快且频繁,所以modifier的值会很小,但有了这一因子后,不管我们的代码跑得快慢,都能够保证英雄的移动速度是恒定的。

现在英雄的移动已经是基于用户的输入了,接下来该检查移动过程中所触发的事件了,也就是英雄与怪物相遇。这就是本游戏的胜利点,monstersCaught +1然后重新开始新一轮。

渲染物体

// 画出所有物体
var render = function () {
if (bgReady) {
ctx.drawImage(bgImage, 0, 0);
} if (heroReady) {
ctx.drawImage(heroImage, hero.x, hero.y);
} if (monsterReady) {
ctx.drawImage(monsterImage, monster.x, monster.y);
} // 计分
ctx.fillStyle = "rgb(250, 250, 250)";
ctx.font = "24px Helvetica";
ctx.textAlign = "left";
ctx.textBaseline = "top";
ctx.fillText("Monsterrs caught: " + monstersCaught, 32, 32);
};

之前的工作都是枯燥的,直到你把所有东西画出来之后。首先当然是把背景图画出来。然后如法炮制将英雄和怪物也画出来。这个过程中的顺序是有讲究的,因为后画的物体会覆盖之前的物体。

这之后我们改变了一下Canvas的绘图上下文的样式并调用fillText 来绘制文字,也就是记分板那一部分。本游戏没有其他复杂的动画效果和打斗场面,绘制部分大功告成!

主循环函数

// 游戏主函数
var main = function () {
var now = Date.now();
var delta = now - then; update(delta / 1000);
render(); then = now; // 立即调用主函数
requestAnimationFrame(main);
};

上面的主函数控制了整个游戏的流程。先是拿到当前的时间用来计算时间差(距离上次主函数被调用时过了多少毫秒)。得到modifier后除以1000(也就是1秒中的毫秒数)再传入update函数。最后调用render 函数并且将本次的时间保存下来。

关于游戏中循环更新画面的讨论可参见「Onslaught! Arena Case Study」。

关于循环的进一步解释

// requestAnimationFrame 的浏览器兼容性处理
var w = window;
requestAnimationFrame = w.requestAnimationFrame || w.webkitRequestAnimationFrame || w.msRequestAnimationFrame || w.mozRequestAnimationFrame;

如果你不是完全理解上面的代码也没关系,我只是觉得拿出来解释一下总是极好的

为了循环地调用main函数,本游戏之前用的是setInterval。但现今已经有了更好的方法那就是requestAnimationFrame。使用新方法就不得不考虑浏览器兼容性。上面的垫片就是出于这样的考虑,它是Paul Irish 博客原版的一个简化版本。

启动游戏!

// 少年,开始游戏吧!
var then = Date.now();
reset();
main();

总算完成了,这是本游戏最后一段代码了。先是设置一个初始的时间变量then用于首先运行main函数使用。然后调用 reset 函数来开始新一轮游戏(如果你还记得的话,这个函数的作用是将英雄放到画面中间同时将怪物放到随机的地方以方便英雄去捉它)。

到此,相信你已经掌握了开发一个简单H5小游戏需要的基本功了。玩玩这个游戏或者下载代码自己研究研究吧

如何开发一个简单的HTML5 Canvas 小游戏的更多相关文章

  1. 一个简单的基于canvas小游戏

    GDOI2016是我的退役战,不知道是题目画风不对,还是我自身的问题. 不过没关系啦,反正已经进过一次队OI生涯就没有什么遗憾的了. 这几天尝试着去做了个所谓的html5小游戏,略显简陋,但还是写个总 ...

  2. HTML5 Canvas小游戏基础:用户交互

    交互是游戏的根本.缺少了用户交互,游戏就不能称之为游戏,只能说是动画或电影.事件是浏览器响应用户交互操作的一种机制. 1.事件和事件执行 事件定义了用户与页面交互时产生的各种操作(主要通过鼠标或热键的 ...

  3. 两个Canvas小游戏

    或许连小游戏都算不上,可以叫做mini游戏. 没有任何框架或者稍微有点深度的东西,所以有js基础的或者要追求炫酷效果的可以直接ctrl+w了. 先贴出两个游戏的试玩地址: 是男人就走30步 是男人就忍 ...

  4. Cocos2d-x-Lua 开发一个简单的游戏(记数字步进白色块状)

    Cocos2d-x-Lua 开发一个简单的游戏(记数字步进白色块状) 本篇博客来给大家介绍怎样使用Lua这门语言来开发一个简单的小游戏-记数字踩白块. 游戏的流程是这种:在界面上生成5个数1~5字并显 ...

  5. 作业1开发一个简单的python计算器

    开发一个简单的python计算器 实现加减乘除及拓号优先级解析 用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568 ...

  6. django学习-11.开发一个简单的醉得意菜单和人均支付金额查询页面

    1.前言 刚好最近跟技术部门的[产品人员+UI人员+测试人员],组成了一桌可以去公司楼下醉得意餐厅吃饭的小team. 所以为了实现这些主要点餐功能: 提高每天中午点餐效率,把点餐时间由20分钟优化为1 ...

  7. 重新想象 Windows 8 Store Apps (64) - 后台任务: 开发一个简单的后台任务

    [源码下载] 重新想象 Windows 8 Store Apps (64) - 后台任务: 开发一个简单的后台任务 作者:webabcd 介绍重新想象 Windows 8 Store Apps 之 后 ...

  8. Python开发一个简单的BBS论坛

    项目:开发一个简单的BBS论坛 需求: 整体参考“抽屉新热榜” + “虎嗅网” 实现不同论坛版块 帖子列表展示 帖子评论数.点赞数展示 在线用户展示 允许登录用户发贴.评论.点赞 允许上传文件 帖子可 ...

  9. [Python3 练习] 007 简单的猜数字小游戏

    题目:简单的猜数字小游戏 (1) 描述 程序随机生成一个数字,玩家用键盘输入所猜数字,在规定次数内猜对为胜. (2) 要求 程序随机生成一个 1 到 100 的自然数 有 7 次机会去猜 机会用尽之前 ...

随机推荐

  1. C# 注册 Windows 热键

    闲扯: 前几日,一个朋友问我如何实现按 F1 键实现粘贴(Ctrl+V)功能,百度了一个方法,发给他,他看不懂(已经是 Boss 的曾经的码农),我就做了个Demo给他参考.今日得空,将 Demo 整 ...

  2. 多线程的通信和同步(Java并发编程的艺术--笔记)

    1. 线程间的通信机制 线程之间通信机制有两种: 共享内存.消息传递.   2. Java并发 Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式执行,通信的过程对于程序员来说是完全透 ...

  3. JavaScript中fn()和return fn()

    看博客时,注意到return的重要性 参考:http://www.cnblogs.com/raoyunxiao/p/5644032.html 看似反常的例子: var i = 0; function ...

  4. CSS3 3D立方体效果-transform也不过如此

    CSS3系列已经学习了一段时间了,第一篇文章写了一些css3的奇技淫巧,原文戳这里,还获得了较多网友的支持,在此谢过各位,你们的支持是我写文章最大的动力^_^. 那么这一篇文章呢,主要是通过一个3D立 ...

  5. Webpack 配置摘要

    open-browser-webpack-plugin 自动打开浏览器 html-webpack-plugin 通过 JS 生成 HTML webpack.optimize.UglifyJsPlugi ...

  6. 深入理解 Java G1 垃圾收集器--转

    原文地址:http://blog.jobbole.com/109170/?utm_source=hao.jobbole.com&utm_medium=relatedArticle 本文首先简单 ...

  7. Redis百亿级Key存储方案(转)

    1 需求背景 该应用场景为DMP缓存存储需求,DMP需要管理非常多的第三方id数据,其中包括各媒体cookie与自身cookie(以下统称supperid)的mapping关系,还包括了supperi ...

  8. 高效而稳定的企业级.NET Office 组件Spire(.NET组件介绍之二)

    在项目开发中,尤其是企业的业务系统中,对文档的操作是非常多的,有时几乎给人一种错觉的是”这个系统似乎就是专门操作文档的“.毕竟现在的很多办公中大都是在PC端操作文档等软件,在这些庞大而繁重的业务中,单 ...

  9. 全网独家MongoDB Certified DBA Associate考试认证视频

    该视频意在让所有学员一次通过考试,避免重复考试而承担的巨额考试费用! 目前MongDB发展迅猛,有赶超mysql,和oracle看齐的苗头.在这个时候MongoDB也适时的推出了官方的认证考试&quo ...

  10. web前端开发分享-目录

    1. web前端开发分享-css,js入门篇 2. web前端开发分享-css,js进阶篇 3. web前端开发分享-css,js提高篇 4. web前端开发分享-css,js工具篇 5. web前端 ...