怎样用HTML5 canvas制作有水平游戏一个简单的游戏

三分钟HTML5画布(Canvas)动画教程
通常大部分Web程序员平时的工作只是开发一些常规的网站、信息化办公系统等。在他们的眼里,Web游戏动画开发程序员似乎神奇的多,Web游戏动画明显比普通的Web应用要灵活,更注重交互和视觉冲击,而动画动作、做图配色等方面更是外行。之前我也在一篇《》中向大家展示了Web动画的神奇魅力。
但事实上,也许你想错了。Web动画并不难。你只需要掌握一些非常基本的知识就能开发出非常漂亮的Web动画。以前开发动画应用你可能需要学习很复杂的动画制作框架。自从HTML5画布(Canvas)功能面世后,Web动画就一下子从云端跌落到了地面——任何一个Web程序员都可以轻易的用画布(Canvas)技术+JavaScript来开发出各种动画效果。网上用来方便程序员开发画布(Canvas)游戏动画的JS工具包非常丰富,《》这款练习盲打的游戏就是一个叫做的游戏引擎。而文下面的教程中将使用的是另外一个叫做的Web动画工具包。它们都是开源的。
把鼠标放到上面的小丑脸上,然后移开,看看会发生效果。
这就是一个最简单的动画。简单吗?你能做出来吗?
下面我们就用三分钟时间,用很少的几行代码把这个动画做出来。你只需要了解很少的几个API,然后使用需要的动画参数,就能制作出这个有趣又能响应你的动作的Web动画。
第一步,画五官
这个小丑没有耳朵和眉毛,所以只剩下三官,但它的两个眼睛我们要分别绘制,所以一共是四个部分。下面先看看代码。
绘制左眼的代码
var leftEye = new Kinetic.Line({
points: [0, 200, 50, 190, 100, 200, 50, 210],
tension: 0.5,
closed: true,
stroke: 'white',
strokeWidth: 10
绘制右眼的代码
var rightEye = new Kinetic.Line({
x: sw - 250,
points: [0, 200, 50, 190, 100, 200, 50, 210],
tension: 0.5,
closed: true,
stroke: 'white',
strokeWidth: 10
绘制鼻子的代码
var nose = new Kinetic.Line({
points: [240, 280, sw/2, 300, sw-240,280],
tension: 0.5,
closed: true,
stroke: 'white',
strokeWidth: 10
绘制嘴巴的代码
var mouth = new Kinetic.Line({
points: [150, 340, sw/2, 380, sw - 150, 340, sw/2, sh],
tension: 0.5,
closed: true,
stroke: 'red',
strokeWidth: 10
你会不会觉得很失望,原来就这么简单几行代码。没错,就是这么简单,相信你对自己能成为一名Web游戏动画程序员开始有信心了! ?
简单讲解一下上面的代码。Kinetic就是我们使用的js工具包。在页面的头部,我们需要这样引用它:
&script type="text/javascript"
src="/js/kinetic-v5.0.1.min.js"&&/script&
其它几个分别是几个关键点,线条弹性,颜色,宽度等。这些都很容易理解。
第二步,让图动起来
这个动画之所以能吸引人,是因为它能响应你的鼠标动作,和用户有互动,这是一个成功的动画最关键的地方。如果你仔细观察,这个小丑五官的变化只是形状的变化,眼睛变大,嘴巴变大,鼻子变大,但特别的是这个变化不是瞬间变化,而是有过渡性的,这里面有一些算法,这就是最让人发愁的地方。幸运的是,这算法技术都非常的成熟,不需要我们来思考,在这些动画引擎库里都把这些技术封装成了非常简单方便的接口。下面我们来看看如何让动起来。
左眼的动画
var leftEyeTween = new Kinetic.Tween({
node: leftEye,
duration: 1,
easing: Kinetic.Easings.ElasticEaseOut,
points: [0, 200, 50, 150, 100, 200, 50, 200]
右眼的动画
var rightEyeTween = new Kinetic.Tween({
node: rightEye,
duration: 1,
easing: Kinetic.Easings.ElasticEaseOut,
points: [0, 200, 50, 150, 100, 200, 50, 200]
鼻子的动画
var noseTween = new Kinetic.Tween({
node: nose,
duration: 1,
easing: Kinetic.Easings.ElasticEaseOut,
points: [220, 280, sw/2, 200, sw-220,280]
嘴巴的动画
var mouthTween = new Kinetic.Tween({
node: mouth,
duration: 1,
easing: Kinetic.Easings.ElasticEaseOut,
points: [100, 250, sw/2, 250, sw - 100, 250, sw/2, sh-20]
这些代码非常的简单,而且变量名能自释其意。稍微有点经验的程序员想看懂这些代码应该不难。基本每段代码都是让你提供一些点,指定动画动作的衰退模式和持续时间。
完整的动画代码
&!DOCTYPE HTML&
padding: 0
#container {
background-color:
&div id="container"&&/div&
&script src="/js/lib/kinetic-v5.0.1.min.js"&&/script&
&script defer="defer"&
var sw = 578;
var sh = 400;
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 400
var layer = new Kinetic.Layer({
var leftEye = new Kinetic.Line({
points: [0, 200, 50, 190, 100, 200, 50, 210],
tension: 0.5,
closed: true,
stroke: 'white',
strokeWidth: 10
var rightEye = new Kinetic.Line({
x: sw - 250,
points: [0, 200, 50, 190, 100, 200, 50, 210],
tension: 0.5,
closed: true,
stroke: 'white',
strokeWidth: 10
var nose = new Kinetic.Line({
points: [240, 280, sw/2, 300, sw-240,280],
tension: 0.5,
closed: true,
stroke: 'white',
strokeWidth: 10
var mouth = new Kinetic.Line({
points: [150, 340, sw/2, 380, sw - 150, 340, sw/2, sh],
tension: 0.5,
closed: true,
stroke: 'red',
strokeWidth: 10
layer.add(leftEye)
.add(rightEye)
.add(nose)
.add(mouth);
stage.add(layer);
var leftEyeTween = new Kinetic.Tween({
node: leftEye,
duration: 1,
easing: Kinetic.Easings.ElasticEaseOut,
points: [0, 200, 50, 150, 100, 200, 50, 200]
var rightEyeTween = new Kinetic.Tween({
node: rightEye,
duration: 1,
easing: Kinetic.Easings.ElasticEaseOut,
points: [0, 200, 50, 150, 100, 200, 50, 200]
var noseTween = new Kinetic.Tween({
node: nose,
duration: 1,
easing: Kinetic.Easings.ElasticEaseOut,
points: [220, 280, sw/2, 200, sw-220,280]
var mouthTween = new Kinetic.Tween({
node: mouth,
duration: 1,
easing: Kinetic.Easings.ElasticEaseOut,
points: [100, 250, sw/2, 250, sw - 100, 250, sw/2, sh-20]
stage.getContainer().addEventListener('mouseover', function() {
leftEyeTween.play();
rightEyeTween.play();
noseTween.play();
mouthTween.play();
stage.getContainer().addEventListener('mouseout', function() {
leftEyeTween.reverse();
rightEyeTween.reverse();
noseTween.reverse();
mouthTween.reverse();
我相信你已经在3三分钟内看完并看懂了这个动画的制作过程和原理。当然,这个动画很简单,我们这里只是粗浅的讲解一些HTML5画布(Canvas)动画技术的皮毛。如果想真正的成为Web动画告诉,那你还需要做很多的努力。但如果你只是业余爱好,我相信这已经足够让你骄傲了。
相关文章:
扫一扫二维码分享:用HTML5制作一个简单的弹力球游戏
作者:佚名
字体:[ ] 来源:互联网 时间:05-12 18:10:29
这篇文章主要介绍了用HTML5制作一个简单的弹力球游戏,主要还是利用到了HTML5的Canvas API,需要的朋友可以参考下
学canvas学了有一个多礼拜了,觉得canvas真心好玩。学canvas的人想法估计都跟我差不多,抱着写游戏的态度去学canvas的。所以运动学啊、碰撞检测啊、一些简单的算法神马的是基础啊。以前没做过游戏的我学起来还真心吃力。今天就来说下用canvas写个最简单的弹力球游戏,就运用了最简单的重力作用以及碰撞检测。
  先上DEMO: (鼠标点击canvas里的空白区域会给与小球新速度)
  【创建小球对象】
  第一步就是先创建一个小球对象,写好小球的构造函数:代码如下:var Ball = function(x , y , r , color){
this.oldx =
this.oldy =
this.vx = 0;
this.vy = 0;this.radius =
this.color =
  小球属性很简单,xy是小球的坐标,vx和vy是小球的初始水平速度和初始垂直速度。radius就是小球的半径,color是小球颜色(为了区分不同球),oldx和oldy是记录小球的上一帧的位置,后期球与球之间碰撞后用于位置修正(后面其实没用上,位置修正直接计算了,如果用oldx来设置很不严谨,不过记录一下,难免会用得到)。
  小球属性写好后,就在小球原型中写小球的动作了:代码如下:Ball.prototype = {
paint:function(){
ctx.save();
ctx.beginPath();
ctx.arc(this.x , this.y , this.radius , 0 , Math.PI*2);
ctx.fillStyle=this.
ctx.fill();
ctx.restore();
this.moving =
run:function(t){
if(!this.candrod) {
this.paint();
this.oldx = this.x;
this.oldy = this.y;&/p&
if(Math.abs(this.vx) & 0.01){
this.vx = 0;
else this.vx += this.vx&0? -mocali*t : mocali*t;&/p&
this.vy = this.vy + g *
this.x += t * this.vx *
this.y += t * this.vy *&/p&
if(this.y & canvas.height - ballRadius || this.y & ballRadius){
this.y = this.y & ballRadius ? ballRadius : (canvas.height - ballRadius);
this.vy = -this.vy*collarg
if(this.x & canvas.width - ballRadius || this.x & ballRadius){
this.x = this.x & ballRadius ? ballRadius : (canvas.width - ballRadius);
this.derectionX = !this.derectionX;
this.vx = -this.vx*
this.paint();
  小球的动作方法也很简单,就两个,第一个方法是把自己画出来,第二个方法就是控制小球的运动。t是当前帧与上一帧的时间差。用于计算小球的速度的增量从而得出小球的位移增量,从而计算出小球的新位置并且将小球重绘。得出新位置的同时判断小球的新位置有无超出墙壁,如果超出则进行速度修正让小球反弹。
  第二个方法里的一些常量ballRadius =30, g = 9.8 , mocali = 0.5,balls = [],collarg = 0.8,pxpm = canvas.width/20; 意思很明显:ballradius是球半径,g是重力加速度,mocali是空气阻力引起的水平方向的减速度,balls是一个用于存放小球对象的数组,collarg是弹力系数。pxpm是像素与米之间的映射,把画布当成是20米宽的区域。
  【碰撞检测】
  创建好小球对象后,就开始写碰撞了,小球与小球之间的碰撞:代码如下:function collision(){
for(var i=0;i&balls.i++){
for(var j=0;j&balls.j++){
var b1 = balls[i],b2 = balls[j];
if(b1 !== b2){
var rc = Math.sqrt(Math.pow(b1.x - b2.x , 2) + Math.pow(b1.y - b2.y , 2));
if(Math.ceil(rc) & (b1.radius + b2.radius)){&/p&
//获得碰撞后速度的增量
var ax = ((b1.vx - b2.vx)*Math.pow((b1.x - b2.x) , 2) + (b1.vy - b2.vy)*(b1.x - b2.x)*(b1.y - b2.y))/Math.pow(rc , 2)
var ay = ((b1.vy - b2.vy)*Math.pow((b1.y - b2.y) , 2) + (b1.vx - b2.vx)*(b1.x - b2.x)+(b1.y - b2.y))/Math.pow(rc , 2)&/p&
//给与小球新的速度
b1.vx = (b1.vx-ax)*
b1.vy = (b1.vy-ay)*
b2.vx = (b2.vx+ax)*
b2.vy = (b2.vy+ay)*&/p&
//获取两球斜切位置并且强制扭转
var clength = ((b1.radius+b2.radius)-rc)/2;
var cx = clength * (b1.x-b2.x)/
var cy = clength * (b1.y-b2.y)/
b1.x = b1.x+
b1.y = b1.y+
b2.x = b2.x-
b2.y = b2.y-
&p&  每一帧都进行小球之间碰撞的判断,如果两个小球球心距离小于两球半径之和,则证明两个小球发生了碰撞。然后进行计算两个小球碰撞之后的速度变化量。ax和ay就是速度变化量。 后面长长的公式就是这个:
具体原理我就不说了,想了解原理就直接戳 。 下面那段就是防止小球重复碰撞检测导致无法正常反弹,所以计算两小球的球心距离,然后算出两个小球的斜切位置,并且将两个小球的位置进行更正。
  【运动动画】
  最后一步:代码如下:&/p&
&p&canvas.onclick = function(event){
event = event || window.
var x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft - canvas.offsetL
var y= event.clientY + document.body.scrollTop + document.documentElement.scrollTop - canvas.offsetT&/p&
balls.forEach(function(){
this.vx = (x - this.x)/20; //初速度 m/s
this.vy = (y - this.y)/20;
function animate(){
ctx.save();
ctx.fillStyle = "rgba(255,255,255,0.2)";
ctx.fillRect(0,0,canvas.width,canvas.height)
ctx.restore();
// ctx.clearRect(0,0,canvas.width,canvas.height)&/p&
var t1 = new Date();
var t = (t1 - t0)/1000;
collision();
balls.forEach(function(){
this.run(t);
t0 = t1;&/p&
if("requestAnimationFrame" in window){
requestAnimationFrame(animate);
else if("webkitRequestAnimationFrame" in window){
webkitRequestAnimationFrame(animate);
else if("msRequestAnimationFrame" in window){
msRequestAnimationFrame(animate);
else if("mozRequestAnimationFrame" in window){
mozRequestAnimationFrame(animate);
通过点击画布的位置来给于小球初速度,然后animate就是动画的每一帧运行的方法。上面的 ctx.fillStyle = &rgba(255,255,255,0.2)&; ctx.fillRect(0,0,canvas.width,canvas.height)是给小球添加虚影,我觉得这样会更好看,如果觉得不喜欢,就直接用clearRect清除就行了。然后就是计算每一帧的时间差,然后对小球数组里小球数组进行遍历重绘。然后再加入碰撞检测的collision方法。动画也就做完了。
  至此,就已经写完了,源码地址:
大家感兴趣的内容
12345678910
最近更新的内容如何基于 HTML5 画布制作一款简单的游戏 | indienova
如何基于 HTML5 画布制作一款简单的游戏
译者按, 本文是一篇使用 HTML5 开发游戏的入门教程, 要完全看懂教程, 需要懂得一点简单的 HTML 和 JavaScript 技术. 如果你还对此一无所知, 请先学习相关的基础知识. 本教程面向的读者主要是对游戏开发的基本流程还一无所知但却怀有兴趣的人, 经验更丰富的爱好者可以尝试参考其它更高级的教程.
如何基于 HTML5 画布制作一款简单的游戏
听说, 你们想要找一篇用HTML5开发简单游戏的快速入门教程? 那我们这就来一行一行来实际地做一个小游戏出来吧!(本文原作者为 Matt Hackett, 已经开发过一款上架 Steam 的作品 A Wizard's Lizard, 因此无需怀疑他的资历问题).
点开右边的超链接可以浏览的代码. 你也可以通过本句内的超链接这款游戏.
游戏效果截图
1. 创建画布
// 创建画布
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 512;
canvas.height = 480;
document.body.appendChild(canvas);
首先我们需要创建一个画布元素. 我并没有直接在HTML中写入画布元素的标签, 而是选择用几行简短的JavaScript代码来创建它, 步骤非常容易. 创建好元素后, 我们将它的上下文保存到变量ctx中, 稍后可以用它来调用负责渲染的指令. 我们设定画布为二维画布(这个例子显然只是个简单的平面小游戏), 接着将它加入文档模型, 这样我们就能在页面上看到画布出现了.
2. 加载图片
var bgReady =
var bgImage = new Image();
bgImage.onload = function () {
bgImage.src = "images/background.png";
游戏需要视觉呈现, 因此让我们来加载一些图片! 为了尽可能让这个例子保持简单, 这里我将素材只作为一张图片来载入, 并没有使用一个封装精美的类或者其他的结构. 布尔量bgReady可以告诉我们现在能否安全地绘制该图片, 如果尝试在加载完毕前绘制图片, 将会抛出一个DOM报错.
本例中, 我们共计需要为三种图片素材编写加载代码, 包括: 背景图, 英雄还有怪物.
3. 游戏对象
// 游戏对象
var hero = {
speed: 256, // movement in pixels per second
var monster = {
var monstersCaught = 0;
现在, 我们来定义一些稍后会用到的变量. 英雄hero的属性speed用来设置代表其角色的图片在画布上每秒移动多少像素. 本游戏设定怪物无法移动, 因此其字面量定义中只包含代表位置的坐标属性. 最后, 我们定义变量monstersCaught来存储分数, 这个例子中, 它代表玩家抓住了多少怪物.
4. 玩家输入
// 键盘控制操作码(key control handle)
// 用 KeyDown 缓存按键序列
var keysDown = {};
addEventListener("keydown", function (e) {
keysDown[e.keyCode] =
}, false);
addEventListener("keyup", function (e) {
delete keysDown[e.keyCode];
}, false);
接着我们来编写控制键盘输入的代码. (那些仅有网页背景的开发者可能会在这里遇见第一个小小的障碍.) 在进行网页开发时, 最恰当的处理是在用户一开始输入的时候就开始播放动画或者发送响应数据. 但在游戏中, 我们会要求游戏逻辑只作出唯一的响应, 以保持对事件发生的时机和时间能够做到紧凑的控制. 因此我们将用户的输入缓存起来, 而不是直接执行它们.
我们只需使用对象keysDown来存储输入事件对应的键盘代码就可以了. 当该对象中包含某个键盘代码时就说明玩家按下了对应的按键. 简单极了!
// 当玩家抓住怪物时重置游戏中的某些状态
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));
调用重置函数可以开始一局新游戏或新关卡, 你也可以在任何需要的时候调用它. 它用来将游戏中的一些变量重置为默认值. 本游戏中, 它会把玩家控制的英雄放置在屏幕中央, 把怪物放置在屏幕的某个随机位置.
6. 更新对象状态
//更新对象状态
var update = function (modifier) {
if (38 in keysDown) { // Player holding up
hero.y -= hero.speed *
if (40 in keysDown) { // Player holding down
hero.y += hero.speed *
if (37 in keysDown) { // Player holding left
hero.x -= hero.speed *
if (39 in keysDown) { // Player holding right
hero.x += hero.speed *
// 怪物和英雄是否相遇?
// 根据怪物图片和英雄图片位置的距离来判断.
hero.x &= (monster.x + 32)
&& monster.x &= (hero.x + 32)
&& hero.y &= (monster.y + 32)
&& monster.y &= (hero.y + 32)
++monstersC
状态更新函数每隔一小段时间就被调用一次. 首先它会检查玩家是否按下了上下左右四个方向键. 如果有按下, 则将英雄朝对应方向移动一小段距离.
该函数的参数modifier乍看之下令人摸不到头绪, 但你稍后会看到主函数是如何调用这个函数的, 我这里就先解释一下. modifier是一个单位为1的表示更新函数调用时间间隔的数. 如果恰好有一秒过去, 该参数的值为1, 英雄移动速度乘以1倍, 这意味着在一秒内英雄将移动256像素. 如果更新函数调用间隔仅有半秒, 则该参数值为0.5, 英雄的移动速度乘以0.5倍, 这意味着在半秒内英雄将移动128像素. 诸如此类. 由于该函数调用十分频繁快速, 因此该修正参数的值总是非常的小, 但使用这种方法我们可以确保无论游戏脚本的执行速度多快多慢, 玩家所控制的英雄却总能保持相同的移动速度.
我们已经能够检查玩家的输入并据此来移动英雄的位置, 现在我们接着来处理我们控制的英雄能做些什么, 在本游戏中, 我们要处理的是代表英雄和玩家的图片相遇时(简单的碰撞检测实现), 会发生些什么. 这样就勉强是个可玩的游戏了. 这里我们将分数+1(即对monstersCaught变量+1), 然后重置游戏.
7. 渲染对象
// 绘制所有东西
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);
视觉的交互能够为游戏带来更多的乐趣, 因此让我们把一切都"画"到屏幕上来! 首先我们将背景图在屏幕上, 接着绘制英雄和怪物. 注意, 绘制的顺序非常重要, 后绘制的素材会遮挡之前画布上的内容.
接着, 我们通过画布的上下文变量的属性来设置字体和颜色, 之后我们调用fillText方法来显示玩家得分. 鉴于我们并没有加入更复杂的动画和移动方式, 因此绘制工作就此完成.
8. 游戏主循环
// 游戏主循环
var main = function () {
var now = Date.now();
var delta = now -
update(delta / 1000);
// 再次调用主循环
requestAnimationFrame(main);
游戏的主循环用来控制整个游戏的执行流程. 首先我们获得当前的时间戳, 然后算出时间间隔量delta(继上次执行主循环后经过了多少时间). 我们将该间隔量除以1000(因为1s=1000ms)来得到参数modifier以调用状态更新函数. 然后我们调用绘制函数, 再次保存时间戳.
这篇教程里谈到了更多有关游戏主循环的知识:
9. 游戏主循环的注解
// 对 requestAnimationFrame 的跨浏览器支持
requestAnimationFrame = w.requestAnimationFrame || w.webkitRequestAnimationFrame || w.msRequestAnimationFrame || w.mozRequestAnimationF
无需担心, 这部分无法完全理解也无关紧要, 但我觉得稍微解释一点循环部分的代码对初学者会大有裨益.
为了持续地执行主循环函数, 本教程的老版本曾经使用方法. 近来又出来了更好的新办法: 即通过方法. 然而, 正如多数的 web 技术一样, 我们需要考虑代码的跨浏览器兼容问题. 我这里使用的兼容代码主要基于.
10. 开始游戏
// 开始游戏!
var then = Date.now();
几近完成, 这就是最后的代码片段了! 我们需要先设定一次时间戳(保存到变量then中). 接着我们将游戏的状态置为默认值(你应该还记得这会将英雄放置在屏幕中央, 而怪物则出现在屏幕的随机位置), 然后开始游戏主循环.
恭喜你! (我希望)你现在应该已经理解了编写 JavaScript 通过画布元素进行游戏开发的基本知识了. 自己来尝试一下吧! 你可以试玩游戏, 或者在github上创建代码的分支来尝试改造一个属于自己的版本.
社区的改造版本
这篇教程只涵盖了基本的制作流程, 但作为一款完整的游戏这个例子还是有点单薄, 缺乏很多必要内容: 音效呢? 能否让英雄不跑到屏幕之外? 如果你已经感觉到这篇教程做出的游戏过于简单, 缺乏玩点, 那还不快点动手发挥自己的创意去改造它! 在原教程的回复区内一些读者已经分享了他们的创意.
基本和教程中做出的游戏没有区别. 代码存放在了网站codepen上,你可以很方便地在线编辑修改和运行代码.
游戏关于一只误入超级玛丽世界喜欢吃甜筒的小宝宝. 在本教程的基础上实现了边缘碰撞检测等2d游戏常用技术.
尽管温室效应逐年加剧, 人与自然的对峙日益加重, 小智还是毅然决然地投身珍稀保护动物抓捕之旅. 这一次他前往大海深处抓捕一只贪吃的鲤鱼王. 专门用于打击单身狗的那种本地双人小游戏. 用方向键控制小智或者用wasd控制鲤鱼王逃离小智的魔掌.
增加了音效, 游戏菜单还有撞到障碍物就会结束游戏等许多新要素. 颇具可玩性. 试着来挑战一下能得到多少分数吧.
寻找进一步的帮助
有点迷茫? 我们的社区充满了助人为乐的游戏开发爱好者. 到论坛来发帖问问吧! GOTO
本教程原文作者Matt Hackett所开发的A Wizard's Lizard是另一款模仿以撒的结合的roguelike游戏. 要素丰富, 画风可爱. 根据玩家的反馈信息来看, 存在操作手感偏差的缺憾. 有兴趣的读者尝试一下. 购买链接:
作者背景: Matt Hackett, 独立游戏开发者, 主要作品A Wizard's Lizard。
已有 4 读者收藏
&分享这篇文章
您可能还会对这些文章感兴趣
参与此文章的讨论
<div id="failed-message" data-notify-position="top-right" data-notify-type="error" data-notify-msg=" 操作失败!请通知管理人员。">}

我要回帖

更多关于 html5 canvas 游戏 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信