技术规格说明书
V1.0
最后修改于2016/10/26
1.简介
1.1 概述
在需求规格确定之后,我们团队根据项目需求对现有的开发技术做了广泛的调查,最终经过选择比较,最终选定将技术栈选用React Native
作为前端的跨平台开发技术,后端服务器部分选用Django
框架作为开发技术。
1.2 前端技术
在当下这个移动互联网的时代,前端技术迭代更新的速度非常快。由于我们应用的性质要求所有用户都下载安装才能进行游戏,而在10人,甚至多达20人的一局游戏中,基本不可能全部都使用Android
或者IOS
平台的手机。所以这就要求我们开发的应用要同时运行在两个平台上。在比较原生(Swift
、Java
)技术和当下的各种跨平台技术(WebApp
)等之后,我们最终选择了2015年Facebook
公司新近推出的React Native
技术来作为前端跨平台开发技术。这种技术利用系统内建的Java Script
解释器来作为桥梁,调用原生系统组件来显示UI,即拥有一定的通用性(Learn once,Write everywhere
)又避免了之前的WebApp
由于全Web端显示而带来的组件臃肿,渲染性能低,用户体验差等缺点。非常适合我们项目这种新创项目的开发,可以复用大量代码,节约相当多的时间成本。
在UI方面,我们选用阿里团队开源的Ant.Design-Mobile
组件库进行基础UI的开发,这是一套同时支持React
移动网页端和React Native
APP端的组件库,封装了开发中常用的40余个组件,整体风格统一,同时也暴露了很多API接口以便进一步定制,对于UI开发经验不多的我们团队来说,是一个很好的上手渠道。
1.3 后端技术
服务器端当前已经有很多成熟的开源框架提供给我们使用,比如基于Ruby
的Rails
、基于Python
的Django
、基于Js
的Express
等等,这些框架都支持了基础的M(odel)-V(iew)-C(ontroller)设计模式,提供了大量的基本功能模块以满足网站服务器开发的各种需求。从这些框架上手可以明显降低服务器后端开发难度。而鉴于我们团队的成员大都都有Python
开发经验,而Django
推出时间也较久,有很多成熟的项目在使用,并且拥有庞大且相当活跃的用户社区,版本迭代也非常迅速及时,且拥有完善的开发文档以供学习,因此我们最终选定使用Django
框架作为后端开发技术。(其实我们最开始也有过使用Node.JS
开发后端的想法,这样的好处是由于前段使用的React Native
也是使用Js
进行开发,这样前后端就可以全栈使用Js
开发,这样会降低整个团队的学习成本,然而后来正式因为Js
高昂的学习成本,我们最后选择了更加容易上手的Django-Python
来进行后端开发)
2.前端技术规格
2.1 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 Native
的Virtual 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
WebSocket
是 HTML5
一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯,它建立在 TCP
之上,同 HTTP
一样通过 TCP
来传输数据,但是它和HTTP
最大不同是:
WebSocket
是一种双向通信协议,在建立连接后,WebSocket
服务器和Browser/Client Agent
都能主动的向对方发送或接收数据,就像Socket
一样;WebSocket
需要类似TCP
的客户端和服务器端通过握手连接,连接成功后才能相互通信。
下面贴两张图来展示WebSocket
和HTTP
请求的区别
这张是传统的HTTP请求
可以看出来,HTTP
请求是短时链接,且服务端不记录客户端的状态。将一次次的请求原子化,服务端在收到客户端的请求后根据请求类型返回相应的数据。这种模式最大的缺点就在于不能从服务端主动向客户端发送数据。
这张是WebSocket
可以看出,在WebSocket
中,客户端与服务端建立的是长链接,服务端可以主动向客户端发送信息,客户端也可以向服务端发送信息。这样就突破了传统HTTP
请求一问一答
式的障碍。
而我们的APP作为某种类型上的网络游戏,需要客户端和服务端数据随时保持一致,对实时性的要求较高。而如果使用传统的HTTP
请求,要实现服务器端数据与客户端数据的实时同步只有采用轮询方法,而这样的轮询对于服务器处理能力以及带宽的要求都是极大的。小规模的应用还能勉强满足,但一旦面对用户量上升的情况,这样的设计是不能满足要求的。
如上图所示,轮询和WebSocket
的性能开销随着用户的增长不断拉大。
因此我们选择采用Websocket作为服务器与客户端通信的方式。
而在Django
框架中,在1.9版本中新增了对于WebSocket
的支持,使用Django channe
l能很方便的通过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 | 猎人杀人 |