1 个不稳定版本
0.2.2 | 2024年5月6日 |
---|
#12 在 #ryot 中
用于 ryot_tiled
120KB
2K SLoC
Ryot 轨迹
什么是Ryot轨迹?
Ryot轨迹利用光线投射的概念,为Bevy提供强大的轨迹系统。它旨在与Bevy的ECS无缝协作,使开发者能够在需要精确轨迹计算、支持障碍物、视线和其他复杂交互的游戏和模拟中实现基于轨迹的高级机制。尽管它针对2D网格环境进行了优化,但它可以轻松扩展以满足特定的游戏需求和开放世界场景。它是Ryot框架的一部分,依赖于Ryot Core和Ryot Utils。
光线投射和轨迹
轨迹是游戏开发中的基本概念,使复杂的游戏机制,如抛物线运动、视线、迷雾、碰撞检测等成为可能。光线投射是一种通过在2D或3D环境中追踪光线来模拟轨迹的技术,并在途中检测碰撞和交互。它在游戏中被广泛用于实现逼真的物理、照明效果和AI行为,为创建引人入胜的游戏体验提供了一种通用的工具。
在Ryot轨迹的上下文中,为在Bevy项目中实现轨迹系统提供了一个全面的解决方案,提供了一个灵活且可扩展的框架来处理轨迹逻辑。通过利用ECS架构和与Bevy的无缝集成,开发者可以创建动态的轨迹系统,以适应不断变化的游戏条件和玩家交互。
轨迹使用Bevy RayCast3d作为底层光线投射库,用于2D和3D环境。
功能
- 无缝Bevy集成:旨在与Bevy的ECS协同工作,提供平滑的集成并确保与Bevy的事件系统的兼容性。
- 光线投射支持:利用光线投射模拟轨迹,实现精确的碰撞检测和与障碍物的交互。
- 2D优化:特别针对2D网格导航进行优化,为基于瓦片和开放世界游戏环境提供强大的工具。
- 可扩展架构:设计用于灵活,允许开发者扩展和自定义轨迹逻辑以适应特定的游戏需求。
基本设置
在设置轨迹框架之前,让我们理解核心概念: Point
、TrajectoryPoint
、Navigable
、RadialArea<P>
、Perspective<P>
和 Trajectory<T, P>
。
Point
Point 特性代表世界中的位置。它是 Ryot 生态系统的核心概念,允许您将您自己的世界表示与 Ryot 及其空间算法集成。
TrajectoryPoint
Point 特性的扩展,TrajectoryPoint 特性代表世界中可以用于计算轨迹的位置。它用于生成给定空间中点的边界框,用于检查点是否与射线投射相交。这个包主要使用 aabb3d(轴对齐边界框)来表示空间中点的边界框,并使用射线投射 aabb 交集来检查点是否在轨迹内或不在。
Navigable
Navigable 特性属于 ryot_core
,用于确定一个点是否可导航。它用于确定演员是否可以穿越世界中的特定点,例如这个点是否可通行。
目前,Navigable 有两个标志:is_walkable
和 is_flyable
。第一个用于确定演员是否可以穿越点,第二个用于确定演员是否可以穿越点。
RadialArea
RadialArea 结构是游戏世界中区域的描述性表示。它只包含原始和可复制的类型,实现了 Hash,其主要目的是用作视角的描述性表示,允许缓存复杂计算的视角并在将来重用。
Perspective
Perspective 结构是从给定的观众点获取的视角的表示。它包含一个遍历数组,遍历是 RayCast3d 和射线穿越的区域元组。它用于表示观众可以在特定场景/条件下从空间中的给定点看到的所有轨迹。
Trajectory
轨迹结构是游戏世界中轨迹的表示。它是可以附加到 ECS 中的实体上的组件,用于表示实体的轨迹请求。它包含轨迹的径向区域、轨迹必须满足的条件、可以共享轨迹的实体,以及一组可以用于自定义轨迹计算的参数。
Bevy
要集成 ryot_trajectories
,您需要将轨迹添加到您的 Bevy 应用中。这是通过调用 add_trajectory<T, P, N>
来完成的,其中 T
是表示轨迹上下文的标记类型,P
是轨迹点类型,N
是可导航类型。此方法是 Bevy 应用构建器上的构建器方法。
以下是一个基本示例
use bevy::prelude::*;
use ryot_core::prelude::*;
use ryot_trajectories::prelude::*;
fn setup<P: TrajectoryPoint + Component>(mut commands: Commands) {
// here we use () as a marker, but in a real scenario you should use a marker type
// that properly represents the context of the trajectory.
commands.spawn(Trajectory::<(), P>::default());
}
fn build_app<P: TrajectoryPoint + Component>(app: &mut App) -> &mut App {
app
.add_plugins(DefaultPlugins)
.add_trajectory::<(), P, Flags::default()>()
}
组件
这个包有两个主要的 ECS 组件
TrajectoryRequest<T, P>
此组件连接到需要轨迹计算的实体。它指定了轨迹算法的参数
- 区域:表示轨迹将覆盖的径向区域。
- 共享给:轨迹可以与之共享的实体。
- 条件:轨迹必须满足的条件,基于可导航类型和位置。
- 参数:一组可用于自定义轨迹计算的参数。
- 最大碰撞次数:在这个轨迹中发射的射线可以有的最大碰撞次数。
- 反向:如果轨迹应以反向顺序(从末端到起点)进行分析。
- 执行类型:轨迹应具有的执行类型:一次或基于时间。
- 上次执行时间:轨迹上次执行的时间,一个标志,用于确定是否再次执行轨迹。
它是公共API的一部分,应由用户用于触发轨迹计算。
轨迹结果<T, P>
此组件连接到已完成轨迹计算的实体。它包含轨迹计算的结果,表示为
- 碰撞:轨迹与世界的碰撞,意味着在这些点上轨迹不可导航。
- 位置:碰撞发生的位置。
- 距离:从轨迹起点到碰撞的距离。
- 前一个位置:碰撞前的轨迹位置。
- 穿透:碰撞是否穿透,意味着轨迹在碰撞后继续。
- 感兴趣区域:轨迹的感兴趣区域,意味着在这些点上轨迹是可导航的,可以影响世界。
此组件连接到已完成轨迹计算的实体。它是公共API的一部分,应由用户用于检查轨迹结果。
系统
轨迹框架由三个主要系统组成
update_intersection_cache<T, P>
:此系统更新由TrajectoryRequest<T, P>组件中存在的径向区域表示的交叉缓存。此缓存用于加速轨迹计算,避免重新计算已计算过的射线投射aabb交叉。process_trajectories<T, P, N>
:轨迹框架的主系统,它执行ECS中存在的轨迹请求,计算轨迹并将结果附加到实体上。share_results<T, P>
:此系统将实体的轨迹结果与轨迹可以共享的实体共享。
还有两个系统是清理过程的一部分
remove_stale_results<T, P>
:此系统删除不再与轨迹请求关联的轨迹结果。remove_stale_trajectories<T, P>
:此系统删除不再有效的轨迹请求。
示例
根据您的需求选择一个示例来运行,例如处理多个实体或处理障碍物
cargo run --example example_name --features stubs
将example_name替换为您希望运行的示例名称。
理解示例
库中包含的每个示例都展示了轨迹系统的不同方面
- 基本:演示了一个基本的完整轨迹用例,包括障碍物和不同的径向区域。
- 压力测试:评估轨迹在高负载条件下的性能。
构建自己的场景
利用ExampleBuilder
来自定义和创建定制的轨迹示例/测试场景
fn main() {
// ExampleBuilder::<T /* Contextual Marker */, P /* TrajectoryPoint */, N /* Navigable */>::new()
// .with_trajectories(/* array of (trajectory, count) tuples, containing the trajectories to be instantiated and how many */)
// .with_obstacles(/* number of obstacles to be instantiated */)
// .app() // basic app with visual capabilities
// /* add your custom systems, plugins and resources here */
// .run();
}
基准测试
包括性能基准测试,以提供对库效率的洞察。可以在各种条件下运行基准测试来评估性能
cargo bench --features stubs
结果
轨迹系统有三个主要基准:创建轨迹、执行轨迹和检查可导航点与轨迹。基准覆盖不同的场景,如线性、扇形和圆形区域,以及不同的范围值。
以下表格提供了轨迹系统基准测试结果的概述
创建
测试名称 | 类型 | 范围(距离) | 时间(ns/iter) | 可变性(± ns) | 每秒迭代次数(iters/s) |
---|---|---|---|---|---|
create_linear_range_10 | 线性 | 10 | 143 | 3 | 6,993,007 |
create_linear_range_100 | 线性 | 100 | 821 | 114 | 1,218,027 |
create_linear_range_255 | 线性 | 255 | 1,337 | 197 | 747,951 |
create_45_degrees_sector_range_10 | 径向_45 | 10 | 1,160 | 12 | 862,069 |
create_45_degrees_sector_range_100 | 径向_45 | 100 | 17,142 | 409 | 58,358 |
create_45_degrees_sector_range_255 | 径向_45 | 255 | 29,580 | 830 | 33,822 |
create_90_degrees_sector_range_10 | 径向_90 | 10 | 2,734 | 112 | 365,632 |
create_90_degrees_sector_range_100 | 径向_90 | 100 | 34,297 | 883 | 29,159 |
create_90_degrees_sector_range_255 | 径向_90 | 255 | 59,535 | 2,329 | 16,802 |
create_circular_range_3 | 圆形 | 3 | 1,871 | 28 | 534,759 |
create_circular_range_5 | 圆形 | 5 | 4,724 | 74 | 211,640 |
create_circular_range_10 | 圆形 | 10 | 9,819 | 292 | 101,844 |
create_circular_range_25 | 圆形 | 25 | 38,055 | 769 | 26,284 |
create_circular_range_50 | 圆形 | 50 | 81,998 | 2,237 | 12,195 |
create_circular_range_100 | 圆形 | 100 | 143,330 | 2,569 | 6,979 |
create_circular_range_255 | 圆形 | 255 | 277,505 | 40,670 | 3,605 |
执行
测试名称 | 类型 | 范围(距离) | 时间(ns/iter) | 可变性(± ns) | 每秒迭代次数(iters/s) |
---|---|---|---|---|---|
execute_linear_range_10 | 线性 | 10 | 95 | 1 | 10,526,316 |
execute_linear_range_100 | 线性 | 100 | 1,169 | 32 | 855,048 |
execute_linear_range_255 | 线性 | 255 | 2,783 | 349 | 359,323 |
execute_45_degrees_sector_range_10 | 径向_45 | 10 | 602 | 6 | 1,660,798 |
execute_45_degrees_sector_range_100 | 径向_45 | 100 | 23,884 | 666 | 41,866 |
execute_45_degrees_sector_range_255 | 径向_45 | 255 | 60,248 | 897 | 16,600 |
execute_90_degrees_sector_range_10 | 径向_90 | 10 | 1,227 | 29 | 815,073 |
execute_90_degrees_sector_range_100 | 径向_90 | 100 | 47,821 | 972 | 20,914 |
execute_90_degrees_sector_range_255 | 径向_90 | 255 | 121,197 | 25,467 | 8,250 |
execute_circular_range_3 | 圆形 | 3 | 920 | 77 | 1,086,957 |
execute_circular_range_5 | 圆形 | 5 | 2,034 | 123 | 491,699 |
execute_circular_range_10 | 圆形 | 10 | 5,074 | 215 | 197,203 |
execute_circular_range_25 | 圆形 | 25 | 27,759 | 923 | 36,020 |
execute_circular_range_50 | 圆形 | 50 | 92,329 | 1,405 | 10,828 |
execute_circular_range_100 | 圆形 | 100 | 199,025 | 3,846 | 5,025 |
execute_circular_range_255 | 圆形 | 255 | 812,311 | 28,281 | 1,231 |
可导航碰撞
测试名称 | 类型 | 范围(距离) | 时间(ns/iter) | 可变性(± ns) | 每秒迭代次数(iters/s) |
---|---|---|---|---|---|
check_1million_obstacles_against_line_range_15 | 线性 | 15 | 44 | 1 | 22,727,273 |
check_1million_obstacles_against_line_range_50 | 线性 | 50 | 267 | 8 | 3,745,318 |
check_1million_obstacles_against_line_range_100 | 线性 | 100 | 585 | 15 | 1,709,402 |
check_1million_obstacles_against_line_range_255 | 线性 | 255 | 1,699 | 38 | 588,581 |
check_1million_obstacles_against_45_degrees_sector_range_15 | 径向_45 | 15 | 612 | 21 | 1,633,987 |
check_1million_obstacles_against_45_degrees_sector_range_50 | 径向_45 | 50 | 6,660 | 1,812 | 150,150 |
check_1million_obstacles_against_45_degrees_sector_range_100 | 径向_45 | 100 | 17,077 | 612 | 58,545 |
check_1million_obstacles_against_45_degrees_sector_range_255 | 径向_45 | 255 | 53,496 | 8,487 | 18,692 |
check_1million_obstacles_against_90_degrees_sector_range_15 | 径向_90 | 15 | 1,339 | 117 | 746,808 |
check_1million_obstacles_against_90_degrees_sector_range_50 | 径向_90 | 50 | 13,385 | 405 | 74,706 |
check_1million_obstacles_against_90_degrees_sector_range_100 | 径向_90 | 100 | 40,414 | 1,383 | 24,742 |
check_1million_obstacles_against_90_degrees_sector_range_255 | 径向_90 | 255 | 108,612 | 4,525 | 9,209 |
check_1million_obstacles_against_circle_range_15 | 圆形 | 15 | 5,136 | 55 | 194,748 |
check_1million_obstacles_against_circle_range_50 | 圆形 | 50 | 67,538 | 2,029 | 14,810 |
check_1million_obstacles_against_circle_range_100 | 圆形 | 100 | 161,176 | 3,945 | 6,206 |
check_1million_obstacles_against_circle_range_255 | 圆形 | 255 | 437,340 | 10,785 | 2,287 |
此README格式清晰地划分了功能、示例用法和基准测试,为任何希望将ryot_trajectories
库集成到其项目中的用户提供了一个全面的指南。
依赖项
~17–59MB
~1M SLoC