Skip to content

Bevy virtual Joystick for mobile games (Works with mouse on desktop)

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
Notifications You must be signed in to change notification settings

SergioRibera/virtual_joystick

Folders and files

NameName
Last commit message
Last commit date

Latest commit

2fc705a · Jan 16, 2025
Dec 31, 2023
Aug 3, 2023
Dec 7, 2024
Dec 7, 2024
Dec 7, 2024
Jan 16, 2025
Jul 11, 2023
Jul 11, 2023
Apr 19, 2023
Jan 16, 2025
Jan 16, 2025
Feb 27, 2024
Feb 27, 2024
Jan 16, 2025
Jan 16, 2025
Apr 19, 2023
Aug 6, 2024

Repository files navigation

Bevy Virtual Joystick

VJoystick_Fixed_Preview


GitHub Workflow Status GitHub release (latest by date)

Create and use a Virtual Joystick in a UI for bevy Game Engine.

Versions

Aviable and compatible versions

bevy VirtualJoystick
0.15 2.4.0
0.14 2.3.0
0.13 2.2.0
0.12 2.1.0
0.11 2.0.1
0.10.1 1.1.2

Features

  • Support Mouse and Touch
  • Easy usage
  • Multiple Joysticks on screen
  • Multiple types of joystick behaviour
  • Track events on Joystick (Press, Drag and Up)
  • Support Axis block (Horizontal, Vertical or Both)

NOTE: To compile android projects you can use cargo-apk or the docker-rust-android project container where you don't have to install or prepare any sdk, for more details see the readme of the mobile projects

Axis

Both (Default) Horizontal Vertical
VJoystick_Fixed_Both VJoystick_Fixed_Horizontal VJoystick_Fixed_Vertical

Joystick Types

Fixed Floating (Default) Dynamic (TODO: Fix movement feel)
VJoystick_Fixed_Both VJoystick_Floating_Both VJoystick_Dynamic_Both

Examples

Features

  • inspect: for world inspect with egui inspector
  • serde (default): for serialization support for all types (usable for save and load settings)
virtual_joystick = {
    version = "*",
    default-features = false,
    features = [ "inspect", "serde" ]
}

Usage

Check out the examples for details.

# to run example
cargo run --example simple -F=inspect

Add to Cargo.toml

[dependencies]
bevy = "0.12"
virtual_joystick = "*" # Add your version

The minimal requirement:

use bevy::prelude::*;
// import crate
use virtual_joystick::*;

// ID for joysticks
#[derive(Default, Reflect, Hash, Clone, PartialEq, Eq)]
enum JoystickControllerID {
    #[default]
    Joystick1,
    Joystick2,
}

#[bevy_main]
fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        // Add plugin to application
        .add_plugin(VirtualJoystickPlugin::<JoystickControllerID>::default())
        .run()
}

Create Joystick

#[bevy_main]
fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        // Add plugin to application
        .add_plugin(VirtualJoystickPlugin)
        // Create system
        .add_startup_system(create_scene)
        // update System
        .add_system(update_player)
        .run()
}


fn create_scene(mut cmd: Commands, asset_server: Res<AssetServer>) {
    cmd.spawn(Camera2dBundle::default());
    cmd.spawn_empty().insert(Player(30.));

    // Spawn Virtual Joystick at horizontal center
    create_joystick(
        &mut cmd,
        asset_server.load("Knob.png"),
        asset_server.load("Outline.png"),
        None,
        None,
        Some(Color::rgba(1.0, 0.27, 0.0, 0.3))),
        Vec2::new(75., 75.),
        Vec2::new(150., 150.),
        VirtualJoystickNode {
            dead_zone: 0.,
            id: "UniqueJoystick".to_string(),
            axis: VirtualJoystickAxis::Both,
            behaviour: VirtualJoystickType::Floating,
        },
        Style {
            width: Val::Px(150.),
            height: Val::Px(150.),
            position_type: PositionType::Absolute,
            left: Val::Percent(50.),
            bottom: Val::Percent(15.),
            ..default()
        },
    );
}

Use variable generated by Joystick

fn update_joystick(
    mut joystick: EventReader<VirtualJoystickEvent<String>>,
    mut player: Query<(&mut Transform, &Player)>,
    time_step: Res<Time>,
) {
    // Get player
    let (mut player, player_data) = player.single_mut();

    // Iter each joystick event
    for j in joystick.read() {
        let Vec2 { x, y } = j.axis();
        // Verify ID of joystick for movement
        match j.id() {
            JoystickControllerID::Joystick1 => {
                // Move player using joystick axis value
                player.translation.x += x * player_data.0 * time_step.delta_seconds();
                player.translation.y += y * player_data.0 * time_step.delta_seconds();
            }
        }
    }
}

TODOs

  • WIP: Add more better documentation