bevy examples

bevy examples

  • bevy 栈太深了,system 是并行的,ECS 是非常复杂的模板

bevy ECS

Monday, December 12, 2022
3:08 PM

范式代码示例描述
OOPif A impl Moveable then A.Move(self.Pos, self.Vel) end在执行移动之前,需要检查对象是否具有位置(Pos)和速度(Vel)组件。
ECSforeach Entity with Pos and Vel do Move(Entity) end在ECS中,不需要进行组件检查,因为查询系统只返回具有指定组件的实体。

其实就是模式匹配,当筛选条件足够复杂时,ECS 保持代码清晰,不需要 ifelse check

何为复杂,就是组件的复杂组合,比如 C1 & C2 | C3,上限就是真正的数据库查询,需要专门的query语言SQL query 完了拿到数据再去使用

第二是,这种范式写的代码更加健壮和容易维护
因为每次都是动态查询,不容易漏
EC的做法就是check C,然后缓存C,这种容易break
每次都全量查询刷新,保证正确
而ECS底层的DOD确保了查询非常快(虽然比缓存慢),这个权衡的收益是正的

hello world

fn main() {
    App::new().add_systems(Update, hello_world_system).run();
}

fn hello_world_system() {
    println!("hello world");
}

3d scene

在这里插入图片描述

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    // circular base
    commands.spawn(PbrBundle {
        mesh: meshes.add(Circle::new(4.0)),
        material: materials.add(Color::WHITE),
        transform: Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
        ..default()
    });
    // cube
    commands.spawn(PbrBundle {
        mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)),
        material: materials.add(Color::rgb_u8(124, 144, 255)),
        transform: Transform::from_xyz(0.0, 0.5, 0.0),
        ..default()
    });
    // light
    commands.spawn(PointLightBundle {
        point_light: PointLight {
            shadows_enabled: true,
            ..default()
        },
        transform: Transform::from_xyz(4.0, 8.0, 4.0),
        ..default()
    });
    // camera
    commands.spawn(Camera3dBundle {
        transform: Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
        ..default()
    });
}

breakout

  • 好歹是个完整游戏,有增删查改,模拟和 UI、音效

  • setup 把所有东西都预先 spawn 好

  • 这是 AxB 个 brick

  • spawn bundle 就是 spawn entity

  • bundle 这里是个临时的类型 tuple,brick 和 collider 是 tag

  • collided sfx,类似 event queue 的设计

  • sfx asset 提前加载,存个 handle,这里再 query 出来,这里用 resource 其实就是单例

#[derive(Resource, Deref)]
struct CollisionSound(Handle<AudioSource>);

#[derive(Event, Default)]
struct CollisionEvent;
 
let ball_collision_sound = asset_server.load("sounds/breakout_collision.ogg");
commands.insert_resource(CollisionSound(ball_collision_sound));

mut collision_events: EventWriter<CollisionEvent>,
collision_events.send_default();

fn play_collision_sound(
    mut commands: Commands,
    mut collision_events: EventReader<CollisionEvent>,
    sound: Res<CollisionSound>,
) {
    // Play a sound once per frame if a collision occurred.
    if !collision_events.is_empty() {
        // This prevents events staying active on the next frame.
        collision_events.clear();
        commands.spawn(AudioBundle {
            source: sound.clone(),
            // auto-despawn the entity when playback finishes
            settings: PlaybackSettings::DESPAWN,
        });
    }
}
  • input->move paddle transform,逻辑很简单
  • system 的参数就是函数的输入输出,等系统检测到有这个 pattern 的 input 就调用这个 system 来处理
  • input、time 是单例
  • mut query: Query<&mut Transform, With>,就是获取 paddle 的 transform(可写)
  • ECS 的 data 中,component 是基本单位,bundle 是可嵌套的文件夹,方便整理,实际 entity 有这么些 components,query 时是 flat 的
  • sprite bundle 是渲染对象,有位置组件,直接更新这个
commands.spawn((
    SpriteBundle {
        transform: Transform {
            translation: Vec3::new(0.0, paddle_y, 0.0),
            scale: PADDLE_SIZE.extend(1.0),
            ..default()
        },
        sprite: Sprite {
            color: PADDLE_COLOR,
            ..default()
        },
        ..default()
    },
    Paddle,
    Collider,
));

fn move_paddle(
    keyboard_input: Res<ButtonInput<KeyCode>>,
    mut query: Query<&mut Transform, With<Paddle>>,
    time: Res<Time>,
) {
    let mut paddle_transform = query.single_mut();
    let mut direction = 0.0;
    if keyboard_input.pressed(KeyCode::ArrowLeft) {
        direction -= 1.0;
    }
    if keyboard_input.pressed(KeyCode::ArrowRight) {
        direction += 1.0;
    }
    // Calculate the new horizontal paddle position based on player input
    let new_paddle_position =
        paddle_transform.translation.x + direction * PADDLE_SPEED * time.delta_seconds();
    // Update the paddle position,
    // making sure it doesn't cause the paddle to leave the arena
    let left_bound = LEFT_WALL + WALL_THICKNESS / 2.0 + PADDLE_SIZE.x / 2.0 + PADDLE_PADDING;
    let right_bound = RIGHT_WALL - WALL_THICKNESS / 2.0 - PADDLE_SIZE.x / 2.0 - PADDLE_PADDING;
    paddle_transform.translation.x = new_paddle_position.clamp(left_bound, right_bound);
}
  • 速度积分
fn apply_velocity(mut query: Query<(&mut Transform, &Velocity)>, time: Res<Time>) {
    for (mut transform, velocity) in &mut query {
        transform.translation.x += velocity.x * time.delta_seconds();
        transform.translation.y += velocity.y * time.delta_seconds();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值