H5游戏开发,页面中的

H5游戏开发:套圈圈

2018/01/25 · HTML5 · 游戏

原文出处: 凹凸实验室   

 

HTML5的Websocket(理论篇 I)

2017/10/28 · HTML5 · websocket

原文出处: 转转前端   

先请来TA的邻居:

http:无状态、基于tcp请求/响应模式的应用层协议 (A:哎呀,上次你请我吃饭了么? B:我想想, 上次请你吃了么)
tcp:面向连接、保证高可靠性(数据无丢失、数据无失序、数据无错误、数据无重复到达) 传输层协议。(看啊,大阅兵,如此规整有秩序)

为什么要引入Websocket:

RFC开篇介绍:本协议的目的是为了解决基于浏览器的程序需要拉取资源时必须发起多个HTTP请求和长时间的轮询的问题。

long poll(长轮询): 客户端发送一个request后,服务器拿到这个连接,如果有消息,才返回response给客户端。没有消息,就一直不返回response。之后客户端再次发送request, 重复上次的动作。

图片 1

从上可以看出,http协议的特点是服务器不能主动联系客户端,只能由客户端发起。它的被动性预示了在完成双向通信时需要不停的连接或连接一直打开,这就需要服务器快速的处理速度或高并发的能力,是非常消耗资源的。

这个时候,Websocket出现了。

让浏览器不再显示 https 页面中的 http 请求警报

2015/08/26 · 基础技术 · HTTPS, 浏览器

原文出处: 李靖(@Barret李靖)   

HTTPS 是 HTTP over Secure Socket Layer,以安全为目标的 HTTP 通道,所以在 HTTPS 承载的页面上不允许出现 http 请求,一旦出现就是提示或报错:

Mixed Content: The page at ‘‘ was loaded over HTTPS, but requested an insecure image ‘’. This content should also be served over HTTPS.

HTTPS改造之后,我们可以在很多页面中看到如下警报:

图片 2

很多运营对 https 没有技术概念,在填入的数据中不免出现 http 的资源,体系庞大,出现疏忽和漏洞也是不可避免的。

前言

虽然本文标题为介绍一个水压套圈h5游戏,但是窃以为仅仅如此对读者是没什么帮助的,毕竟读者们的工作生活很少会再写一个类似的游戏,更多的是面对需求的挑战。我更希望能举一反三,给大家在编写h5游戏上带来一些启发,无论是从整体流程的把控,对游戏框架、物理引擎的熟悉程度还是在某一个小难点上的思路突破等。因此本文将很少详细列举实现代码,取而代之的是以伪代码展现思路为主。

游戏 demo 地址:

Websocket是什么:

RFC中写到:WebSocket协议使在控制环境下运行不受信任代码的客户端和能够选择与那些代码通信的远程主机之间能够双向通信。

对,划重点:双向通信

Websocket在连接之后,客户端可以主动发送消息给服务器,服务器也可以主动向客户端推送消息。比如:预订车票信息,除了我们发请求询问车票如何,当然更希望如果有新消息,可以直接通知我们。

其特点:

(1)握手阶段采用 HTTP 协议,默认端口是80和443

(2)建立在TCP协议基础之上,和http协议同属于应用层

(4)可以发送文本,也可以发送二进制数据

(5)没有同源限制,客户端可以与任意服务器通信

(6)协议标识符是ws(如果加密,为wss),如ws://localhost:8023

简单来说,Websocket协议分为两部分:握手和数据传输。

图片 3

CSP设置upgrade-insecure-requests

好在 W3C 工作组考虑到了我们升级 HTTPS 的艰难,在 2015 年 4 月份就出了一个 Upgrade Insecure Requests 的草案,他的作用就是让浏览器自动升级请求。

在我们服务器的响应头中加入:

header("Content-Security-Policy: upgrade-insecure-requests");

1
header("Content-Security-Policy: upgrade-insecure-requests");

我们的页面是 https 的,而这个页面中包含了大量的 http 资源(图片、iframe等),页面一旦发现存在上述响应头,会在加载 http 资源时自动替换成 https 请求。可以查看 google 提供的一个 demo:

图片 4

不过让人不解的是,这个资源发出了两次请求,猜测是浏览器实现的 bug:

