游戏框架-UI管理器

前言 UI一般是入门程序员首先接触到的内容,除了业务的开发,还有很多值得研究的地方。 例如共用的组件设计,渲染的效果(如各种文字效果,在UI上显示模型和特效),性能上的优化(如虚拟的滚动列表,UI的动静分离,图集等),还有UI的管理。 这篇文章就来实现一个简单的UI管理器,主要的类就是管理类和窗口类。 框架功能 打开单例UI 加载 层级管理 显隐管理 UI生命周期管理(初始化,打开后,轮询更新,关闭后,销毁) 类图 管理类 管理类是外部调用的入口,打开和关闭窗口,管理UI的层级以及显示隐藏状态。 打开窗口以后,实例化一个对应的类,调用其加载方法 窗口加载完成或者窗口关闭以后回调到管理类中,遍历窗口列表重新设置其深度值,根据是否有全屏窗口,把比全屏窗口以下的窗口都设置为隐藏。 窗口类 窗口类定义了窗口的特性,如是否全屏,所在层级等,按项目需求添加特性。 窗口类中需要复写其生命周期函数,在合适的时机做合适的事情。 一般在初始化时,初始化组件,打开后,显示内容,关闭或者销毁时,做清理。 另外一个值得注意的时,当UI被隐藏的时候,不是使用SetActive(false),因为这样会引起UI的网格重构,可以把UI移动到一个渲染不到的layer下,或者直接移出相机以外。 打开UI 打开UI时的流程如下 功能拓展 这里只是实现了基础的功能,实际项目中还有许多功能可能用得上,但只需在这基础上拓展即可。 支持面板中的嵌套Prefab:可以在UIWindow给再写一个父类。 打开关闭动画支持:涉及到生命周期的调用实际,需要等待动画完成以后再调用打开关闭方法。 增加缓存:关闭UI时不立即销毁,而且先缓存起来,真正销毁的时机,可以是达到一定的缓存值,或者是存活时机,按实际需要写。 增加通用的遮罩管理。 增加多实例的窗口打开,一个窗口可以多开。 总结 UI管理器基本的功能,以及拓展的功能介绍完了。 实现这些功能不难,但是想要优雅把这些功能都组织起来,让调用简单,代码阅读清晰,也是一门艺术。 参考 UniFramework/UniFramework/UniWindow at main · gmhevinci/UniFramework

2024-7-23 · Dand

PuerTS学习路线

前言 最近看到腾讯又出了个游戏脚本的热更方案,相对lua有以下的优势。 JavaScript生态有众多的库和工具链,结合专业商业引擎的渲染能力,快速打造游戏 相比游戏领域常用的lua脚本,TypeScript的静态类型检查有助于编写更健壮,可维护性更好的程序 安装 安装PuerTS | PUER Typescript 安装包到Unity,在合适的地方接入JSEnv 自定义加载器 PuerTS自带一个加载器,通过Resource加载JS代码,但是实际开发的时候推荐的是TypeScript,需要转译到JS再执行。 编辑器中推荐使用TS-Loader,编辑器中直接使用TS进行开发,没有其他需要关注的地方。 提供一个PuerTS的Loader,使你在Editor下,可以直接读取TS。 无需研究tsconfig、无需研究ESM、CommonJS,无需自行编译ts,无需理会和调试相关的debugpath/sourceMap/控制台跳转。 https://github.com/zombieyang/puerts-ts-loader 编辑器中直接使用TS开发,在发布时还需要编译成JS,参考ts-loader中的TSReleaser-Resources.cs函数,在发布资源前编译成JS。 编译好的JS要支持热更,所以使用默认的加载器,自己定义一个加载器,传入的参数是JS的路径,返回的是JS的代码内容,结合项目的资源加载返回即可,我的Demo使用了YooAssets框架。 调试 用类似NodeJs的方式调试即可。 VSCode Debug 指引 | PUER Typescript TS学习 TS拥有静态类型检测,能编写更具有健壮性的代码,也有更多现有的资源。 深入理解 TypeScript | 深入理解 TypeScript Handbook - The TypeScript Handbook TypeScript 入门教程 ES6 入门教程 前言 | TypeScript手册

2024-7-17 · Dand

Unity使用Socket

