技术规格说明书

V1.0

最后修改于2016/10/26

1.简介

1.1 概述

在需求规格确定之后,我们团队根据项目需求对现有的开发技术做了广泛的调查,最终经过选择比较,最终选定将技术栈选用React Native作为前端的跨平台开发技术,后端服务器部分选用Django框架作为开发技术。

1.2 前端技术

在当下这个移动互联网的时代,前端技术迭代更新的速度非常快。由于我们应用的性质要求所有用户都下载安装才能进行游戏,而在10人,甚至多达20人的一局游戏中,基本不可能全部都使用Android或者IOS平台的手机。所以这就要求我们开发的应用要同时运行在两个平台上。在比较原生(SwiftJava)技术和当下的各种跨平台技术(WebApp)等之后,我们最终选择了2015年Facebook公司新近推出的React Native技术来作为前端跨平台开发技术。这种技术利用系统内建的Java Script解释器来作为桥梁,调用原生系统组件来显示UI,即拥有一定的通用性(Learn once,Write everywhere)又避免了之前的WebApp由于全Web端显示而带来的组件臃肿,渲染性能低,用户体验差等缺点。非常适合我们项目这种新创项目的开发,可以复用大量代码,节约相当多的时间成本。

在UI方面,我们选用阿里团队开源的Ant.Design-Mobile组件库进行基础UI的开发,这是一套同时支持React移动网页端和React NativeAPP端的组件库,封装了开发中常用的40余个组件,整体风格统一,同时也暴露了很多API接口以便进一步定制,对于UI开发经验不多的我们团队来说,是一个很好的上手渠道。

1.3 后端技术

服务器端当前已经有很多成熟的开源框架提供给我们使用,比如基于RubyRails、基于PythonDjango、基于JsExpress等等,这些框架都支持了基础的M(odel)-V(iew)-C(ontroller)设计模式,提供了大量的基本功能模块以满足网站服务器开发的各种需求。从这些框架上手可以明显降低服务器后端开发难度。而鉴于我们团队的成员大都都有Python开发经验,而Django推出时间也较久,有很多成熟的项目在使用,并且拥有庞大且相当活跃的用户社区,版本迭代也非常迅速及时,且拥有完善的开发文档以供学习,因此我们最终选定使用Django框架作为后端开发技术。(其实我们最开始也有过使用Node.JS开发后端的想法,这样的好处是由于前段使用的React Native也是使用Js进行开发,这样前后端就可以全栈使用Js开发,这样会降低整个团队的学习成本,然而后来正式因为Js高昂的学习成本,我们最后选择了更加容易上手的Django-Python来进行后端开发)

2.前端技术规格

2.1 React-Redux

React-Redux

React在设计之初就提供了良好的组件化特性,每个View有一个唯一的State与之对应,而这个State会通过属性的形式传给它的子组件,这样一层层的传递使得开发者在开发组件时不用受到其他组件的影响,只需要一次专注于一件事情上,找出组件中会根据不同属性变化的部分,并设计相应的State对应不同的视图变化,这样单向的思维模式大大减少了开发者开发时所需考虑的状态类型,使得开发变得更加容易高效。整个过程就像下图显示的那样,相当于一颗大树从源头汲取养分State并通过管道传送给底层子组件的过程。

而在这种模式下,往往底层的模块接收到用户的操作后会对上层的State进行修改,产生反向数据流这样往往会破坏数据的单向流动,并且不止一处会对上层组件进行修改,当这样的反向数据流越来越多,程序会变得愈加复杂和难以理解,而且会出现很多不可预测的Bug。

于是React-Redux就应运而生,它将将对State的数据处理原子化,并引入的单向数据流的概念,即将对数据的修改不直接操作组件的State,而是将他们通过Reducer收集起来,并把所有对状态的修改放入一个统一的Store中存储,并进行下一个循环的数据流动将修改后的状态再次通过组件关系数输入到各个组件中完成对组件View的修改,就像下图所示

图中的V代表View、A代表Action、R代表Reducer 这样状态的维护就可以统一管理而不必散落在各个View中了,更加符合One at a time的理念。

而具体对于这个模式的代码实现则见下图

其中对数据的修改由Action Creator产生具有唯一类型标示的Action,而其触发相对应的唯一处理器Reducer来对状态进行修改,并将更新后的状态发送到全局的Store中,而全局Store中数据的更新会根据React NativeVirtual DOM机制进行对修改的比较,然后重新渲染发生变化的组件部分,形成一个完整的单项数据回路。这就是整个React-Redux的工作流程。

而在我们的项目中,将完全按照单项数据流的React-Redux模式进行开发,将所有修改页面的Action、处理页面修改并修改属性状态的Reducer、分解好的原子化的组件Component分目录保存,统一管理。下图是我们的前端目录结构

.

├── src #开发目录

| |

| ├──constants #ActionTypes和Urls

| |