图片 5

当然,如果我们不方便在服务器/Nginx 上操作,也可以在页面中加入 meta 头:

XHTML

<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" />

1
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" />

目前支持这个设置的还只有 chrome 43.0,不过我相信,CSP 将成为未来 web 前端安全大力关注和使用的内容。而 upgrade-insecure-requests 草案也会很快进入 RFC 模式。

从 W3C 工作组给出的 example,可以看出,这个设置不会对外域的 a 链接做处理,所以可以放心使用。

1 赞 收藏 评论

图片 6

希望能给诸位读者带来的启发

  1. 技术选型
  2. 整体代码布局
  3. 难点及解决思路
  4. 优化点

Websocket API:

这里是指客户端 API。

技术选型

一个项目用什么技术来实现,权衡的因素有许多。其中时间是必须优先考虑的,毕竟效果可以减,但上线时间是死的。

本项目预研时间一周,真正排期时间只有两周。虽然由项目特点来看比较适合走 3D 方案,但时间明显是不够的。最后保守起见,决定采用 2D 方案尽量逼近真实立体的游戏效果。

从游戏复杂度来考虑,无须用到 Egret 或 Cocos 这些“牛刀”,而轻量、易上手、团队内部也有深厚沉淀的 CreateJS 则成为了渲染框架的首选。

另外需要考虑的是是否需要引入物理引擎,这点需要从游戏的特点去考虑。本游戏涉及重力、碰撞、施力等因素,引入物理引擎对开发效率的提高要大于学习使用物理引擎的成本。因此权衡再三,我引入了同事们已经玩得挺溜的 Matter.js。( Matter.js 文档清晰、案例丰富,是切入学习 web 游戏引擎的一个不错的框架)

WebSocket 构造函数

通过调用WebSocket构造函数来创建一个WebSocket实例对象,建立客户端与服务器的连接。

JavaScript

const ws = new WebSocket('ws://localhost:8023');

1
const ws = new WebSocket('ws://localhost:8023');

整体代码布局

在代码组织上,我选择了面向对象的手法,对整个游戏做一个封装,抛出一些控制接口给其他逻辑层调用。

伪代码:

<!-- index.html --> <!-- 游戏入口 canvas --> <canvas id="waterfulGameCanvas" width="660" height="570"></canvas>

1
2
3
<!-- index.html -->
<!-- 游戏入口 canvas -->
<canvas id="waterfulGameCanvas" width="660" height="570"></canvas>

// game.js /** * 游戏对象 */ class Waterful { // 初始化函数 init () {} // CreateJS Tick,游戏操作等事件的绑定放到游戏对象内 eventBinding () {} // 暴露的一些方法 score () {} restart () {} pause () {} resume () {} // 技能 skillX () {} } /** * 环对象 */ class Ring { // 于每一个 CreateJS Tick 都调用环自身的 update 函数 update () {} // 进针后的逻辑 afterCollision () {} }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// game.js
/**
* 游戏对象
*/
class Waterful {
  // 初始化函数
  init () {}
  
  // CreateJS Tick,游戏操作等事件的绑定放到游戏对象内
  eventBinding () {}
  
  // 暴露的一些方法
  score () {}
  
  restart () {}
  
  pause () {}
  
  resume () {}
  
  // 技能
  skillX () {}
}
/**
* 环对象
*/
class Ring {
  // 于每一个 CreateJS Tick 都调用环自身的 update 函数
  update () {}
  
  // 进针后的逻辑
  afterCollision () {}
}

JavaScript

// main.js // 根据业务逻辑初始化游戏,调用游戏的各种接口 const waterful = new Waterful() waterful.init({...})

1
2
3
4
// main.js
// 根据业务逻辑初始化游戏,调用游戏的各种接口
const waterful = new Waterful()
waterful.init({...})

Websocket事件

WebSocket 是纯事件驱动,通过监听事件可以处理到来的数据和改变的连接状态。服务端发送数据后,消息和事件会异步到达。

  • open:
    服务端响应WebSocket连接请求,就会触发open事件。onopen是响应的回调函数。
JavaScript

