xml地图|网站地图|网站标签 [设为首页] [加入收藏]

美高梅游戏网站

当前位置:美高梅游戏网站 > 美高梅游戏网站 > HTML5游戏开发系列教程4(译)

HTML5游戏开发系列教程4(译)

来源:http://www.gd-chuangmei.com 作者:美高梅游戏网站 时间:2019-11-26 18:29

HTML5 游戏开发基础的教程

2017/03/24 · HTML5 · 2 评论 · 游戏

本文由 伯乐在线 - 紫洋 翻译,艾凌风 校稿。未经许可,禁止转载!
英文出处:Mikołaj Stolarski & Tomasz Grajewski。欢迎加入翻译组。

在游戏的视觉效果定义其整体外观、感觉和游戏玩法本身。玩家被好的视觉体验所吸引,从而可达到产生更多的流量。这是创建成功的游戏和为玩家提供很多乐趣的关键。

在这篇文章中,我们基于 HTML5 游戏的不同视觉效果实现,提出几个构思方案。这些示例将依据我们自己的游戏《Skytte 》所实现的效果。我会解释支持他们的基本思想, ,并提供应用于我们项目中的效果。

原文地址:

你会学到什么

在我们开始之前, 我想列出一些我希望你能从本文中学习的知识:

  • 基本的游戏设计
    我们来看看通常用于制造游戏和游戏效果的模式: 游戏循环、精灵、碰撞和粒子系统。
  • 视觉效果的基本实现
    我们还将探讨支持这些模式的理论和一些代码示例。

这篇文章是我们继续使用canvas来进行HTML5游戏开发系列的文章,我们要学习下一个元素:精灵动画和基本的声音处理。在我们这个示例中,你将看见一直正在飞的龙,我们能一直听见它翅膀扇动的声音,当鼠标释放事件发生时还有龙咆哮的声音。最后我们将教会龙移动到鼠标按下处。

常见的模式

让我们从游戏开发中常用的大一些模式和元素开始

前一篇的的介绍在HTML5游戏开发系列教程3(译)。

精灵

这些只是在游戏中代表一个对象的二维图像。精灵可以用于静态对象, 也可以用于动画对象, 当每个精灵代表一个帧序列动画。它们也可用于制作用户界面元素。

通常游戏包含从几十到几百精灵图片。为了减少内存的使用和处理这些映像所需的能力, 许多游戏使用精灵表。

第一步:HTML

精灵表

这些都用来在一个图像中合成一套单个精灵。这减少了在游戏中文件的数量,从而减少内存和处理电源使用。精灵表包含许多单精灵堆积彼此相邻的行和列,和类似精灵的图像文件,它们包含可用于静态或动画。

图片 1

精灵表例子。(图像来源: Kriplozoik)

下面是Code + Web的文章, 帮助您更好地理解使用精灵表的益处。

index.html

游戏循环

重要的是要认识到游戏对象并不真正在屏幕上移动。运动的假象是通过渲染一个游戏世界的屏幕快照, 随着游戏的时间的一点点推进 (通常是1/60 秒), 然后再渲染的东西。这实际上是一个停止和运动的效果, 并常在二维和三 维游戏中使用。游戏循环是一种实现此停止运动的机制。它是运行游戏所需的主要组件。它连续运行, 执行各种任务。在每个迭代中, 它处理用户输入, 移动实体, 检查碰撞, 并渲染游戏 (推荐按这个顺序)。它还控制了帧之间的游戏时间。

下面示例是用JavaScriptpgpg语言写的非常基本的游戏循环︰

JavaScript

