Organizations

  • 有限状态机作用 按照游戏玩家对象设计基本上有以下状态: Idle(bool): 待机 Move(bool): 移动 Run(bool): 冲刺 Attack(bool): 攻击 Jump(bool): 跳跃 按照简单代码处理, 如果玩家先 移动 再按下 冲刺 的情况, 这时候玩家应该需要做到的是 冲刺 覆盖掉 移动 状态, 而不是 移动 和 冲刺 同时 bool=true. 可以自己编写代码测试或者自己脑子模拟下以下情景: 假设玩家初期带有 移动 状态, 那么玩家移动状态追加 isMove 即可 但是后来玩家又多出个 冲刺 状态, 因为行为是和 移动 互斥必须编写代码判断处于状态中 再后来玩家有多个 飞行(fly) 状态, 又要覆盖编写 if|switch 状态判断切换逻辑 这里如果按照比较初级的游戏逻辑来处理判断代码: // 判断玩家是否可以移动, 不允许玩家处于奔跑和飞行状态 if (!isRun && !isFly) { // 允许移动并切换, 其他UI逻辑代码略 isMove = true; } // 判断玩家是否可以奔跑, 不允许玩家处于飞行状态 if (!isFly) { // 允许奔跑 isRun = true; } // 其他诸如追加传送和闪现的追加行为状态判断 可以看到这里面的状态复杂度及其可怕, 特别判断自身状态情况完全耦合在一起纠缠不清; 而如果要定义这种抢占状态(唯一状态)并发的情况, 衍生出利用 有限状态机(FSM) 处理并发行为状态.
    QFramework Created Sun, 10 Nov 2024 20:16:38 +0800
  • Command 和 Query Command 负责 增|删|改, Query 负责 查, 但是 Query 并不是必选的; 有的时候计算量不是那么重的时候可以直接通过 Model 直接查询数据, 但是复杂跨 Model 数据就依赖 Query. 比如当前有 玩家模型(PlayerModel) 和 敌人模型(EnemyModel), 你需要战斗计算敌人的真实攻击数值; 这个真实攻击数值等于 敌人攻击力减去玩家防御值 的情况, 这时候就需要跨模型查询出 玩家防御值 - 敌人攻击值 抵消的伤害: /// <summary> /// 计算敌人真实伤害 /// </summary> public class EnemyAttackValueQuery : AbstractQuery<int> { protected override int OnDo() { // 获取敌人攻击 - 玩家防御之后真实数值 var v = this.GetModel<EnemyModel>().Atk - this.GetModel<PlayerModel>().Def; return v > 0 ? v : 1; // 最小攻击伤害 1 } } 这种复杂查询的情况就适合 Query 进行发挥; 对于网络游戏来说, 如果需要从服务器同步数据则通过 Query 内部拉取, 而增删改同步到服务器则需要在 Command 做处理
    QFramework Created Sat, 09 Nov 2024 21:35:56 +0800
  • UniTask 说明 虽然 Unity 提供自带协程方案, 但是本身开销庞大且要求 UnityEngine API 必须从主线程调用才能被执行, 也就没有比较好的办法在其他线程调用 UnityEngine API 相关调度方式( 无法脱离 MonoBehaviour 去进行调用任务 ). 所以社区提出了 UniTask 方案, 在保证Unity线程安全的前提下还支持 C# 的异步编程模型,而且不需要依赖 MonoBehaviour 便可以运行. 对于需要用到 Action 回调处理地方, 用 UniTask 处理更加方便 配置安装 这里采用官方的 Git 导出方案, 直接在 Package Manager 点击 add package from git URL 输入以下地址自动安装: https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask 这样默认就在项目组件当中追加好 UniTask 第三方依赖了. 常规使用 UniTask 性能极佳且对第三方插件没有兼容性问题( 如 DoTween ), 这里提供常用到的使用说明: // 使用UniTask所需的命名空间 using Cysharp.Threading.Tasks; // 你可以返回一个形如 UniTask<T>(或 UniTask) 的类型,这种类型事为Unity定制的,作为替代原生Task<T>的轻量级方案 // 为Unity集成的 0GC,快速调用,0消耗的 async/await 方案 async UniTask<string> DemoAsync() { // 你可以等待一个Unity异步对象 var asset = await Resources.
    QFramework Created Sat, 09 Nov 2024 15:36:50 +0800
  • 场景管理 在 Untiy 当中也是需要管理好自身场景, 很多人以为场景切换就是单纯场景资源编号之后调用 Unity 自身的 SceneManager 就行; 实际上场景并非这么简单直接切换, 因为场景还有不少问题需要处理: 是否为独占场景: 需要关闭其他UI独占场面 是否同个分组场景: 有的场景是多个子场景拼接 是否为界面首页: 默认进入游戏的主界面 首页是切换Loading还是Home: 确定启动游戏之后先运行 Loading 还是 Home 其他还有做 ab 包动态下载场景资源的情况 另外需要说明下 Unity 的场景概念很多人混合在一起, 他和 Godot 节点式常见不一样, 需要在菜单栏 File(文件) - BuildSetting(构建设置) 当中去手动或者代码引入对应 Scene 对象场景; 但是有人也喜欢在单一 Scene 之中引入外部预制件并设置 Active 状态让其可用或者不可用唤起, 这种方法好处就是直接单次加载所有预制件直接 Active 切换场景即可, 但是坏处就是某个场景资源十分庞大直接卡死所有其他简单场景. 所以在 Unity 场景分为以下几种意思: Scene: 也就是 BuildSetting 设置的场景, 包括且不限制 登录Scene | 首页Scene | 游戏Scene 等等之类单一场景 Page: 子场景也就是场景内显示的预制体层, 比如触发点击战场情况时候看起来转场到战场地图的 新场景, 同时还有打开背包进入背包菜单界面的情况 注意: 这里后续会大量依赖 QFramework|UniTask|FSM 这些组件, 所以最好对其有基本认识.
    QFramework Created Sat, 09 Nov 2024 14:50:08 +0800
  • DoTween动画 众所周知 Unity 本身具有动画系统, 但是因为其不成熟的体现后续都采用第三方库来处理, 其中最知名的就是 DoTween 插件. DoTween官方网站 DoTween 极可能把功能封装完成简单函数调用, 提供了包含且不限于 线性插值、贝塞尔曲线插值、弹簧插值 等动画插值处理方法. 官方提供的样例也简单, 直接 下载插件 解压放入项目即可进行商业化使用. 插件移动过去之后一键点击配置, 最多需要点击设置 ASMDEF 处理下, 之后就可以开始直接代码配置. 这里集合之前 QFramework 追加事件即可: using System.Collections.Generic; using QFramework; using UnityEngine; using DG.Tweening; // 引入动画系统 // 项目文件: QGame/ViewController/RevolveController.cs namespace QGame.ViewController { /// <summary> /// 旋转事件 /// </summary> public struct RevolveEvent { public bool Stop; public GameObject[] Objects; public float Duration; } /// <summary> /// 旋转控制器 /// </summary> public class RevolveController : MonoBehaviour, IController,IOnEvent<RevolveEvent> { /// <summary> /// 返回引用句柄 /// </summary> /// <returns>IArchitecture</returns> public IArchitecture GetArchitecture() { return QGameApp.
    QFramework Created Thu, 07 Nov 2024 11:54:06 +0800
  • QFramework架构 QFramework 官方样例推荐的架构推荐以 MVC 架构方式构建项目目录, 这里以 Scripts 目录为主要代码基准目录: 以此 Scripts 目录下为主, 同级推荐生成以下目录: ViewController: IController 接口,负责接收输入和状态变化时的表现,一般情况下,MonoBehaviour 均为表现层 System: ISystem 接口,帮助IController承担一部分逻辑,在多个表现层共享的逻辑,比如计时系统、商城系统、成就系统等 Model: IModel 接口,负责数据的定义数据的增删查改方法 Utility: IUtility 接口,负责提供基础设施,比如存储方法、序列化方法、网络连接方法、蓝牙方法、SDK、框架继承等或者第三方库集成 Command: ICommand 接口,核心业务区用于获取对象做游戏逻辑计算, 包括且不限于游戏初始化|关卡进入等 Event: IEvent 接口,统一的全局事件目录, 定义游戏所需的所有调配事件 各层级调用规则: IController 更改 ISystem|IModel 的状态必须用 Command 来处理 ISystem|IModel 状态发生变更后通知 IController 必须用 Event|BindableProperty IController 可以获取 ISystem|IModel 对象来进行数据查询 ICommand 不能有状态和内部属性相关 上层可以直接获取下层, 下层不能获取上层对象, 下层如果想要向上层通信必须用 Event 上层向下层通信用方法调用(只是做查询,状态变更用Command),IController的交互逻辑为特别情况,只能用Command 这里简单以玩家对战的战斗场景来说明: 玩家进入战斗场景, 这时候其实是处理表现层 IController 这时候玩家确认攻击指令, 其实就是调用 ICommand 指令功能 ICommand 接受指令直接先查询玩家 IModel 信息确认玩家攻击力造成的伤害 ICommand 查询到伤害推送给 ISystem 战斗系统确认是否已经打败 ISystem 确认伤害打败怪物采用 IEvent 通知 ICommand 开始进行胜利结算 ICommand 接收到结算的时候, IUtility 负责保存胜利奖励信息和状态信息 上面就是各层级所需的逻辑流程, 后续就是规划具体游戏业务功能.
    QFramework Created Sun, 03 Nov 2024 13:06:06 +0800
  • QFramework初探 在 Unity 当中其实可以从0开始编写内部游戏框架, 但是出于项目加快上线计划从而会采用开源框架, 而这里采用开源社区的 QFramework 来引入. 内部只有 QFramework.cs 单个文件, 直接创建文件夹之后复制黏贴到项目就能直接开始项目搭建. 这里先编写个官方测试样例, 代码之中会具体说明相关的功能作用: using System.Collections.Generic; using QFramework; using UnityEngine; using Random = UnityEngine.Random; // 自定义项目的命名空间, 防止命名空间污染 namespace Examples { /// <summary> /// 挂载的控制器名称, 需要实现 IController 接口 /// </summary> public class QueryModelController : MonoBehaviour, IController { #region 数据模型 /// <summary> /// 生成随机数模型 /// </summary> public class RandomValueModel : AbstractModel { /// <summary> /// 模型初始化, 可以通过本地配置|网络获取到数据 /// </summary> protected override void OnInit() { } /// <summary> /// 模拟服务器|数据库查询获得信息 /// </summary> /// <returns></returns> public int Execute() { return Random.
    QFramework Created Sat, 02 Nov 2024 14:41:21 +0800
  • JPA多数据源配置 Java 的 JPA ORM 框架默认单数据处理, 也就是假设数据库当中有 admin|app|business 这个不同区域数据库的时候就需要自己处理好相关多数据源配置. 这种情况是十分常见的, 在高并发负载的情况下业务会拆分到多个数据源的数据库来暴露给内网服务机连接 首先需要编写 application.properties 配置文件, 改写原来的配置: # 其他略 #================================================================ # Admin库, 注意这里采用 jdbc-url 而不是单纯的 url 配置, 并且采用 MariaDB 驱动而非 MySQL spring.datasource.admin.driver-class-name=org.mariadb.jdbc.Driver spring.datasource.admin.jdbc-url=jdbc:mariadb://127.0.0.1:3306/admin?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=UTC spring.datasource.admin.username=admin spring.datasource.admin.password=admin # App库 spring.datasource.app.driver-class-name=org.mariadb.jdbc.Driver spring.datasource.app.jdbc-url=jdbc:mariadb://127.0.0.1:3306/app?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=UTC spring.datasource.app.username=app spring.datasource.app.password=app # hikari配置 spring.datasource.type=com.zaxxer.hikari.HikariDataSource spring.datasource.hikari.maximum-pool-size=2 spring.datasource.hikari.connection-test-query=SELECT 1 #================================================================ 需要说明如果采用多数据源配置, 应该采用 spring.datasource.数据源KEY标识.driver-class-name 格式来处理, 另外需要注意 spring.datasource.数据源KEY标识.jdbc-url 这个配置采用的是 jdbc-url 而不是 url. 现在就是编写将这些配置注入到 IOC 容器当中配置: package com.meteorcat.sdk.orange.config; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.
    Java Created Thu, 31 Oct 2024 22:13:21 +0800
  • 搭建自我信息茧房 信息茧房 是指人们关注的信息领域会习惯性地被自己的兴趣所引导, 从而将自己的生活桎梏于像蚕茧一般的 "茧房" 中的现象. 在日常自媒体资讯流当中会根据你日常习惯来推送给你所需的相关资讯, 比如喜欢日常浏览 经济 相关新闻会推送时事相关对应的新闻, 而消息提供方之中会将该账号生成 用户个人画像 指明用户希望查看到相关的 经济人士 相关资讯新闻从而等待后续相关新闻推送. 只提取自己感兴趣的新闻就像 茧 当中只看自己相关消息, 这种方式很容易封闭自己的新闻渠道 日常新闻资源得益于 rss 可以采集相关数据从而提取所需的, 这里以 知乎 官方 rss 源做解析: 知乎RSS: https://www.zhihu.com/rss 该接口可以提取出每日的新闻数据, 可以以内部的 原文url 做唯一key标识 这里提供下另外新闻 JSON 格式来做实例, 这解析 RSS 之后数据库入库的消息示例: [ { "author": "zhihu", "link": "https://www.zhihu.com/aaa", "title": "指环王动画电影《指环王:洛汗之战》发布概念海报制作特辑", "content": "测试内容AAA", "key": "2e78390ab87d41ecd3bd1f301c51e8c6", "createAt": 1729328629 }, { "author": "zhihu", "link": "https://www.zhihu.com/bbb", "title": "2024年Q3国产游戏收入创新高,点点互动产品领跑9月出海榜", "content": "测试内容BBBB", "key": "0cc44849bda5d4f6a170515041700248", "createAt": 1729328568 } ] RSS 的数据源可以通过自己编写定时 Python 脚本爬虫抓取解析处理并且做数据库入库处理
    部署 Created Sat, 19 Oct 2024 15:27:44 +0800
  • PWA应用 如果 Chrome 浏览器版本比较新就可以直接让其安装到桌面上作为应用来处理: 无论手机还是桌面浏览器都能将网页安装到桌面从而变成 APP 来使用. 这里实现的方法其实十分简单, 只需要在 html 页面上追加 manifest 声明网页元数据: <head> <link rel="manifest" href="manifest.json"> </head> 这里的 manifest.json 就是声明 PWA 的 JSON 格式配置文件, 实际上就是相当于调用本地浏览器的 Web 应用入口, 也就是 轻应用 的概念的由来, 无须复杂的桌面|移动端开发来构建的应用. manifest.json 配置是所有浏览器通用的, 这里提供 Mozilla 的官方说明: Mozilla Manifest 配置的样例如下: { "name": "HackerWeb", "short_name": "HackerWeb", "start_url": ".", "display": "standalone", "background_color": "#fff", "description": "A simply readable Hacker News app.", "icons": [ { "src": "images/touch/homescreen48.png", "sizes": "48x48", "type": "image/png" }, { "src": "images/touch/homescreen72.
    部署 Created Mon, 14 Oct 2024 14:56:59 +0800