// 连接请求open事件处理: ws.onopen = e =&gt; {
console.log('Connection success'); ws.send(`Hello ${e}`); };

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f447934b5b531196143-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f447934b5b531196143-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f447934b5b531196143-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f447934b5b531196143-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f447934b5b531196143-5">
5
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f447934b5b531196143-1" class="crayon-line">
 // 连接请求open事件处理:
</div>
<div id="crayon-5b8f447934b5b531196143-2" class="crayon-line crayon-striped-line">
     ws.onopen = e =&gt; {
</div>
<div id="crayon-5b8f447934b5b531196143-3" class="crayon-line">
         console.log('Connection success');
</div>
<div id="crayon-5b8f447934b5b531196143-4" class="crayon-line crayon-striped-line">
         ws.send(`Hello ${e}`);
</div>
<div id="crayon-5b8f447934b5b531196143-5" class="crayon-line">
     };
</div>
</div></td>
</tr>
</tbody>
</table>

如果要指定多个回调函数,可以使用addEventListener方法。

JavaScript

ws.addEventListener('open', e => { ws.send(`Hello ${e}`); });

1
2
3
ws.addEventListener('open', e => {
  ws.send(`Hello ${e}`);
});

当open事件触发时,意味着握手阶段已结束。服务端已经处理了连接的请求,可以准备收发数据。

  • Message:收到服务器数据,会触发消息事件,onmessage是响应的回调函数。如下:
JavaScript

// 接受文本消息的事件处理: ws.onmessage = e =&gt; { const data =
e.data; if (typeof data === "string") { console.log("Received string
message ",data); } else if (data instanceof Blob) {
console.log("Received blob message ", data); } };

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f447934b62129912854-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f447934b62129912854-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f447934b62129912854-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f447934b62129912854-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f447934b62129912854-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f447934b62129912854-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f447934b62129912854-7">
7
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f447934b62129912854-8">
8
</div>
<div class="crayon-num" data-line="crayon-5b8f447934b62129912854-9">
9
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f447934b62129912854-1" class="crayon-line">
// 接受文本消息的事件处理:
</div>
<div id="crayon-5b8f447934b62129912854-2" class="crayon-line crayon-striped-line">
ws.onmessage = e =&gt; {
</div>
<div id="crayon-5b8f447934b62129912854-3" class="crayon-line">
    const data = e.data;
</div>
<div id="crayon-5b8f447934b62129912854-4" class="crayon-line crayon-striped-line">
    if (typeof data === &quot;string&quot;) {
</div>
<div id="crayon-5b8f447934b62129912854-5" class="crayon-line">
        console.log(&quot;Received string message &quot;,data);
</div>
<div id="crayon-5b8f447934b62129912854-6" class="crayon-line crayon-striped-line">
    } else if (data instanceof Blob) {
</div>
<div id="crayon-5b8f447934b62129912854-7" class="crayon-line">
        console.log(&quot;Received blob message &quot;, data);
</div>
<div id="crayon-5b8f447934b62129912854-8" class="crayon-line crayon-striped-line">
    }
</div>
<div id="crayon-5b8f447934b62129912854-9" class="crayon-line">
};
</div>
</div></td>
</tr>
</tbody>
</table>

服务器数据可能是文本,也可能是二进制数据,有Blob和ArrayBuffer两种类型,在读取到数据之前需要决定好数据的类型。

  • Error发生错误会触发error事件, onerror是响应的回调函数, 会导致连接关闭。
JavaScript

//异常处理 ws.onerror = e =&gt; { console.log("WebSocket Error: " ,
e); handleErrors(e); };

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f447934b66862080563-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f447934b66862080563-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f447934b66862080563-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f447934b66862080563-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f447934b66862080563-5">
5
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f447934b66862080563-1" class="crayon-line">
//异常处理
</div>
<div id="crayon-5b8f447934b66862080563-2" class="crayon-line crayon-striped-line">
ws.onerror = e =&gt; {
</div>
<div id="crayon-5b8f447934b66862080563-3" class="crayon-line">
    console.log(&quot;WebSocket Error: &quot; , e);
</div>
<div id="crayon-5b8f447934b66862080563-4" class="crayon-line crayon-striped-line">
    handleErrors(e);
</div>
<div id="crayon-5b8f447934b66862080563-5" class="crayon-line">
};
</div>
</div></td>
</tr>
</tbody>
</table>
  • Close当连接关闭时触发close事件,对应onclose方法,连接关闭之后,服务端和客户端就不能再通信。

