Steel教程 - 玩家控制

玩家控制

本章我们来编写板子的控制逻辑。

Player组件

板子作为玩家操作的对象,我们可以创建一个组件Player,来标记这个对象,并且添加一个成员变量speed来设定控制的速度:

#[derive(Component, Edit, Default)]
struct Player {
    move_speed: f32,
}

Player作为一个组件,首先需要实现Component。为了让我们的编辑器可以添加/删除/修改Player组件,需要实现Edit。实现了Edit组件也必须实现Default,这是为了编辑器添加这个组件时可以为其设置一个默认值。

Player组件写好了后,只需要在SteelApp中注册一下,就可以在编辑器里面使用了:

#[no_mangle]
pub fn create() -> Box<dyn App> {
    SteelApp::new()
        .add_plugin(Physics2DPlugin)
        .register_component::<Player>()
        .boxed()
}

完成代码修改后,点击顶部菜单的“Project -> Compile”按钮编译。编译完成后,就可以给我们的Board实体添加一个Player组件了,并设置move_speed为10,然后点击顶部菜单的“Scene -> Save”保存一下场景即可。

玩家控制系统player_control_system

接下来我们想要通过键盘改变Board实体的移动速度来操作它,那么它就不应该受到重力而下落,也不应该受到任何其他的力而改变速度,此时我们可以把它的RigidBody2D组件的body_type改成KinematicVelocityBased,这样可以确保它的速度由我们的代码控制。

实现系统player_control_system:

fn player_control_system(
    player: View<Player>,
    mut transform: ViewMut<Transform>,
    rb2d: View<RigidBody2D>,
    mut physics2d_manager: UniqueViewMut<Physics2DManager>,
    input: UniqueView<Input>,
) {
    for (player, transform, rb2d) in (&player, &mut transform, &rb2d).iter() {
        if let Some(rb2d) = physics2d_manager.rigid_body_set.get_mut(rb2d.handle()) {
            let mut linvel = Vec2::ZERO;
            if input.key_held(VirtualKeyCode::Left) {
                linvel = Vec2::new(-player.move_speed, 0.0);
            } else if input.key_held(VirtualKeyCode::Right) {
                linvel = Vec2::new(player.move_speed, 0.0);
            }
            rb2d.set_linvel(linvel.into(), true);
        }
    }
}

我们遍历所有存在Player组件、Transform组件和RigidBody2D组件的实体,场景中这样的实体一定是我们的Board,因此for循环应该只会执行一次。通过physics2d_manager.rigid_body_set.get_mut方法,传入RigidBody2D组件的handle,可以得到物理世界中的RigidBody对象。Input单例提供了读取当前的按键情况,其key_held函数可以查询当前某个键是否被按下了。如果当前键盘按下了左键或右键,我们通过RigidBody的set_linvel方法设置一个向左或向右的速度。这样就实现了对Board的操控。

我们将player_control_system放在Schedule::Update阶段运行即可,因为这个阶段会在编辑器中未运行游戏的时候跳过,我们不希望在游戏未运行的时候就能操控板子:

#[no_mangle]
pub fn create() -> Box<dyn App> {
    SteelApp::new()
        .add_plugin(Physics2DPlugin)
        .register_component::<Player>()
        .add_system(Schedule::Update, player_control_system)
        .boxed()
}

此时尝试运行游戏,按键盘左右按钮,可以控制Board左右移动了。但是很快你会发现一个问题,Board可以移动到屏幕外。为了解决这个问题我们给Board的位置增加一个范围限制就好了:

fn player_control_system(
    player: View<Player>,
    mut transform: ViewMut<Transform>,
    rb2d: View<RigidBody2D>,
    mut physics2d_manager: UniqueViewMut<Physics2DManager>,
    input: UniqueView<Input>,
) {
    for (player, transform, rb2d) in (&player, &mut transform, &rb2d).iter() {
        if let Some(rb2d) = physics2d_manager.rigid_body_set.get_mut(rb2d.handle()) {
            ...
            if transform.position.x > 9.0 {
                transform.position.x = 9.0
            }
            if transform.position.x < -9.0 {
                transform.position.x = -9.0
            }
        }
    }
}

适配安卓系统

player_control_system还有一个问题,如果我们的游戏运行在安卓手机上,一般来说安卓系统是没有连接键盘的,安卓系统习惯使用触屏来操作,我们还需要为安卓定制一套操作方式,这里我们做一个简单的实现,如果触摸了屏幕左半区域,则向左移动Board,如果触摸了屏幕右半区域,则向右移动Board:

fn player_control_system(
    player: View<Player>,
    mut transform: ViewMut<Transform>,
    rb2d: View<RigidBody2D>,
    mut physics2d_manager: UniqueViewMut<Physics2DManager>,
    input: UniqueView<Input>,
    egui_ctx: UniqueView<EguiContext>,
) {
    for (player, transform, rb2d) in (&player, &mut transform, &rb2d).iter() {
        if let Some(rb2d) = physics2d_manager.rigid_body_set.get_mut(rb2d.handle()) {
            let mut linvel = Vec2::ZERO;
            ...
            if steel::platform::BUILD_TARGET == BuildTarget::Android {
                egui_ctx.input(|input| {
                    if let Some(press_origin) = input.pointer.press_origin() {
                        if press_origin.x < input.screen_rect.center().x {
                            linvel = Vec2::new(-player.move_speed, 0.0);
                        } else {
                            linvel = Vec2::new(player.move_speed, 0.0);
                        }
                    }
                });
            }
            rb2d.set_linvel(linvel.into(), true);
            ...
        }
    }
}

我们使用常量steel::platform::BUILD_TARGET来判断当前是否在为安卓系统编译,如果是安卓系统则添加安卓系统的操作逻辑。我们这次使用了另一个单例EguiContext,来实现左右半屏的触屏判断,因为Input目前暂时还不支持触屏事件的处理。

在将游戏编译成安卓apk之前,需要安装安卓SDK,你可以通过安装Android Studio来安装安卓SDK。然后还需要执行以下命令安装cargo-ndk:

rustup target add aarch64-linux-android
cargo install cargo-ndk

插入可以adb调试的安卓手机,点击编辑器顶部菜单的“Project -> Export -> Android”即可编译到安卓手机上查看效果。

下一章:推一下球

上一章:场景搭建

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SSSxCCC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值