var lastUpdate; function tick() { var now = window.Date.now(); if (lastUpdate) { var elapsed = (now-lastUpdate) / 1000; lastUpdate = now; // Update all game objects here. update(elapsed); // ...and render them somehow. render(); } else { // Skip first frame, so elapsed is not 0. lastUpdate = now; } // This makes the `tick` function run 60 frames per second (or slower, depends on monitor's refresh rate). window.requestAnimationFrame(tick); };

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var lastUpdate;
 
function tick() {
  var now = window.Date.now();
 
  if (lastUpdate) {
    var elapsed = (now-lastUpdate) / 1000;
    lastUpdate = now;
 
    // Update all game objects here.
    update(elapsed);
    // ...and render them somehow.
    render();
  } else {
    // Skip first frame, so elapsed is not 0.
    lastUpdate = now;
  }
 
  // This makes the `tick` function run 60 frames per second (or slower, depends on monitor's refresh rate).
  window.requestAnimationFrame(tick);
};

请注意,上面的例子中是非常简单。它使用可变时间增量 (已用的变量),并建议升级此代码以使用固定的增量时间。有关详细信息, 请参阅本文。

         HTML5 Game Development - Lesson 4 | Script Tutorials













             HTML5 Game Development - Lesson 4
             Back to original tutorial on Script Tutorials

碰撞检测

碰撞检测是指发现物体之间的交点。这对于许多游戏是必不可少的, 因为它用来检测玩家击中墙壁或子弹击中敌人, 诸如此类等等。当检测到碰撞时, 它可以用于游戏逻辑设计中;例如, 当子弹击中玩家时, 健康分数会减少十点。

有很多碰撞检测算法, 因为它是一个性能繁重的操作, 明智的选择最好的方法是很重要的。要了解有关碰撞检测、算法以及如何实现它们的更多信息, 这里有一篇来自MDN 的文章。

第二步:CSS

粒子和粒子系统

粒子基本上是用粒子系统的精灵。在游戏开发中一个粒子系统是由粒子发射器和分配给该发射器的粒子组成的一个组成部分。它用来模拟各种特效,像火灾、 爆炸、 烟、 和下雨的影响。随着时间的推移微粒和每个发射器有其自身的参数来定义各种变量,用于模拟的效果,如速度、 颜色、 粒子寿命或持续时间,重力、 摩擦和风速。

css/main.css

欧拉积分

欧拉积分是运动的积分方程的一种方法。每个对象的位置计算基于其速度,质量和力量,并需要重新计算每个 tick 在游戏循环。欧拉方法是最基本和最有用的像侧滚动的射击游戏,但也有其它的方法,如Verlet 积分和 RK4积分,会更好地完成其他任务。下面我将展示一个简单的实现的想法。

你需要一个基本的结构以容纳对象的位置、 速度和其他运动相关的数据。我们提出两个相同的结构,但每一个都有不同的意义,在世界空间中︰ 点和矢量。游戏引擎通常使用某种类型的矢量类,但点和矢量之间的区别是非常重要的,大大提高了代码的可读性 (例如,您计算不是两个矢量,但这两个点之间的距离,这是更自然)。

这次我将不会写出css的代码了--它仅仅是页面布局设计样式而已,没有啥特殊的,可以从源代码包中获取。

简单地说, 它代表了二维空间空间中的一个元素, 它有 x 和 y 坐标, 它定义了该点在该空间中的位置。

JavaScript

function point2(x, y) { return {'x': x || 0, 'y': y || 0}; }

1
2
3
function point2(x, y) {
  return {'x': x || 0, 'y': y || 0};
}

第三步:JS

矢量

一个矢量是一个具有长度 (或大小) 的几何对象和方向。2 D 游戏中矢量主要是用于描述力(例如重力、 空气阻力和风) 和速度,以及禁止运动或光线反射。矢量有许多用途。

JavaScript

function vector2(x, y) { return {'x': x || 0, 'y': y || 0}; }

1
2
3
function vector2(x, y) {
  return {'x': x || 0, 'y': y || 0};
}

上述函数创建了新的二维矢量和点。在这种情况下, 我们不会在 javascript 中使用 new 运算符来获得大量的性能。还要注意, 有一些 第三方库可用来操纵矢量 (glMatrix 是一个很好的候选对象)。

下面是在上面定义的二维结构上使用的一些非常常用的函数。首先, 计算两点之间的距离:

JavaScript

point2.distance = function(a, b) { // The x and y variables hold a vector pointing from point b to point a. var x = a.x - b.x; var y = a.y

  • b.y; // Now, distance between the points is just length (magnitude) of this vector, calculated like this: return Math.sqrt(x*x + y*y); };
1
2
3
4
5
6
7
point2.distance = function(a, b) {
  // The x and y variables hold a vector pointing from point b to point a.
  var x = a.x - b.x;
  var y = a.y - b.y;
  // Now, distance between the points is just length (magnitude) of this vector, calculated like this:
  return Math.sqrt(x*x + y*y);
};

矢量的大小 (长度) 可以直接从最后一行的上面的函数,这样计算︰

JavaScript

vector2.length = function(vector) { return Math.sqrt(vector.x*vector.x

  • vector.y*vector.y); };
1
2
3
vector2.length = function(vector) {
  return Math.sqrt(vector.x*vector.x + vector.y*vector.y);
};

图片 2

矢量的长度。

矢量规范化也是非常方便的。下面的函数调整矢量的大小,所以它成为一个单位矢量;也就是说,它的长度是 1,但保持它的方向。

JavaScript

vector2.normalize = function(vector) { var length = vector2.length(vector); if (length > 0) { return vector2(vector.x / length, vector.y / length); } else { // zero-length vectors cannot be normalized, as they do not have direction. return vector2(); } };

1
2
3
4
5
6
7
8
9
10
vector2.normalize = function(vector) {
  var length = vector2.length(vector);
 
  if (length > 0) {
    return vector2(vector.x / length, vector.y / length);
  } else {
    // zero-length vectors cannot be normalized, as they do not have direction.
    return vector2();
  }
};

图片 3

矢量归一化。

另一个有用的例子是,其方向指从一个位置到另一个位置︰

JavaScript

// Note that this function is different from `vector2.direction`. // Please don't confuse them. point2.direction = function(from, to) { var x = to.x - from.x; var y = to.y - from.y; var length = Math.sqrt(x*x + y*y); if (length > 0) { return vector2(x / length, y / length); } else { // `from` and `to` are identical return vector2(); } };

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Note that this function is different from `vector2.direction`.
// Please don't confuse them.
point2.direction = function(from, to) {
  var x = to.x - from.x;
  var y = to.y - from.y;
  var length = Math.sqrt(x*x + y*y);
 
  if (length > 0) {
    return vector2(x / length, y / length);
  } else {
    // `from` and `to` are identical
    return vector2();
  }
};

点积是对两个矢量 (通常为单位矢量) 的运算, 它返回一个标量的数字, 表示这些矢量的角度之间的关系。

JavaScript

vector2.dot = function(a, b) { return a.x*b.x + a.y*b.y; };

1
2
3
vector2.dot = function(a, b) {
  return a.x*b.x + a.y*b.y;
};

js/script.js

图片 4

矢量点积

点积是一个矢量投影矢量 b 上的长度。返回的值为 1 表示两个矢量指向同一方向。值为-1 意味着矢量方向相反的矢量 b 点。值为 0 表示该矢量是垂直于矢量 b。

这里是实体类的示例,以便其他对象可以从它继承。只描述了与运动相关的基本属性。

JavaScript

function Entity() { ... // Center of mass usually. this.position = point2(); // Linear velocity. // There is also something like angular velocity, not described here. this.velocity = vector2(); // Acceleration could also be named `force`, like in the Box2D engine. this.acceleration = vector2(); this.mass = 1; ... }

1
2
3
4
5
6
7
8
9
10
11
12
function Entity() {
  ...
  // Center of mass usually.
  this.position = point2();
  // Linear velocity.
  // There is also something like angular velocity, not described here.
  this.velocity = vector2();
  // Acceleration could also be named `force`, like in the Box2D engine.
  this.acceleration = vector2();
  this.mass = 1;
  ...
}

您可以在你的游戏中使用像素或米为单位。我们鼓励您使用米,因为在开发过程中,它更容易平衡的事情。速度,应该是米每秒,而加速度应该是米每秒的平方。

当使用一个第三方物理引擎,只是将存储在您的实体类的物理主体(或主体集) 的引用。然后,物理引擎将在每个主体内存储所述的属性,如位置和速度。

基本的欧拉积分看起来像这样︰

JavaScript

acceleration = force / mass velocity += acceleration position += velocity

1
2
3
acceleration = force / mass
velocity += acceleration
position += velocity

上面的代码必须在游戏中每个对象的每个帧中执行。下面是在 JavaScript 中的基本执行代码︰

JavaScript

Entity.prototype.update = function(elapsed) { // Acceleration is usually 0 and is set from the outside. // Velocity is an amount of movement (meters or pixels) per second. this.velocity.x += this.acceleration.x * elapsed; this.velocity.y += this.acceleration.y * elapsed; this.position.x += this.velocity.x * elapsed; this.position.y += this.velocity.y * elapsed; ... this.acceleration.x = this.acceleration.y = 0; }

1
2
3
4
5
6
7
8
9
10
11
12
13
Entity.prototype.update = function(elapsed) {
  // Acceleration is usually 0 and is set from the outside.
  // Velocity is an amount of movement (meters or pixels) per second.
  this.velocity.x += this.acceleration.x * elapsed;
  this.velocity.y += this.acceleration.y * elapsed;
 
  this.position.x += this.velocity.x * elapsed;
  this.position.y += this.velocity.y * elapsed;
 
  ...
 
  this.acceleration.x = this.acceleration.y = 0;
}

经过的是自最后一个帧 (自最近一次调用此方法) 所经过的时间量 (以秒为单位)。对于运行在每秒 60 帧的游戏,经过的值通常是 1/60 秒,也就是 0.016 (6) s。

上文提到的增量时间的文章也涵盖了这个问题。

要移动对象,您可以更改其加速度或速度。为实现此目的,应使用如下所示的两个函数︰

JavaScript

Entity.prototype.applyForce = function(force, scale) { if (typeof scale === 'undefined') { scale = 1; } this.acceleration.x += force.x * scale / this.mass; this.acceleration.y += force.y * scale / this.mass; }; Entity.prototype.applyImpulse = function(impulse, scale) { if (typeof scale === 'undefined') { scale = 1; } this.velocity.x += impulse.x * scale / this.mass; this.velocity.y += impulse.y * scale / this.mass; };

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Entity.prototype.applyForce = function(force, scale) {
  if (typeof scale === 'undefined') {
    scale = 1;
  }
  this.acceleration.x += force.x * scale / this.mass;
  this.acceleration.y += force.y * scale / this.mass;
};
 
Entity.prototype.applyImpulse = function(impulse, scale) {
  if (typeof scale === 'undefined') {
    scale = 1;
  }
  this.velocity.x += impulse.x * scale / this.mass;
  this.velocity.y += impulse.y * scale / this.mass;
};

要向右移动一个对象你可以这样做︰

JavaScript

// 10 meters per second in the right direction (x=10, y=0). var right = vector2(10, 0); if (keys.left.isDown) // The -1 inverts a vector, i.e. the vector will point in the opposite direction, // but maintain magnitude (length). spaceShip.applyImpulse(right, -1); if (keys.right.isDown) spaceShip.applyImpulse(right, 1);

1
2
3
4
5
6
7
8
9
// 10 meters per second in the right direction (x=10, y=0).
var right = vector2(10, 0);
 
if (keys.left.isDown)
  // The -1 inverts a vector, i.e. the vector will point in the opposite direction,
  // but maintain magnitude (length).
  spaceShip.applyImpulse(right, -1);
if (keys.right.isDown)
  spaceShip.applyImpulse(right, 1);

请注意,在运动中设置的对象保持运动。您需要实现某种减速停止移动的物体 (空气阻力或摩擦,也许)。

    iBgShiftX = 100   dragonW = 75; 
  dragonH = 70; 
  iSprPos = 0; 
  iSprDir = 4; 
  dragonSound; 
  wingsSound; 
  bMouseDown = ; 
  iLastMouseX = 0;   
  iLastMouseY = 0;   



      .x =     .y =     .w =     .h =     .image =     .bDrag =   


      ctx.clearRect(0, 0  

      clear(); 


     iBgShiftX -= 4      (iBgShiftX <= 0         iBgShiftX = 1045      ctx.drawImage(backgroundImage, 0 + iBgShiftX, 0, 1000, 940, 0, 0, 1000, 600 

     iSprPos++      (iSprPos >= 9         iSprPos = 0  

               (iLastMouseX >             dragon.x += 5           (iLastMouseY >             dragon.y += 5           (iLastMouseX <             dragon.x -= 5           (iLastMouseY <             dragon.y -= 5   

     ctx.drawImage(dragon.image, iSprPos*dragon.w, iSprDir*dragon.h, dragon.w, dragon.h, dragon.x - dragon.w/2, dragon.y - dragon.h/2  



 $(     canvas = document.getElementById('scene'     ctx = canvas.getContext('2d' 
      width =      height = 

     backgroundImage =      backgroundImage.src = 'images/hell.jpg'     backgroundImage.onload =       backgroundImage.onerror =          console.log('Error loading the background image.'  

     dragonSound =  Audio('media/dragon.wav'     dragonSound.volume = 0.9 

     wingsSound =  Audio('media/wings.wav'     wingsSound.volume = 0.9     wingsSound.addEventListener('ended', () { 
         .currentTime = 0              },   

      oDragonImage =      oDragonImage.src = 'images/dragon.gif'     oDragonImage.onload =       dragon =  Dragon(400, 300 
     $('#scene').mousedown((e) { 
          mouseX = e.layerX || 0          mouseY = e.layerY || 0         (e.originalEvent.layerX) { 
             mouseX =             mouseY =  
         bMouseDown =  
          (mouseX > dragon.x- dragon.w/2 && mouseX < dragon.x- dragon.w/2 +dragon.w &&
             mouseY > dragon.y- dragon.h/2 && mouseY < dragon.y-dragon.h/2 + 
             dragon.bDrag =              dragon.x =             dragon.y =   
     $('#scene').mousemove((e) { 
          mouseX = e.layerX || 0          mouseY = e.layerY || 0         (e.originalEvent.layerX) { 
             mouseX =             mouseY =  

         iLastMouseX =         iLastMouseY = 

                      dragon.x =             dragon.y =  

          (mouseX > dragon.x && Math.abs(mouseY-dragon.y) < dragon.w/2) {  //右
             iSprDir = 0         }   (mouseX < dragon.x && Math.abs(mouseY-dragon.y) < dragon.w/2) {  //左
             iSprDir = 4         }   (mouseY > dragon.y && Math.abs(mouseX-dragon.x) < dragon.h/2) {  //下
             iSprDir = 2         }   (mouseY < dragon.y && Math.abs(mouseX-dragon.x) < dragon.h/2) {  //上
             iSprDir = 6         }   (mouseY < dragon.y && mouseX <             iSprDir = 5         }   (mouseY < dragon.y && mouseX >             iSprDir = 7         }   (mouseY > dragon.y && mouseX <             iSprDir = 3         }   (mouseY > dragon.y && mouseX >             iSprDir = 1   
     $('#scene').mouseup((e) { 
         dragon.bDrag =          bMouseDown =  

         dragonSound.currentTime = 0   
     setInterval(drawScene, 30); 
 });

武器的影响

现在我要解释一下, 在我们的 HTML5 游戏中, 某些武器效果是如何射击的

程序是怎样实现的:首先我们定义了画布,上下文,然后加载了背景图片,两个声音,再初始化我们的龙和绑定了不同的鼠标事件。在我们主循环重绘方法中,我移动了背景图片,并更新了帧的位置,最后画龙。在我们的代码里你可以发现几个有趣的方法:

本文由美高梅游戏网站发布于美高梅游戏网站,转载请注明出处:HTML5游戏开发系列教程4(译)

关键词:

上一篇:9.SVG动画

下一篇:没有了