WebSocket 规范中定义了ping 帧 和pong 帧,可以用来做心跳重连,网络状态查询等,但是目前 浏览器只会自动发送pong帧,而不会发ping 帧。(有兴趣可详查ping和pong帧)

JavaScript

//关闭连接处理 ws.onclose = e => { const code = e.code; const reason = e.reason; console.log("Connection close", code, reason); };

1
2
3
4
5
6
//关闭连接处理
ws.onclose = e => {
    const code = e.code;
    const reason = e.reason;
    console.log("Connection close", code, reason);
};

初始化

游戏的初始化接口主要做了4件事情:

  1. 参数初始化
  2. CreateJS 显示元素(display object)的布局
  3. Matter.js 刚体(rigid body)的布局
  4. 事件的绑定

下面主要聊聊游戏场景里各种元素的创建与布局,即第二、第三点。

WebSocket 方法:

WebSocket 对象有两个方法:send 和 close

  • send:客户端和服务器建立连接后,可以调用send方法去发送消息。
JavaScript

//发送一个文本消息 ws.send("this is websocket");

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f447934b6d916593124-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f447934b6d916593124-2">
2
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f447934b6d916593124-1" class="crayon-line">
//发送一个文本消息
</div>
<div id="crayon-5b8f447934b6d916593124-2" class="crayon-line crayon-striped-line">
ws.send(&quot;this is websocket&quot;);
</div>
</div></td>
</tr>
</tbody>
</table>

在open事件的回调中调用send()方法传送数据:

JavaScript

const ws = new WebSocket('ws://localhost:8023'); ws.onopen = e => { console.log('Connection success'); ws.send(`Hello ${e}`); };

1
2
3
4
5
const ws = new WebSocket('ws://localhost:8023');
ws.onopen = e => {
    console.log('Connection success');
    ws.send(`Hello ${e}`);
};

如果想通过响应其他事件发送消息,可通过判断当前的Websocket的readyState属性。接下来会说到readyState.

  • closeclose方法用来关闭连接。调用close方法后,将不能发送数据。close方法可以传入两个可选的参数,code 和reason, 以告诉服务端为什么终止连接。
JavaScript

ws.close(); //1000是状态码,代表正常结束。 ws.close(1000, "Closing
normally");

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f447934b73487491254-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f447934b73487491254-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f447934b73487491254-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f447934b73487491254-4">
4
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f447934b73487491254-1" class="crayon-line">
ws.close();
</div>
<div id="crayon-5b8f447934b73487491254-2" class="crayon-line crayon-striped-line">
 
</div>
<div id="crayon-5b8f447934b73487491254-3" class="crayon-line">
//1000是状态码,代表正常结束。
</div>
<div id="crayon-5b8f447934b73487491254-4" class="crayon-line crayon-striped-line">
ws.close(1000, &quot;Closing normally&quot;);
</div>
</div></td>
</tr>
</tbody>
</table>

一、CreateJS 结合 Matter.js

阅读 Matter.js 的 demo 案例,都是用其自带的渲染引擎 Matter.Render。但是由于某些原因(后面会说到),我们需要使用 CreateJS 去渲染每个环的贴图。

不像 Laya 配有和 Matter.js 自身用法一致的 Render,CreateJS 需要单独创建一个贴图层,然后在每个 Tick 里把贴图层的坐标同步为 Matter.js 刚体的当前坐标。

伪代码:

JavaScript

createjs.Ticker.addEventListener('tick', e => { 环贴图的坐标 = 环刚体的坐标 })

1
2
3
createjs.Ticker.addEventListener('tick', e => {
  环贴图的坐标 = 环刚体的坐标
})

使用 CreateJS 去渲染后,要单独调试 Matter.js 的刚体是非常不便的。建议写一个调试模式专门使用 Matter.js 的 Render 去渲染,以便跟踪刚体的运动轨迹。

WebSocket 属性

  • readyState:

readyState值表示连接状态,是只读属性。它有以下四个值:

