Bevy_behave行为树

行为树是AI的一种,适合npc或敌人的简单智能。

这个库非常棒,实现简单高效,符合作者宣传的简单API.

下面都属于枚举,每个枚举值都是一个一条处理分支。

Match的分支不需要全部返回相同的类型

看下面的代码,感受一下神奇的写法。

match分支返回的是bool,这是明确的,但前两条分支并没有返回什么,而是提前结束了函数。

在常见的match写法中,每个分支的返回值都应该保持一致的类型。但这里打破了这个规则。

传送门

传送门的目的在传送二字上,位置移动是基本属性。下面讨论一下几种位置移动。

  • 关卡之间的移动
    • 如果是单向的,直接重建就行了。
    • 如果是双向的,就需要记住传送门的位置,Entity_ref就很好用
  • 关卡内的移动,不管是单向还是双向,都需要记住位置的

游戏中的“传送门”机制,其本质是空间跳转系统,可能包括视角转换、位置转换、甚至状态同步。 不同类型的游戏中,传送门系统的复杂程度和实现方式差别很大。 以下是业界主流的几种实现方式和思路,按技术复杂度和类型分类讲讲:


  • 代表作:《塞尔达》、《GTA》、《原神》的传送点

  • 实现方法:

    • 触发区域检测(如碰撞盒)
    • 更换玩家位置(player.position = target.position
    • 可选切场景(Unload/Load Scene)
    • 传送后延迟输入、动画、黑屏遮挡等优化体验
  • 代表作:《传送门(Portal)》

  • 实现方法:

    • 渲染视窗穿透效果(Render Texture) 另一端的摄像机渲染结果投射到传送门的表面材质上
    • 空间转换(Transform Matrix) 玩家穿越时,将玩家的局部空间坐标转换到目标门的空间下
    • 多摄像头同步(双向 portal) 避免错位,需要追踪摄像机朝向并同步场景中的穿越视角
  • 代表作:《传送门骑士》、《死亡搁浅》、UE大型开放世界

  • 实现方法:

    • 门后其实是另一片地形的加载区域
    • 利用“世界划分 + 流式加载”(World Partition / Stream)实现无缝加载
    • 门的边缘使用遮挡或狭窄通道隐藏加载过程

  • 碰撞体(trigger volume)
  • 输入行为(交互键触发)
  • 动画中不可传送
  • 传送后恢复状态(坐标、朝向、动画、速度)
  • 状态一致性:传送前广播断开,再加入新场景
  • 服务器端主导传送判断,防止作弊
  • 设置 stencil buffer 分离 portal 和场景
  • 使用裁剪矩阵 / portal clipping planes 防止“错穿”

引擎 支持情况 示例/技术栈
Unity ✅ RenderTexture + 相机同步 可用开源插件如 Portal Render
Unreal Engine ✅ RenderTarget + SceneCapture 官方示例项目即含 Portal 示例
Godot ✅ ViewportTexture + Portal Camera 有社区插件支持
自研引擎/Bevy等 ECS 🟡 需自行处理空间变换与渲染纹理

  • 传送体验要顺畅

角色移动资料收集

游戏角色移动的方案,不管是在独立游戏还是商业大作中,通常都会围绕普通移动(行走、奔跑)跳跃冲刺这几个基本能力进行设计。以下是目前行业里主流与较新方案的系统化分析。

本文聚焦于2d top-down游戏。


  • 优点:支持物理交互、坡道、重力,表现真实。

  • 使用方式

    • 给角色加刚体(Rigidbody),通过 velocityAddForce() 控制移动。
    • Unreal 用 AddMovementInput(),底层已包含加速、滑动、楼梯处理等。
  • 优点:控制更直接,适合 2D 或无物理需求的游戏。

  • 典型用法

    • transform.Translate() 或手动设置 position += speed * deltaTime
  • 用于 AI 或点击移动型角色(RTS、RPG)
  • 结合路径平滑处理 + 动画状态切换
  • 动作动画中包含位移,动画驱动角色移动
  • 常用于 3A 游戏,能确保动画和位置完全一致
  • 缺点:不灵活,难调整实时响应性

  • 使用 velocity.y += jump_force,再交给重力慢慢拉下
  • 优点:自然、支持多段跳,结合动画易实现
  • 按下跳跃时开始计时,持续往上推力,松开跳跃按钮时立刻终止上升

插件如何捕捉用户操作

一个Plugin提供了一些函数接受用户输入,在首帧之前要统计所有用户输入, 一个简单的方法是在Plugin::finish()做处理。

在生态库中很少见到这种需求,只有在复杂生态库才能看到。

在Plugin构建时添加一个保存数据的资源,在所有插件加载完毕后,读取数据并删掉资源。

直接通过world创建实体

system包含world参数属于独占system,为了性能考虑,一般不会大规模在每帧中使用独占system. 抛开这些场景,world还是很好用的.