前言 之前开发一直都在关注客户端的逻辑,没有涉及到网络相关的知识,这几天参考网上的资料学习了一下做个简易的网络聊天室,记录一下。 简单的网络通信于 Unity 客户端借助 Socket 与服务端达成连接,并依据事先约定的协议(诸如 json、protobuf 等)展开通信。 客户端 客户端的主要功能:连接,发送数据,接收数据。 这里参考开源的简易网络框架,可以自定义其包体,编码器和解码器。 using System.Net; using System.Net.Sockets; using System.Text; using UnityEngine; using UniFramework.Network; // 登录请求消息 class LoginRequestMessage { public string Name; public string Password; } // 登录反馈消息 class LoginResponseMessage { public string Result; } // TCP客户端 UniFramework.Network.TcpClient _client = null; // 创建TCP客户端 void CreateClient() { // 初始化网络系统 UniNetwork.Initalize(); // 创建TCP客户端 int packageMaxSize = short.MaxValue; var encoder = new DefaultNetPackageEncoder(); var decoder = new DefaultNetPackageDecoder(); _client = UniNetwork.CreateTcpClient(packageMaxSize, encoder, decoder); // 连接服务器 var remote = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8000); _client.ConnectAsync(remote, OnConnectServer); } // 关闭TCP客户端 void CloseClient() { if(_client != null) { _client.Dispose(); _client = null; } } void OnConnectServer(SocketError error) { Debug.Log($"Server connect result : {error}"); if (error == SocketError.Success) Debug.Log("服务器连接成功!"); else Debug.Log("服务器连接失败!"); } void Update() { // 每帧去获取解析的网络包 DefaultNetPackage networkPackage = client.PickPackage() as DefaultNetPackage; if(networkPackage != null) { string json = Encoding.UTF8.GetString(networkPackage.BodyBytes); LoginResponseMessage message = JsonUtility.FromJson<LoginResponseMessage>(json); Debug.Log(message.Result); } } // 发送登录请求消息 void SendLoginMessage() { LoginRequestMessage message = new LoginRequestMessage(); message.Name = "hevinci"; message.Password = "1234567"; DefaultNetPackage networkPackage = new DefaultNetPackage(); networkPackage.MsgID = 10001; networkPackage.BodyBytes = Encoding.UTF8.GetBytes(JsonUtility.ToJson(message)); _client.SendPackage(networkPackage); } Buffer格式 // 写入包头 { // 写入消息ID ringBuffer.WriteInt(package.MsgID); // 写入包体长度 ringBuffer.WriteInt(bodyData.Length); } // 写入包体 ringBuffer.WriteBytes(bodyData, 0, bodyData.Length); 协议号:4位 包体长度:4位 内容:其他位,这里可以使用JSON或者Protobuf序列化 服务端 服务端主要功能:管理连接的客户端,接收数据,处理数据,发送数据。 ...

2024-7-17 · Dand

YooAsset学习笔记3:编辑器

前言 上一篇文章简述了YooAsset资源管理框架运行时的框架的使用,接下来通过代码分析其内部原理。 概览 YooAsset框架的编辑器部分包含了四个部分。 收集:通过过滤规则,资源包名,定位地址,划分打包资源。 构建:根据收集到的资源,同时收集其依赖资源,打包成Bundle包。 Reporter:打包后的资源报告。 Debugger:运行时的资源加载情况。 收集 收集器主要通过过滤规则,资源包名,定位地址来划分资源,具体的用法在使用文档有详细介绍,本系列的第一篇也有介绍。 构建 资源构建也是和运行时的设计相似,按步骤构建,流程如下图所示。 构建管线分为内置管线,可编程管线,原生管线,内置管线和可编程管线的构建步骤基本一致,调用的API有不同。原生管线就是直接复制文件,不构建Bundle。 总结 编辑器的框架就是如此,先收集再按步骤构建,再配套Reporter和Debugger进行排查分析。 本系列教程到此结束,感谢阅读。 参考 https://github.com/tuyoogame/YooAsset

2024-6-29 · Dand

YooAsset学习笔记2:运行时

前言 上一篇文章简述了YooAsset资源管理框架的使用,接下来通过代码分析其内部原理。 整体框架 YooAsset框架主要分为运行时和编辑器部分,运行时实现了资源包加载,Bunlde加载,缓存,下载,从上到下的结构。编辑器部分实现了资源收集,资源构建。这篇文章主要分析运行时的机制,结构如下。 加载流程 运行时加载流程主要如下,加载开始以后,每一层的模块基本都是轮询实现。 简单的例子就是调用加载,从上到下,上层等待下层完成自己的步骤,如果完成,再轮询到下个阶段。 每一层根据加载模式和资源类型的不同派生到不同的对象负责轮询,下面的示例是远程加载AssetBundle的示意流程。 根据运行模式初始化包 外部调用加载 调用默认包的加载 Provider轮询,等待Bundle加载完成,再从Bundle中加载具体资源 Loader轮询,等待Bundle加载完成 Downloader轮询,等待下载完成 Requeseter轮询,等待请求完成 资源包(ResourcePackage) 资源包是一个比较上层的概念,在游戏初始化时要初始化包和其加载模式。具体的加载模式可以参照上一篇文章。 定义加载模式 维护版本 维护资源清单 资源管理器(ResourceManager) 资源管理器负责加载具体的资源,根据不同的加载模式和资源类型分成不同的Provider和Loader,并返回对应的Handle。 Provider功能:从加载好的Bundle中加载具体的资源 Loader功能:加载Bundle。 下载系统(DownloadSystem) 下载系统由上层的Loader持有,加载Bundle时,如果还没下载,调用下载系统进行下载。 下载器根据不同的加载模式和资源类型区分,分类普通文件和Web文件,区别在于Web文件不进行缓存。 下载功能由Requester承担,分为普通请求和断点续传。 缓存系统(CacheSystem) 缓存系统是在加载资源时,下载过就不需要再次下载了,直接在本地沙盒目录中读取。 缓存系统由下载系统调用,当下载完成以后,保存本地文件,并记录到缓存系统。 如果已有文件,则进行校验再进行记录。 另一个调用是在资源包初始化后,把已经下载的文件验证一遍,记录到缓存系统中。 后记 运行的框架就是如此,虽然功能很多,但结构清晰,使用了分层处理的思想。 下一篇将介绍编辑器的逻辑,敬请期待。 参考 https://github.com/tuyoogame/YooAsset

2024-6-14 · Dand