WebSocket.CONNECTING :连接正在进行,但还没有建立
WebSocket.OPEN :连接已经建立,可以发送消息
WebSocket.CLOSING :连接正在进行关闭握手
WebSocket.CLOSED :连接已经关闭或不能打开

除了在open事件回调中调用send方法,可通过判断readyState值来发送消息。

JavaScript

function bindEventHandler(data) { if (ws.readyState === WebSocket.OPEN) { ws.send(data); } else { //do something } }

1
2
3
4
5
6
7
function bindEventHandler(data) {
    if (ws.readyState === WebSocket.OPEN) {
        ws.send(data);
    } else {
        //do something
    }
}
  • bufferedAmount:当客户端传输大量数据时,浏览器会缓存将要流出的数据,bufferedAmount属性可判断有多少字节的二进制数据没有发送出去,发送是否结束。
JavaScript

ws.onopen = function () { setInterval( function() {
//缓存未满的时候发送 if (ws.bufferedAmount &lt; 1024 * 5) {
ws.send(data); } }, 2000); };

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f447934b7a325701025-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f447934b7a325701025-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f447934b7a325701025-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f447934b7a325701025-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f447934b7a325701025-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f447934b7a325701025-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f447934b7a325701025-7">
7
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f447934b7a325701025-8">
8
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f447934b7a325701025-1" class="crayon-line">
ws.onopen = function () {
</div>
<div id="crayon-5b8f447934b7a325701025-2" class="crayon-line crayon-striped-line">
    setInterval( function() {
</div>
<div id="crayon-5b8f447934b7a325701025-3" class="crayon-line">
        //缓存未满的时候发送
</div>
<div id="crayon-5b8f447934b7a325701025-4" class="crayon-line crayon-striped-line">
        if (ws.bufferedAmount &lt; 1024 * 5) {
</div>
<div id="crayon-5b8f447934b7a325701025-5" class="crayon-line">
            ws.send(data);
</div>
<div id="crayon-5b8f447934b7a325701025-6" class="crayon-line crayon-striped-line">
        }
</div>
<div id="crayon-5b8f447934b7a325701025-7" class="crayon-line">
    }, 2000);
</div>
<div id="crayon-5b8f447934b7a325701025-8" class="crayon-line crayon-striped-line">
};
</div>
</div></td>
</tr>
</tbody>
</table>
  • protocol:protocol代表客户端使用的WebSocket协议。当握手协议未成功,这个属性是空。

接下来,我们说说握手阶段过程。

当我们创建Websocket实例对象与服务器建立连接时,

JavaScript

const ws = new WebSocket('ws://localhost:8023');

1
const ws = new WebSocket('ws://localhost:8023');

首先客户端向服务器发起一个握手请求,其请求报文的内容如下:

JavaScript

GET /game HTTP/1.1 Host: 10.242.17.102:8023 Cache-Control: no-cache Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Protocol: game Sec-WebSocket-Version: 10 Origin: Accept-Encoding: gzip, deflate, sdch Accept-Language: zh-CN,zh;q=0.8

1
2
3
4
5
6
7
8
9
10
11
GET /game HTTP/1.1
Host: 10.242.17.102:8023
Cache-Control: no-cache
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Protocol: game
Sec-WebSocket-Version: 10
Origin: http://192.168.185.16
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8

从请求头中可以看出,其实是一个基于http的握手请求。与通常的http请求不同的是,增加了一些头信息。

  • Upgrade字段:
    通知服务器,现在要使用一个升级版协议 – Websocket。
  • Sec-WebSocket-Key:
    是一个Base64编码的值,这个是浏览器随机生成,通知服务器,需要验证下是否可以进行Websocket通信
  • Sec_WebSocket-Protocol: 是用户自定义的字符串,用来标识服务所需要的协议
  • Sec-WebSocket-Version: 通知服务器所使用的协议版本

服务器响应:

当服务器返回以下内容,就表示已经接受客户端请求啦,可以建立Websocket通信啦。

JavaScript

HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: SIEylb7zRYJAEgiqJXaOW3V+ZWQ=

1
2
3
4
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: SIEylb7zRYJAEgiqJXaOW3V+ZWQ=
  • 101 状态码,表示要转换协议啦
  • Upgrde:
    通知客户端将要升级成Websocket协议
  • Sec-WebSocket-Accept:
    经过服务器确认,并且加密过后的 Sec-WebSocket-Key。用来证明客户端和服务器之间能进行通信了。

图片 7

至此,客户端和服务器握手成功建立了Websocket连接,通信不再使用http数据帧,而采用Websocket独立的数据帧。


以上是Websocket协议的基础理论篇I, 欢迎小伙伴儿们接力(理论篇II, 实战篇神马的), 一起学习一起积累


1 赞 4 收藏 评论

图片 8

二、环

本游戏的难点是要以 2D 去模拟 3D,环是一点,进针的效果是一点,先说环。

环由一个圆形的刚体,和半径稍大一些的贴图层所组成。如下图,蓝色部分为刚体:

图片 9

伪代码:

JavaScript

class Ring { constructor () { // 贴图 this.texture = new createjs.Sprite(...) // 刚体 this.body = Matter.Bodies.circle(...) } }

1
2
3
4
5
6
7
8
class Ring {
  constructor () {
    // 贴图
    this.texture = new createjs.Sprite(...)
    // 刚体
    this.body = Matter.Bodies.circle(...)
  }
}

三、刚体

为什么把刚体半径做得稍小呢,这也是受这篇文章 推金币 里金币的做法所启发。推金币游戏中,为了达到金币间的堆叠效果,作者很聪明地把刚体做得比贴图小,这样当刚体挤在一起时,贴图间就会层叠起来。所以这样做是为了使环之间稍微有点重叠效果,更重要的也是当两个紧贴的环不会因翻转角度太接近而显得留白太多。如图:

图片 10

为了模拟环在水中运动的效果,可以选择给环加一些空气摩擦力。另外在实物游戏里,环是塑料做成的,碰撞后动能消耗较大,因此可以把环的 restitution 值调得稍微小一些。

需要注意 Matter.js 中因为各种物理参数都是没有单位的,一些物理公式很可能用不上,只能基于其默认值慢慢进行微调。下面的 frictionAir 和 restitution 值就是我慢慢凭感觉调整出来的:

JavaScript

this.body = Matter.Bodies.circle(x, y, r, { frictionAir: 0.02, restitution: 0.15 })

1
2
3
4
this.body = Matter.Bodies.circle(x, y, r, {
  frictionAir: 0.02,
  restitution: 0.15
})

四、贴图

环在现实世界中的旋转是三维的,而 CreateJS 只能控制元素在二维平面上的旋转。对于一个环来说,二维平面的旋转是没有任何意义的,无论如何旋转,都只会是同一个样子。

想要达到环绕 x 轴旋转的效果,一开始想到的是使用 rotation + scaleY。虽然这样能在视觉上达到目的,但是 scaleY 会导致环有被压扁的感觉,图片会失真:

图片 11

显然这样的效果是不能接受的,最后我采取了逐帧图的方式,最接近地还原了环的旋转姿态:

图片 12

图片 13

注意在每个 Tick 里需要去判断环是否静止,若非静止则继续播放,并将贴图的 rotation 值赋值为刚体的旋转角度。如果是停止状态,则暂停逐帧图的播放:

JavaScript

// 贴图与刚体位置的小数点后几位有点不一样,需要降低精度 const x1 = Math.round(texture.x) const x2 = Math.round(body.position.x) const y1 = Math.round(texture.y) const y2 = Math.round(body.position.y) if (x1 !== x2 || y1 !== y2) { texture.paused && texture.play() texture.rotation = body.angle * 180 / Math.PI } else { !texture.paused && texture.stop() } texture.x = body.position.x texture.y = body.position.y

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 贴图与刚体位置的小数点后几位有点不一样,需要降低精度
const x1 = Math.round(texture.x)
const x2 = Math.round(body.position.x)
const y1 = Math.round(texture.y)
const y2 = Math.round(body.position.y)
if (x1 !== x2 || y1 !== y2) {
  texture.paused && texture.play()
  texture.rotation = body.angle * 180 / Math.PI
} else {
  !texture.paused && texture.stop()
}
  
texture.x = body.position.x
texture.y = body.position.y

本文由澳门新葡亰平台官网发布于web前端,转载请注明出处:H5游戏开发,页面中的

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。