开发过程中的杂项笔记

这里主要用来存放值得记录但不成体系或规模的开发笔记。

基于状态同步的技能框架

  1. 状态同步

    1. 为什么是状态同步?
      1. 对网络波动的容忍度高
      2. mmorpg 对实时性的要求不算高
      3. 项目的单位数量并不多
      4. 断线重连处理相对容易
      5. 客户端逻辑性能压力较小
    2. 相应的问题是
      1. 回放不好做
      2. 输入反馈会存在网络延迟
      3. 客服随机数的不一致问题
    3. 各个问题是怎么处理的?
      1. 周期性的全量状态快照 + 全事件记录,期间的重播走插值
      2. 使用预测和预演降低观感上的延迟
        1. 在移动上,使用预测,客户端向服务端发送带指令帧号的输入,服务端下发时附带指令帧号,客户端自行回滚计算。同时服务端在没有接受到新的客户端指令时会保留上一个指令继续执行,直到有新指令或明确的停止指令
        2. 在技能上,直接预演本地玩家技能的表现,如动画和相关特效
        3. 在投射物上,使用预生成,运动模拟是依靠模板执行的。在服务端同步下来以后挂载到客户端预生成的投射物上
      3. 客服使用相同的随机种子
  2. 接口

  3. 事件

  4. 带编辑器的工作流

物理模块

  1. 为什么要自己实现?
    1. 项目的主逻辑是不依赖 unity 组件的框架
    2. 可用的第三方库各有问题或缺点
    3. 太重,不需要那么多的特性
    4. 部分规则需要定制
  2. 实现方式
    1. 基本流程

      1. 粗检测 → 窄检测 → 应用加速度 → 解约束 → 应用速度 → 回调
    2. 粗检测

      1. 【TODO】简单的网格法空间划分
      2. Sweep And Prune
    3. 窄检测使用算法

      1. GJK + EPA
        1. 不使用 SAT 是因为
          1. SAT 的原理:将两个多边形的顶点投影在多边形每条边上,获得投影点的集合后做 SAP
          2. 其原理导致多边形碰撞体的计算效率相对较低,边数较少时则相差不大
          3. 想要准确的碰撞点
          4. SAT 需要单独处理部分图形(圆等)反而不是问题,实际上部分简单图形对会特地拿出来处理以减少计算量
        2. 基于闵可夫斯基差和单纯形的算法原理
          1. 定义一个 support 函数,沿着迭代方向找到两个碰撞形上距离最远的两个点,将两个点相减,获得单纯形上的一个点
          2. 随机选一个初始方向,用 support 函数得到第一个 support 点
          3. 将初始方向取反,作为下一次的迭代方向
          4. GJK 迭代循环:
            1. 用 support 函数获得一个新的 support 点
            2. 若新的 support 点在迭代方向上的点积小于等于零,说明该方向上已经找不到一个可以跨越原点的 support 点了,即不存在一个单纯形能包含原点。两个多边形不相交,退出迭代
            3. 若 support 点达到三个,这三个点组成的单纯形若包含原点,说明出现了碰撞
            4. 否则仅保留离原点最近的 support 边上的两个点
            5. 计算这两个点构成的直线的垂线,垂线取朝向原点的方向作为下一次的迭代方向
            6. 循环
          5. EPA
            1. 目标:获得原点到最近的闵可夫斯基差集多边形边的垂线向量,即为穿透向量

            2. 找到单纯形上离原点最近的边,向外做垂线作为迭代方向扩展单纯形,直到无法扩展为止,最终向量就是穿透向量

            3. 求碰撞点对

              1. 记录支撑点的原始点
                每次调用支撑函数时,保存原始形状中的点:

                s=a−b 其中 a∈A, b∈B

              2. 插值计算碰撞点
                假设 EPA 找到的最近边由支撑点 s1= a1 − b1​ 和 s2 = a2 − b2 组成,且原点在该边上的投影为:

                p = λs1 + (1−λ) s2 (λ∈[0,1])

                对应的原始碰撞点为:

                a = λa1 + (1−λ)a2, b = λb1 + (1−λ)b2

    4. 约束处理

      1. 积分物体速度
      2. 生成碰撞点对、碰撞法线与碰撞深度
      3. 解算物体接触速度约束
      4. 积分物体位置
      5. 解算物体接触位置约束

行为树

遮挡剔除

  1. 空间划分 + Raycast
  2. PVS
  3. SOC

角色属性

  1. cur & next 概念

    1. 保持帧稳定
    2. 规范接口,方便建立统一规则
  2. 角色状态

    1. 引用计数机制
    2. 客户端额外维护一份本地角色状态,只针对自己施加给自己的状态,用来提供及时反馈

空间划分

  • 网格 (O(1))
    • 插入、更新都很快
    • 对于跨越多个网格的物体占用的空间更大
  • 四叉树 (Insert: O(logN), Query: O(n))
    • 动态更新?找到旧位置的节点,移除离开的物体,并考虑这个子树是否需要删除,然后将物体插入树上的新位置
    • 对于在边界上反复横跳的物体会导致频繁更新
  • 松散四叉树
    • 解决反复横跳问题。定义了宽松的出口边界,经验上出口边界的长度是入口边界的 2 倍最好
    • 刚好 2 倍时,会使得一个**“AABB 小于入口边界长度”**的碰撞体的位置在入口范围内时,其 AABB 一定不会超出出口边界。可以以此规划入口边界大小
    • 一种组合用法:网格法 + 松散四叉树,降低查找时间复杂度到 O(1)
  • 八叉树 (Insert: O(logN), Query: O(n))
  • BVH 树

TODO

  • 图形学复习
  • 行为树复习
  • 3C 看一下