Unity3D开发网络游戏
使用工具:MacOS10.15/Unity2019/Rider2020/MySql8.0/Navicat15
完成TCPServer服务端和TCPClient客户端
实现服务端对一个客户端的连接,只能发送接收一条消息
使用异步完成了服务端和多个客户端之间的互连
-
解决粘包、分包问题(使客户端传输数据之前都加上数据长度)
-
测试字节转换
- 完善了客户端发送数据
- 完成了服务端解析数据
- 完成数据库测试
- 构建游戏分层结构
- 开始游戏服务器端GameServer的开发
- 创建Server类,Client类,Message类
- 创建类库Common,开发控制层
- 客户端多个request对应服务端一个controller,一个request负责发起一个请求并处理这个请求的响应
- 采用中介者模式,创建controllerManager管理所有控制器
- 通过ControllerManager进行请求的分发处理
- 客户端请求的响应处理
- client接收消息后转发给ControllerManager进行管理
- Message单一职责原则,只负责处理解析消息
- 完善对消息的处理以及传递给ControllerManager,controller处理完请求之后会有返回值,controllerManager会根据这个返回值判断是否需要给客户端响应。如果不为空,调用server.SendResponse给客户端响应。
-
完成了ConnHelper对数据库的连接,在客户端与服务端建立连接时创建的client中连接数据库,断连时也先断开数据库。
-
开始Unity客户端的开发
-
导入资源(UIFramework框架和Res游戏资源)
-
游戏客户端架构分析图
-
分析游戏客户端架构写成文档(对扩展开放,对修改关闭)
-
Scripts-(Manager/Net) 脚本文件分为两类
-
GameFacade放在场景当中,其余Manager不需要挂载在游戏物体上,由GameFacade直接进行管理
-
修改UIFramework中的UIManager符合现有框架
-
完善了客户端框架
以框架为基础开发游戏,开发ui时是登陆与注册页面,需要发起请求,所以先开发网络模块(与客户端的连接,request的管理)
-
开发ClientManager,完成了与服务器端的连接和关闭
- 为Unity项目更新common.dll动态链接库
- 完善客户端向服务器端发送消息的功能 Message/ClientManager
- 完善接收服务器端的消息并解析
- 修改BaseManager,为它提供一个GameFacade的引用用于各种管理器方便访问GameFacade
- 创建请求基类BaseRequset,用来发起请求。
- 完善了Request对象的管理(修改GameFacade单例模式,中介者模式,gameFacade调用RequestManager,BaseRequest调用GameFacade方法)
-
完善了ClientManager处理响应,中介者模式通过facade调用RequestManager的处理响应方法HandleResponse
完成了前期准备工作:服务器端架构与客户端架构的开发
-
开始游戏逻辑的开发
-
控制场景的视野漫游动画作为菜单界面背景
-
开发登陆按钮
-
设计登录面板
-
设计注册面板和提示信息面板
-
创建面板脚本使他们继承自BasePanel
-
创建面板预制体,修改json文件和枚举类型
-
开发提示信息模块
让Message的方法在其他地方可以进行调用:为BasePanel提供可以访问到UIManager的成员uiMng,使每一个UI面板都可以访问到UIManager,UIManger管理所有面板,面板与面板之间不进行交互,减少耦合。
-
初始化DOTween插件并测试
- 开发游戏开始界面和登录面板进入的动画效果(使用DOTween)
- 动画优化(点击登录按钮,弹出登录面板,登录按钮消失;关闭登录面板,登录按钮显示,重复此过程)
- 修复bug(未控制动画的Normal状态,导致登录按钮点击两次以后无法消失,修改Button,在Normal状态下修改Scale为1,使它保持为1的状态)
- 处理登录请求(在客户端输入用户名和密码,点击登录获取到信息,通过Request发送到服务器端,在服务器端进行校验,正确允许登录,不正确返回错误信息。)
- 设计用户表和战绩表
-
处理客户端LoginPanel登录按钮点击(根据输入错误的用户名和密码反馈不同信息)
-
修改客户端代码(与解析处理相关的所有RequestCode都改成ActionCode,使一个ActionCode对应一个Request类)
-
创建LoginRequest类和RegisterRequest类,分别对应ActionCode中的Login和Register
RequestCode里的User对应UserController
-
在客户端发送登录请求
服务器端创建登录请求对应的RequestCode和ActionCode,添加枚举类型,并在LoginPanel的OnLoginClick()增加发送客户端登录信息
-
创建UserController,完成DAO层与Model层的创建,完成登录信息数据库校验方法
-
在服务器端发送登录的响应,建立返回类型的枚举类,返回字符串
-
在客户端处理登录的响应
-
测试登录流程并处理Bug(从客户端发送请求到服务器端,服务端处理请求给客户端响应,客户端处理响应)
-
注册面板的显示和隐藏设计
- 完成在客户端发送注册请求
- 完成在服务器端处理注册请求(判断用户名是否存在,存在则注册失败,不存在注册成功)
- 在客户端处理注册的响应
- 开发声音管理器
- 开发播放控制按钮点击声音(将GameFacade注入到所有面板中,使它持有播放声音的方法,BasePanel父类持有facade对象,中介者模式实现在其他面板调用播放声音的方法)
-
设计房间面板,显示个人信息(创建房间面板空物体,添加imageUi,左边为个人信息,右边为房间列表)
-
设计房间列表
添加滚动面板,在滚动面板上添加空物体,使用Vertical Layout Group垂直排序
设计房间条目 取消Chilld Force Expand的Heigh取消勾选从上往下排列
为ScrollRect增加mask和image设置与背景色相同,隐藏超出布局的面板
-
开发房间列表的滚动条和按钮
- 控制房间列表的显示,即登录成功后显示面板
- 解决Socket关闭后连接问题(接收之前判断释放,clientSocket等于空或者没有与服务器连接的时候,不再进行接收)
-
面板加载各种动画的开发
调用BeginAccept()修改服务器端进行循环接收客户端消息
登录成功进入房间列表登录面板进行隐藏
循环关闭房间列表弹出登录面板
-
解决页面跳转Bug(登录面板登录成功显示房间列表,关闭房间列表显示登录面板,重复操作两次后,不会弹出登录面板,而是弹出登录按钮)
把OnClick事件放在Start方法里面,保证代码只执行一次。
- 修改LoginPanel和RegisterPanel面板的Start方法
- 建立Model-Result类
- 创建ResultDAO对Result进行增删改查
- 修改服务器端对登录请求的处理UserController
- 在客户端处理响应保存战绩
- 在GameFacade添加用户数据的get与set方法,中介者模式。
-
显示个人战绩
-
创建RoomItem控制一个房间项的显示和点击处理
将RoomItem制作预制体
添加RoomItem自身脚本处理自身显示以及按钮点击
-
动态创建房间列表
在RoomList里处理RoomItem的加载,加载到ScrollRect-Layout-VerticalLayoutGroup
根据房间加载的个数来设置布局的高度
(Anchor只有在父物体的宽高发生变化的时候,才会对自身产生影响)
center修改为pivot,pivot设置y为1,占百分之百高度,解决点击加载房间在屏幕正中间的问题,即修改layout的中心点
center是视觉中心点,pivot是计算坐标的点,是一个比例
- 服务器端创建与房间相关的类与成员
- 制作房间界面UI
-
开发房间面板的动画行为和按钮监听
-
控制房间列表和房间面板的切换显示
监听创建房间按钮
添加UIPanelType枚举类型并修改Json文件
-
发起创建房间的请求,在服务器端保存房间信息
服务器端处理请求
客户端处理响应
-
更改服务器端Client的回调函数(客户端强制关闭时,有时候正在进行结束接收,此时的socket已经关闭的话会出现异常)需要判断client是否为空,是return
控制房间列表的加载,在服务器端获取房间列表信息并包装字符串发送给客户端
-
Client 每次登录战绩既保存到服务器端也保存到客户端,因为需要去的房间信息的时候,此时的战绩是别人的战绩不是自己的,在需要在服务器取到别人的战绩。在client保存两个参数,User和Result,即账号和结果。提供set方法用来设置,在UserController登录时候进行设置。
-
Room提供方法用来得到房主信息
-
将client获取的房主信息组拼成字符串方便传输
-
房间与房间之间的信息用“|”分割,房主信息用逗号分隔,将这个数据返回给客户端,客户端解析字符串创建房间列表(此处对数据进行包装是处理数据末尾的多出来的“|”,用if-else进行判断如果是空串返回0,客户端判断是否为0,如果是0就是空的房间列表,如果有列表就删掉字符串的最后一个字符“|”)
-
在客户端解析房间列表信息
在RoomListPanel发起请求,需要在OnEnter中进行判断,因为OnEnter执行在Start之前,页面重新激活刷新列表重新发起一次请求
RoomListRequest中处理服务器响应,根据列表信息加载房间列表
-
测试加载房间列表UI成功
-
为房间添加id,处理加入按钮点击
加入房间的时候明确自己要加入的房间,将房间id从服务器端传送到客户端,在这里将userid作为房间id,通过房间id区分不同的房间
修改客户端解析代码
-
修改创建房间请求的发起时机为点击创建按钮时创建
进入房间面板有两种方式(1:通过创建房间的方式 2:加入房间)
-
注册刷新按钮完成刷新房间列表功能
-
完善处理客户端与服务器断开连接时,将房间关闭需要判断是否是房主,是房主关闭,不是房主保留
-
加入房间请求的发起和服务器端的处理
-
更新客户端共享工程,在ReturnCode加入了NotFound
-
处理加入房间的响应,显示房间玩家战绩
-
解决bug,当客户端关闭时,房间列表里的房间还在
- 服务器端进行新玩家加入消息的广播(房主创建房间后,玩家加入时显示面板信息,但是房主房间页面并没有显示玩家信息)新的玩家加入到房间的时候,在房间里发送一个BroadcastMessage进行消息的广播,广播给其他玩家的客户端,其他玩家进行房间UI界面的更新
- 在客户端接收广播消息,更新界面。创建UpdateRoomRequest脚本添加在RoomPanel面板上。重写接收服务器响应。
- 处理退出房间功能(房主退出房间,直接直接销毁房间;非房主的话,将玩家移除,房间还在。)
- 房间退出时,更新到其他客户端界面
- 处理房主退出房间的功能(首先向其他客户端发出消息,发出一个QuitRoom,让其他客户端依次退出房间)
- FixBug.当房间销毁以后,非房主刷新房间列表,房间还在。(当返回空的房间列表的时候,data数据为0,这是往LoadRoomItemSync里传递一个空的集合UdList)
- 开始游戏请求的发起和处理(添加新的GameController控制游戏相关)
- 添加倒计时面板
- 处理开始游戏功能--添加游戏显示倒计时功能
- 服务器计时,下发同步到客户端,在服务器端进行开始游戏的计时
- 在客户端显示倒计时效果
-
开发角色的动画状态机(Main Camera设置初始位置)
先完成移动控制,在完成视野跟随,在完成攻击功能
-
控制人物的移动
-
设计玩家射击箭的Prefab
-
控制人物攻击(PlayerAttack不需要挂在人物身上,要使用动态挂载的方式,每一个角色在多个客户端都存在,房主只在其中一个客户端被控制,在另一个客户端被同步。移动与攻击只需要挂在需要控制的人物身上,其他客户端的角色不需要挂载,只进行位置的同步)
角色处于Grounded状态才能进行攻击
-
修改移动为有键按下的时候才能进行移动控制
-
为箭身上添加刚体,通过刚体来控制键的移动,不需要使用重力;为箭添加SphereCollider(球体碰撞器)用来做碰撞检测,通过EditCollider缩小球体碰撞器,移动z轴到箭头的位置
-
创建RoleData管理所有的角色信息,使用RoleData保存角色信息,PlayManager里存放集合保存游戏里有哪些角色,方便的根据角色类型加载角色
-
每一个模型的角色类型、模型所在路径、箭所在路径创建单独的类统一管理
- 完成控制相机跟随角色移动
- 控制相机视野跟随和漫游的切换