0Pricing
Learn Rust Coding · Lezione

Gestire input e tempo

Reagisca al giocatore e al tempo.

Gestire input e tempo è una lezione Learn Rust Coding gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Learn Rust Coding, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Learn Rust Coding include 4 lezioni in totale.

L'input come risorsa

Lo stato della tastiera risiede nella risorsa ButtonInput<KeyCode>, fornita da DefaultPlugins.

Richiedetela in sola lettura con Res. Bevy la aggiorna a ogni frame prima di eseguire i vostri sistemi, quindi visualizzate sempre lo stato corrente.

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

pressed e just_pressed

pressed vale true per ogni frame in cui un tasto viene tenuto premuto, ed è ideale per il movimento continuo. just_pressed vale true solo nel frame in cui il tasto viene premuto.

Usate just_pressed per azioni discrete come saltare o sparare, così un singolo tocco produce un solo sparo.

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

Costruire un vettore di direzione

Combinare più tasti in una direzione di movimento. Partite da zero e aggiungete un'unità a ciascun asse per ogni tasto premuto.

Normalizzate il risultato per evitare che il movimento diagonale sia più veloce di quello rettilineo.

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()
}

La risorsa Time

La frequenza dei frame varia a seconda della macchina, quindi non spostate mai un oggetto di una quantità fissa per frame. La risorsa Time fornisce delta_secs(), ovvero i secondi trascorsi dall'ultimo frame.

Moltiplicare la velocità per il delta rende il movimento indipendente dalla frequenza dei frame.

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;
    }
}

Combinare input e tempo

Il pattern corretto combina entrambe le risorse in un unico sistema: leggete la direzione dall'input, quindi moltiplicatela per velocità e delta.

In questo modo i controlli risultano fluidi e coerenti sia che il gioco venga eseguito a 30 sia a 144 frame al secondo.

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);
    }
}

Pulsanti del mouse

I pulsanti del mouse usano la stessa API ButtonInput, con MouseButton al posto di KeyCode.

Questa coerenza fa sì che le abitudini apprese con pressed e just_pressed per la tastiera si applichino direttamente anche ai clic.

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

Timer per i cooldown

Un Timer tiene traccia del tempo trascorso verso una durata obiettivo. Fatelo avanzare con il delta del frame a ogni aggiornamento.

Usate un timer ripetitivo per definire la frequenza di fuoco e controllate just_finished() per sapere quando agire.

#[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"); }
    }
}

Creare un Timer

Costruite un timer a partire da una durata e da un TimerMode. Once si arresta dopo l'attivazione; Repeating si reimposta automaticamente.

Memorizzatelo in un componente, così ogni entità, come una torretta, può avere una propria frequenza indipendente.

use std::time::Duration;

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

FixedUpdate per il determinismo

La fisica e il netcode spesso richiedono un passo temporale fisso. I sistemi nello schedule FixedUpdate vengono eseguiti un numero costante di volte al secondo, indipendentemente dalla frequenza di rendering.

Al loro interno usate Time<Fixed>, il cui delta è sempre uguale e produce una simulazione riproducibile.

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

Tempo trascorso e delta

time.delta_secs() è l'intervallo dall'ultimo frame. time.elapsed_secs() è il tempo totale trascorso dall'avvio.

Il tempo trascorso è ideale per le animazioni guidate da un'onda sinusoidale, come un oggetto raccolto che oscilla, usando il tempo totale come argomento della funzione.

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;
    }
}

Limitare entro i confini

Dopo lo spostamento, mantenete il giocatore sullo schermo limitando il transform entro le semi-estensioni della finestra.

f32::clamp limita ciascun asse. Quando vi servono limiti esatti, leggete le dimensioni della finestra dalla query 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);
    }
}

Verifica rapida

Riflettete sull'indipendenza dalla frequenza dei frame.

Riepilogo: input e tempo

L'input risiede nelle risorse ButtonInput; usate pressed per le azioni mantenute e just_pressed per i tocchi singoli. La risorsa Time e i componenti Timer mantengono il movimento e i cooldown indipendenti dalla frequenza dei frame.

Ora che i controlli sono pronti, l'ultima lezione aggiungerà le collisioni e le transizioni di stato del gioco.

Domande Frequenti

La lezione «Gestire input e tempo» è gratuita?

Sì — il testo completo di «Gestire input e tempo» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Learn Rust Coding, passa a CoddyKit PRO. Il corso Learn Rust Coding include 4 lezioni in totale.

Cosa imparerò in «Gestire input e tempo»?

Reagisca al giocatore e al tempo. Eserciti Learn Rust Coding con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Learn Rust Coding?

Non è richiesta alcuna esperienza precedente. Learn Rust Coding su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.

Quanto tempo richiede la lezione «Gestire input e tempo»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Learn Rust Coding?

Sì. Ogni lezione Learn Rust Coding include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. La mentalità ECS
  2. Creare e muovere entità
  3. Gestire input e tempo
  4. Collisioni e stato del gioco
← Torna a Learn Rust Coding