| ├──actions #actions的文件

| |

| ├──components #内部组件

| |

| ├──containers #容器组件

| |

| ├──reducers #reducer文件

| |

| ├──stores #store配置文件

| |

| └──utils #工具

|

├── node_modules #包文件夹

├── .gitignore

├── index.js #入口文件

└── package.json

2.2 前端原型图及组件介绍

2.2.1 整体跳转结构

2.2.2 登录界面

以上是我们设计的登录界面的原型图,其中的组件如下

组件 功能
Tabbar 切换登录和注册页面
Textinput 输入用户名和密码信息
Ok 确认按钮,进入主界面

2.2.3 主界面之游戏页面

没有加入或创建游戏的视图

以上是我们设计的游戏页面在没有加入游戏状态的原型图,其中的组件如下

组件 功能
CreateRoom 创建一个新房间
FindRoom 根据Textinput的值确定要查找的房间号
Textinput 输入要查找的房间号

加入游戏后等待开始的视图

以上是我们设计的游戏页面在加入游戏后等待开始的状态的原型图,其中的组件如下

组件 功能
UserList 显示现在房间内的用户
Forward 确认准备状态,进入下一步

房主进行游戏设置的视图

以上是我们设计的游戏页面在房主进行游戏设置状态的原型图,其中的组件如下

组件 功能
RoleList 多个CheckBox组成的列表,选择本次游戏加入的特殊角色
VillagerCount 选择本局游戏中的村民人数
WerewolfCount 选择本局游戏中的狼人人数
ChangeRule 选择游戏胜利模式
Return 退回上一步
Forward 确认设置,进入游戏

玩家查看身份视图

以上是我们设计的游戏页面在进入游戏后玩家查看身份状态的原型图,其中的组件如下

组件 功能
Image 身份图片
Forward 进入下一步

玩家黑夜行使功能的视图

以上是我们设计的游戏页面在玩家黑夜行使功能状态的原型图,其中的组件如下

组件 功能
UserList 显示房间内玩家,存活和已死玩家分别显示
UserCommand 显示玩家特殊功能,行使功能界面
Forward 行使完毕,进入下一步

投票视图

以上是我们设计的游戏页面在玩家投票状态的原型图,其中的组件如下

组件 功能
UserList 显示房间内玩家,存活和已死玩家分别显示
Forward 行使完毕,进入下一步

历史记录功能的视图

以上是我们设计的游戏页面在历史记录功能状态的原型图,其中的组件如下

组件 功能
Result 显示游戏结果
HistoryList 显示历史记录
Forward 查看完毕,结束游戏

2.2.4 主界面之当前的局页面

以上是我们设计的当前的局页面原型图,其中的组件如下

组件 功能
RoomList 显示当前进行的游戏

2.2.5 主界面之我的信息页面

以上是我们设计的我的信息页面的原型图,其中的组件如下

组件 功能
Avatar 我的头像
EditProfile 编辑我的信息
Friends 查看我的好友
History 查看我的战绩
Logout 退出登录

3.后端技术规格

3.1 WebSocket

WebSocketHTML5 一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯,它建立在 TCP 之上,同 HTTP 一样通过 TCP 来传输数据,但是它和HTTP最大不同是:

  • WebSocket 是一种双向通信协议,在建立连接后,WebSocket 服务器和 Browser/Client Agent 都能主动的向对方发送或接收数据,就像 Socket 一样;
  • WebSocket 需要类似TCP的客户端和服务器端通过握手连接,连接成功后才能相互通信。

下面贴两张图来展示WebSocketHTTP请求的区别

这张是传统的HTTP请求

可以看出来,HTTP请求是短时链接,且服务端不记录客户端的状态。将一次次的请求原子化,服务端在收到客户端的请求后根据请求类型返回相应的数据。这种模式最大的缺点就在于不能从服务端主动向客户端发送数据。

这张是WebSocket

可以看出,在WebSocket中,客户端与服务端建立的是长链接,服务端可以主动向客户端发送信息,客户端也可以向服务端发送信息。这样就突破了传统HTTP请求一问一答式的障碍。

而我们的APP作为某种类型上的网络游戏,需要客户端和服务端数据随时保持一致,对实时性的要求较高。而如果使用传统的HTTP请求,要实现服务器端数据与客户端数据的实时同步只有采用轮询方法,而这样的轮询对于服务器处理能力以及带宽的要求都是极大的。小规模的应用还能勉强满足,但一旦面对用户量上升的情况,这样的设计是不能满足要求的。

如上图所示,轮询和WebSocket的性能开销随着用户的增长不断拉大。

因此我们选择采用Websocket作为服务器与客户端通信的方式。

而在Django框架中,在1.9版本中新增了对于WebSocket的支持,使用Django channel能很方便的通过WebSocket的方式与客户端进行通信。

3.2 后端API

3.2.1 Register

类型:POST

传入参数:

参数名 参数类型 说明
Username Str 3-16位字母或数字
Passwd Str 6-16位字母或数字

传出参数:

参数名 参数类型 说明
isSuccess Bool 是否注册成功
Msg Str 成功、重名、用户名不符合规则、密码不符合规则

3.2.2 SignUp

类型:POST

传入参数:

参数名 参数类型 说明
Username Str 3-16位字母或数字
Passwd Str 6-16位字母或数字

传出参数:

参数名 参数类型 说明
isSuccess Bool 是否登录成功
Msg Str 成功、用户名不存在、密码错误
Avatar str 用户头像url
id str 用户唯一id

3.2.3 CreateRoom

类型:POST

传入参数:

参数名 参数类型 说明
id Str 用户id
isVisible bool 是否所有人可见

传出参数:

参数名 参数类型 说明
isSuccess Bool 是否登录成功
Msg Str 成功、创建失败
roomid str 房间id(4位)

3.2.4 FindRoom

类型:GET

传入参数:

参数名 参数类型 说明
id Str 用户id
roomid str 四位数字

传出参数:

参数名 参数类型 说明
isSuccess Bool 是否加入成功
Msg Str 成功、房间不存在、已开始游戏
users array 用户id列表
avatars array 用户头像url列表
usercount int 当前房间用户数
dominid str 房主id

3.2.5 DelRoom

类型:POST

传入参数:

参数名 参数类型 说明
id Str 用户id
roomid str 四位数字

传出参数:

参数名 参数类型 说明
isSuccess Bool 是否删除成功
Msg Str 成功、房间不存在、已开始游戏、不是房主

3.2.6 BlockRoom

类型:POST

传入参数:

参数名 参数类型 说明
id Str 用户id
roomid str 四位数字

传出参数:

参数名 参数类型 说明
isSuccess Bool 是否锁死成功
Msg Str 成功、房间不存在、已开始游戏、不是房主
usercount int 当前房间用户数

3.2.7 ReleaseRoom

类型:POST

传入参数:

参数名 参数类型 说明
id Str 用户id
roomid str 四位数字

传出参数:

参数名 参数类型 说明
isSuccess Bool 是否释放成功
Msg Str 成功、房间不存在、已开始游戏、不是房主
usercount int 当前房间用户数

3.2.8 InitRoom

类型:POST

传入参数:

参数名 参数类型 说明
id Str 用户id
roomid str 四位数字
wolfcount int
seercount int
huntercount int
villagercount int
witchcount int
cupidcount int
guardcount int
rule int 0屠边1屠城

传出参数:

参数名 参数类型 说明
isSuccess Bool 是否开始成功
users array 用户id列表

3.2.9 GetRoomInfo

类型:GET

传入参数:

参数名 参数类型 说明
id Str 用户id
roomid str 四位数字

传出参数:

参数名 参数类型 说明
isSuccess Bool 是否查询成功
Msg Str 成功、房间不存在、不在房间内
usercount int 当前房间用户数
usersinfo dict 保存用户角色,存活,头像,用户名,key为用户id
usersid array 用户id列表
captain str 警长用户id,无则为空
daycount int 游戏轮次

3.2.10 Vote(选择警长、白天投票)

传入参数:

参数名 参数类型 说明
id Str 用户id,(警长算两票)
roomid str 四位数字
ObjectId str 投票对象id

传出参数:

参数名 参数类型 说明
isSuccess Bool 操作是否成功
Msg Str 成功、对象未竞选警长

3.2.11 WolfVote(狼人投票)

传入参数:

参数名 参数类型 说明
id Str 用户id
roomid str 四位数字
ObjectId str 投票对象id
lastObejectId str 上次投票对象id

传出参数:

参数名 参数类型 说明
isSuccess Bool 操作是否成功
Msg Str 成功

3.2.12 WolfCom

传入参数:

参数名 参数类型 说明
id Str 用户id,(警长算两票)
roomid str 四位数字
action str 动作

传出参数:

参数名 参数类型 说明
isSuccess Bool 操作是否成功
Msg Str 成功

3.2.13 NextStep

传入参数:

参数名 参数类型 说明
id Str 用户id
roomid str 四位数字
curState int 当前状态
Action dict 动作,key为动作,value为操作对象

传出参数:

参数名 参数类型 说明
isSuccess Bool 操作是否成功
Msg Str 成功、房间不存在、不在房间内、不存在这样的状态转换
curState int 当前状态

3.3 后端状态跳转列表

状态 描述
0 游戏结束
1 等待用户加入
2 房间锁定
3 用户查看手牌
4 预言家
5 狼人
6 丘比特
7 情侣
8 守卫
9 女巫
10 选择竞选警长
11 竞选警长发言
12 竞选警长投票
13 白天发言
14 白天投票
15 猎人杀人
posted @ 2016-10-26 10:47  buaa_overwatch  阅读(490)  评论(1编辑  收藏  举报