ðŠRustðŠ bevyïŒïŒæ¬¡å ã®åºç€
bevy
Rustã«ããã²ãŒã éçºãè©Šããããã°ãçã£å ã«ç®ã«å ¥ãã®ã¯bevyã§ããbevyã¯çŸæç¹ã§ã¡ãããšããã²ãŒã ãšã³ãžã³ã«äžçªè¿ãç¶æ ã«ãããšãããŠããŸããããããUnity颚ã³ã³ããŒãã³ãã·ã¹ãã ã§ã¯ãªãECSã¢ãŒããã¯ãã£ã䜿çšããŸãã
ECS
ãªããžã§ã¯ãæåãšã¯ç°ãªããé¢é£ããç¶æ ãšé¢æ°ããã¹ãŠåã"ãªããžã§ã¯ã"ã«ãŸãšãããã¯ããŸãããE=ãšã³ãã£ãã£ïŒIDçãªãã®ïŒã«ãC=ã³ã³ããŒãã³ãïŒç¶æ ïŒãšS=ã·ã¹ãã ïŒé¢æ°ïŒãå ããããšã§ããã®ãšã³ãã£ãã£ã®æåãå€ããããšãã§ããŸãã
ã·ã¹ãã ãšã¯ãªããªã®ãããæçœã«ããŸããå€æ°ã®ããŒã«ãåŒåã®åœ±é¿ãåããããã«ãPositionãšRigidBodyã³ã³ããŒãã³ããæã£ãŠãããšããŸãã次ã«ãapplyForce()ã·ã¹ãã ã䜿ãããã¬ãŒã ããšã«åŒåã®åœ±é¿ãèšç®ããããŒã«ã®äœçœ®ãæŽæ°ããããšãã§ããŸããã€ãŸããåãã³ã³ããŒãã³ããæã£ãŠãããšã³ãã£ãã£ã«é©çšã§ããé¢æ°ãã·ã¹ãã ãšèšããŸãã
ãŸããã¡ãœããããšã³ãã£ãã£å€ã«å®çŸ©ããããšã§åãé£ç¶ããã¡ã¢ãªé åã«å ¥ãããšã³ãã£ãã£ã®æ°ã¯å€§å¹ ã«å¢å ããŸãããããšãã¡ã€ã³ã¡ã¢ãªããããŒã¿ãåãåºãããã£ãã·ã¥ã«æžã蟌ãé »åºŠã¯å€§å¹ ã«æžå°ããŸããããã§ãã¡ã€ã³ã¡ã¢ãªãžã®ã¢ã¯ã»ã¹ã¯ãã£ãã·ã¥ã®çŽ50å以äžæéããããããšãæãåºããŠãã ããã
ã§ã¯ãæ©ébevyã§ç°¡åãªïŒæ¬¡å ã·ãŒã³ãäœããŸãããã
ã·ãŒã³ãæºå
bevyãããžã§ã¯ãã®ã»ããã¢ããã¯å ¬åŒãµã€ãããåç §ããã ããã°ãšæããŸãã
å žåçãªbevyããã°ã©ã ã¯Appã®å®çŸ©ããå§ãŸããŸãïŒ
fn main() {
App::new().run();
}
ã ããããšã®æç¹ã§ã²ãŒã ãå®è¡ããŠããçã£é»ãªç»é¢ããçŸããŸãããããã«ãã¹ã¿ãŒãã¢ããã·ã¹ãã ãæž¡ããŸãããïŒ
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.run();
}
ãŸãadd_plugins()ã§ããã©ã«ãã®ãã©ã°ã€ã³ãè¿œå ããŸããããããªããšã·ãŒã³ã«ãªããžã§ã¯ããå
¥ããããšã¯ã§ããŸããã
次ã«ãäžã®setup()é¢æ°ã§ã·ãŒã³ãæºåããŸããsetup()ã¯éçºè
ãæå®ãããã®ã§ããŸãã«ã¡ã©ãé
眮ããŸãïŒ
fn setup(mut commands: Commands) {
// ã«ã¡ã©ãè¿œå
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(0.0, 6., 12.0).looking_at(Vec3::new(0., 1., 0.), Vec3::Y),
..default()
});
}
ã€ãŸããcommands.spawn()ã¯Unityã®Instantiate()ã®ããã«ãã²ãŒã ãªããžã§ã¯ãã®çæã«çšããããŸããããã§ã¯ã€ãºã§ãããcommands.spawn()ã«æž¡ãããã®ECSã®ã©ãã§ããïŒ
ãšã³ãã£ãã£ã§ãããããŠãšãã«æž¡ãããtransform
ã¯ãã®ãšã³ãã£ãã£ã®ã³ã³ããŒãã³ãã§ããã
ããŠãã«ã¡ã©ãè¿œå ããããã·ãŒã³ã«å æºã¯ãŸã ãããŸããããã以åãšããŠç»é¢ã¯çã£é»ã§ããåãsetup()é¢æ°ã«æ¬¡ã®ã³ãã³ããè¿œå ããŸãããïŒ
// å
ãè¿œå
commands.spawn(PointLightBundle {
point_light: PointLight {
intensity: 9000.0,
range: 100.,
shadows_enabled: true,
..default()
},
transform: Transform::from_xyz(8.0, 16.0, 8.0),
..default()
});
ã·ãŒã³ã¯å°ãã ãç°è²ã£ãœããªã£ãŠããŸãããçŽ æŽãããïŒ
çã®è¿œå
ãŸãã¯ããŒã«ã空äžã«è¡šç€ºããŸãããã®ããã«ã¡ãã·ã¥ã䜿ããŸããããŸãsetupã®åŒæ°ã«meshesãæž¡ãå¿ èŠããããŸãïŒ
fn setup(..., mut meshes: ResMut<Assets<Mesh>>) {
...
let sphere = meshes.add(shape::UVSphere::default().into());
commands.spawn(PbrBundle {
mesh: sphere,
// ãã®xyzã¯ã«ã¡ã©ã®åããšåã
transform: Transform::from_xyz(0.0, 1.0, 0.0),
..default()
});
}
çã«ã¯ç¢ºãã«è¡šç€ºãããŸããããã³ã¯è²ã«ãªã£ãŠããŸããæ¥çŒãããæ¥ã®äžžã¿ããã§ããã
bevyã®å ¬åŒäŸãåèã«ããã¯ã¹ãã£ãæå®ããŸãïŒ
fn uv_debug_texture() -> Image {
const TEXTURE_SIZE: usize = 8;
let mut palette: [u8; 32] = [
255, 102, 159, 255, 255, 159, 102, 255, 236, 255, 102, 255, 121, 255, 102, 255, 102, 255,
198, 255, 102, 198, 255, 255, 121, 102, 255, 255, 236, 102, 255, 255,
];
let mut texture_data = [0; TEXTURE_SIZE * TEXTURE_SIZE * 4];
for y in 0..TEXTURE_SIZE {
let offset = TEXTURE_SIZE * y * 4;
texture_data[offset..(offset + TEXTURE_SIZE * 4)].copy_from_slice(&palette);
palette.rotate_right(4);
}
Image::new_fill(
Extent3d {
width: TEXTURE_SIZE as u32,
height: TEXTURE_SIZE as u32,
depth_or_array_layers: 1,
},
TextureDimension::D2,
&texture_data,
TextureFormat::Rgba8UnormSrgb,
)
}
çã«ãã¯ã¹ãã£ãé©çšããŸãïŒ
let debug_material = materials.add(StandardMaterial {
base_color_texture: Some(images.add(uv_debug_texture())),
..default()
});
...
PbrBundle {
mesh: sphere,
material: debug_material.clone(), // ãã¯ã¹ãã£ãæž¡ã
transform: Transform::from_xyz(0.0, 1.0, 0.0),
..default()
},
ããã§çãèŠããŠããŸãããæåŸã«ãDefaultPluginsã«ImagePlugin::defaul_nearest()ãæž¡ãããšã§ãè²ãæ£ãã衚瀺ãããŸãã
App::new()
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
...
èœã¡ã
çãèŠããŠããŸããããä»ã«äœãèµ·ãããããŸãé¢çœãã¯ãããŸããããã®ã·ãŒã³ãå°ã楜ããããããã«ãçããšããããèœäžãããŸãããã
ãã®ããã«ç©çãšã³ãžã³ãå¿
èŠã«ãªããŸãããbevyã«ã¯ç¬èªã®ç©çãšã³ãžã³ããããŸããã
ãbevy physics engineããæ€çŽ¢ããã°bevy rapierãbevy xpbdãåºãŠãããšæããŸãã®ã§ãä»åã¯åŸè ãæ¡çšããŸããå ã®ãã¡ã€ã«ã«æ»ããšïŒ
use bevy_xpbd_3d::prelude::*;
fn main() {
App::new()
.add_plugins(PhysicsPlugins::default())
...
}
ããã§ãã©ã°ã€ã³ãè¿œå ããŸãããããšã¯çã«RigidBody::Dynamicã³ã³ããŒãã³ããã€ããã°ãã¡ãããšèœäžããŠãããã¯ãã§ãã
// ã¿ãã«ã䜿ã£ãŠè€æ°ã®ã³ã³ããŒãã³ããæå®ã§ãã
commands.spawn((
RigidBody::Dynamic,
PbrBundle {
mesh: sphere,
material: debug_material.clone(),
transform: Transform::from_xyz(0.0, 1.0, 0.0),
..default()
},
));
ãããããã§ãèœäžããŸãããã³ã³ãœãŒã«ãèŠãã°ãèŠåã衚瀺ãããŸãïŒ
Dynamic rigid body 1v0 has no mass or inertia. This can cause NaN values. Consider adding a `MassPropertiesBundle` or a `Collider` with mass.
ã€ãŸããRigidBodyã ãã§ééå±æ§ã¯ãŸã ãããŸããã®ã§ãMassPropertiesBundleãColliderãå¿ èŠãªããã§ããããšã§å¿ èŠã«ãªãããColliderã䜿ããŸãã
commands.spawn((
RigidBody::Dynamic,
Collider::ball(1.0), // 1.0ã¯Colliderã®ååŸ
PbrBundle {
mesh: sphere,
material: debug_material.clone(),
transform: Transform::from_xyz(0.0, 1.0, 0.0),
..default()
},
));
ä»åã¯ã¡ãããšèœäžããŠãããŸãããã ãããããããŸãããã§ãã®ãŸãŸèŠéããæ¶ããŠããŸããŸããåºãæºåããŸãããïŒ
commands.spawn((
RigidBody::Static,
Collider::cuboid(side_len, 0.002, side_len),
PbrBundle {
mesh: meshes.add(
shape::Plane::from_size(side_len)
.into(),
),
material: materials.add(Color::SILVER.into()),
transform: Transform::from_xyz(0., -1.5, 0.),
..default()
},
));
ä»åã¯åºã«RigidBodyãšColliderã³ã³ããŒãã³ããå ããã®ã¯çããã åºãéãéããªãããã«ããããã§ããããšã¯ãRigidBody::Staticã§ãªããšåºãèœäžããŸãã®ã§ã泚æé¡ããŸãã
ããŸãïŒã²ãŒã ãšãã£ã¿
å ¬åŒãªbevyãšãã£ã¿ã¯ãŸã ãããŸããã代ããã«bevy editor plsã䜿ããŸããUnityãGodotãUE5ãªã©ã®ãšãã£ã¿ã«ã¯çšé ãã§ãããåºæ¬çãªæ©èœãåããŠããŸããè¿œå ã¯ä»ã®ãã©ã°ã€ã³ãšåãïŒ
use bevy_editor_pls::prelude::*;
fn main() {
App::new()
.add_plugins(EditorPlugin::default())
...
}
ã²ãŒã ãå®è¡ããã°ãç»é¢ã®å·Šäžã«ãOpen windowãã®é£ã«ããŒãºãã¿ã³ãçŸããã¯ãªãã¯ãããšãšãã£ã¿ãå±éãããŸããã·ãŒã³ã®ãªããžã§ã¯ãã®ããããã£ã確èªã»å€æŽããããã®ã¡ãã¥ãŒã®ã»ããã²ãŒã ãã¥ãŒãå³ã¯ãªãã¯ããªããåããã°è§åºŠã®å€æŽããããŠã¹ã®ã¹ã¯ããŒã«ã§ãºãŒã ãã§ããŸãã
ãŸãšã
ããã§bevyãšã³ãžã³ã®åºæ¬ã®çŽ¹ä»ã¯å®äºãšãªããŸãããã¡ãããä»åæ§ç¯ããã·ãŒã³ã¯éåžžã«ã·ã³ãã«ãªã®ã§bevyã®æ©èœã®ããäžéšã«ãã觊ããŸããã§ãããä»ã®æ©èœã䜿ããããè€éãªãªããžã§ã¯ããçæããããšãããã£ãããã¬ã³ããŒã·ã¹ãã ãè©ŠããŠãããŸãããã
Discussion