0Pricing
Learn Rust Coding · レッスン

入力と時間を扱う

プレイヤーの操作と時計に反応します。

「入力と時間を扱う」はCoddyKit上の無料Learn Rust Codingレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLearn Rust Coding学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Learn Rust Codingコースには全4レッスンが含まれています。

入力をリソースとして扱う

キーボードの状態は、DefaultPluginsが提供するButtonInput<KeyCode>リソースに保存されます。

Resを使って読み取り専用で要求します。Bevyはシステムの実行前に毎フレーム更新するため、常に現在の状態を取得できます。

fn read_keys(keys: Res<ButtonInput<KeyCode>>) {
    if keys.pressed(KeyCode::Space) {
        info!("space held");
    }
}

pressedとjust_pressed

pressedはキーを押し続けているすべてのフレームでtrueになります。継続的な移動に適しています。just_pressedはキーが押されたフレームでのみtrueになります。

ジャンプや発射のような1回限りのアクションにはjust_pressedを使います。これにより、1回のタップで1回だけ実行できます。

fn jump(keys: Res<ButtonInput<KeyCode>>) {
    if keys.just_pressed(KeyCode::ArrowUp) {
        info!("jump!");
    }
}

方向ベクトルを作る

複数のキーを組み合わせて移動方向を作ります。最初はゼロにし、押されているキーごとに対応する軸へ単位量を加えます。

結果を正規化すると、斜め方向の移動が直線方向より速くなるのを防げます。

fn direction(keys: Res<ButtonInput<KeyCode>>) -> Vec2 {
    let mut d = Vec2::ZERO;
    if keys.pressed(KeyCode::KeyW) { d.y += 1.0; }
    if keys.pressed(KeyCode::KeyS) { d.y -= 1.0; }
    if keys.pressed(KeyCode::KeyA) { d.x -= 1.0; }
    if keys.pressed(KeyCode::KeyD) { d.x += 1.0; }
    d.normalize_or_zero()
}

Timeリソース

フレームレートはマシンによって異なるため、フレームごとに固定量だけ移動させてはいけません。Timeリソースのdelta_secs()から、前のフレームからの経過秒数を取得できます。

速度に経過時間を掛けることで、フレームレートに依存しない移動になります。

fn move_player(time: Res<Time>, mut q: Query<&mut Transform, With<Player>>) {
    let dt = time.delta_secs();
    for mut tf in &mut q {
        tf.translation.x += 200.0 * dt;
    }
}

入力と時間を組み合わせる

実際のパターンでは、2つのリソースを1つのシステムで組み合わせます。入力から方向を読み取り、それを速度とデルタでスケーリングします。

これにより、ゲームが毎秒30フレームでも144フレームでも、滑らかで一貫した操作が可能になります。

fn drive(
    keys: Res<ButtonInput<KeyCode>>,
    time: Res<Time>,
    mut q: Query<&mut Transform, With<Player>>,
) {
    let mut d = Vec2::ZERO;
    if keys.pressed(KeyCode::KeyA) { d.x -= 1.0; }
    if keys.pressed(KeyCode::KeyD) { d.x += 1.0; }
    let step = d.normalize_or_zero() * 300.0 * time.delta_secs();
    for mut tf in &mut q {
        tf.translation += step.extend(0.0);
    }
}

マウスボタン

マウスボタンでは、KeyCodeの代わりにMouseButtonを使い、同じButtonInput APIを利用します。

この一貫性により、キーボードで身につけたpressedとjust_pressedの使い方を、そのままクリックにも適用できます。

fn shoot(buttons: Res<ButtonInput<MouseButton>>) {
    if buttons.just_pressed(MouseButton::Left) {
        info!("fire");
    }
}

クールダウン用のタイマー

Timerは、目標とする時間が経過するまでの時間を追跡します。更新のたびにフレームデルタで進めます。

発射間隔にはリピートタイマーを使い、just_finished()を確認して処理を実行するタイミングを判断します。

#[derive(Component)]
struct Cooldown(Timer);

fn tick(time: Res<Time>, mut q: Query<&mut Cooldown>) {
    for mut cd in &mut q {
        cd.0.tick(time.delta());
        if cd.0.just_finished() { info!("ready"); }
    }
}

タイマーの作成

時間とTimerModeからタイマーを構築します。Onceは1回実行すると停止し、Repeatingは自動的にリセットされます。

タイマーをコンポーネントに保持すると、砲台などの各エンティティがそれぞれ独立した発射間隔を持てます。

use std::time::Duration;

commands.spawn((
    Turret,
    Cooldown(Timer::new(Duration::from_millis(500), TimerMode::Repeating)),
));

決定性のためのFixedUpdate

物理演算やネットコードでは、固定タイムステップが必要になることがよくあります。FixedUpdateスケジュール内のシステムは、レンダリング速度に関係なく、毎秒一定回数実行されます。

その中ではデルタが常に同じTime<Fixed>を使うため、再現可能なシミュレーションになります。

App::new()
    .add_plugins(DefaultPlugins)
    .add_systems(FixedUpdate, physics_step)
    .run();

経過時間とデルタ

time.delta_secs()は前のフレームからの経過時間です。time.elapsed_secs()は起動してからの合計経過時間です。

経過時間は、揺れるアイテムのように正弦波で動かすアニメーションに適しています。合計時間を関数に渡して動きを作れます。

fn bob(time: Res<Time>, mut q: Query<&mut Transform, With<Pickup>>) {
    let t = time.elapsed_secs();
    for mut tf in &mut q {
        tf.translation.y = (t * 2.0).sin() * 10.0;
    }
}

範囲内へのクランプ

移動後にトランスフォームをウィンドウの半分の大きさの範囲内にクランプすると、プレイヤーを画面内に留められます。

f32::clampは各軸を範囲内に収めます。正確な上限と下限が必要な場合は、Windowクエリからウィンドウサイズを読み取ります。

fn clamp(mut q: Query<&mut Transform, With<Player>>) {
    for mut tf in &mut q {
        tf.translation.x = tf.translation.x.clamp(-300.0, 300.0);
        tf.translation.y = tf.translation.y.clamp(-200.0, 200.0);
    }
}

簡単な確認

フレームレートに依存しない処理について考えてみましょう。

まとめ: 入力と時間

入力はButtonInputリソースに格納されます。押し続けている操作にはpressedを、タップにはjust_pressedを使います。TimeリソースとTimerコンポーネントによって、移動やクールダウンをフレームレートに依存しないようにできます。

操作の仕組みが整ったので、最後のレッスンでは衝突とゲーム状態の遷移を追加します。

よくある質問

「入力と時間を扱う」レッスンは無料ですか?

はい。「入力と時間を扱う」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Learn Rust Codingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Learn Rust Codingコースには全4レッスンが含まれています。

「入力と時間を扱う」で何を学びますか?

プレイヤーの操作と時計に反応します。 ブラウザで直接実行するハンズオンコードでLearn Rust Codingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Learn Rust Codingを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのLearn Rust Codingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「入力と時間を扱う」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このLearn Rust Codingレッスンでコードを書いて実行できますか?

はい。すべてのLearn Rust Codingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. ECSの考え方
  2. エンティティを生成して動かす
  3. 入力と時間を扱う
  4. 衝突とゲーム状態
← Learn Rust Codingに戻る