From b3be350047ebeab691bad1968c89b64f58202673 Mon Sep 17 00:00:00 2001 From: griffi-gh Date: Sun, 4 Jun 2023 18:39:55 +0200 Subject: [PATCH] Add touch controls, switch to tinymap for keyboard input --- .readme/touch_controls.png | Bin 0 -> 12715 bytes Cargo.lock | 10 +++ README.md | 39 ++++++--- kubi/Cargo.toml | 1 + kubi/src/block_placement.rs | 2 +- kubi/src/control_flow.rs | 2 +- kubi/src/events.rs | 16 +++- kubi/src/input.rs | 167 +++++++++++++++++++++++++++++++++--- kubi/src/loading_screen.rs | 2 +- 9 files changed, 210 insertions(+), 29 deletions(-) create mode 100644 .readme/touch_controls.png diff --git a/.readme/touch_controls.png b/.readme/touch_controls.png new file mode 100644 index 0000000000000000000000000000000000000000..e7db529ef8fc52be216007e569a6edc074661923 GIT binary patch literal 12715 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 T!(1DW@(2{=)MP7V%)k9F^bu8z literal 0 HcmV?d00001 diff --git a/Cargo.lock b/Cargo.lock index 28f1ccc..8219cbe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -976,6 +976,7 @@ dependencies = [ "shipyard", "static_assertions", "strum", + "tinyset", "uflow", "winapi", ] @@ -1989,6 +1990,15 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "tinyset" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2417e47ddd3809ad40222777ac754ee881b3a6401e38cbeeeb3ee1ca5f30aa0" +dependencies = [ + "rand", +] + [[package]] name = "toml" version = "0.7.3" diff --git a/README.md b/README.md index 3672f7c..9c5df8d 100644 --- a/README.md +++ b/README.md @@ -26,49 +26,64 @@

download

Latest nightly release -

building

+

build for windows/linux

-build/run +**build/run** ```bash cargo build --bin kubi cargo run --bin kubi ``` -build in release mode, with nightly optimizations +**build in release mode, with nightly optimizations** ```bash cargo +nightly build --bin kubi --features nightly --release ``` -build for android +

build for android

-please note that android support is purely experimental! -gamepad, keyboard and mouse input is currently borked, and touch controls are not available. +please note that android support is highly experimental!\ +gamepad, mouse input is currently borked, and proper touch controls are not available.\ srgb and blending are broken too, which leads to many rendering issues -prerequisites: Android SDK, NDK, platform-tools, latest JDK (all should be in $PATH) +prerequisites: Android SDK, command line tools, NDK, platform-tools, latest JDK\ +(make sure that your $PATH variable is configured properly) -Setup: +**Setup:** ```bash cargo install cargo-apk cargo target add aarch64-linux-android ``` -Build: -`--no-default-features` is required for keyboard input! +**Build:** + +`--no-default-features` is required for keyboard input!\ +(`prefer-raw-events` feature *must* be disabled on android) + +Mouse input is not implemented, touch only! ```bash cargo apk build -p kubi --no-default-features ``` -Run: +**Run:** ```bash -cargo apk run -p kubi --features nightly +cargo apk run -p kubi --no-default-features ``` +

android controls

+ +touch control scheme + +- Left side: **Movement** +- Rigth side: **Camera controls** +- Bottom right corner: + - **B** (e.g. place blocks) + - **A** (e.g. break, attack) +

mutiplayer

to join a multiplayer server, just pass the ip address as an argument diff --git a/kubi/Cargo.toml b/kubi/Cargo.toml index 2307e82..6528890 100644 --- a/kubi/Cargo.toml +++ b/kubi/Cargo.toml @@ -27,6 +27,7 @@ postcard = { version = "1.0", features = ["alloc"] } serde_json = { version = "1.0", optional = true } lz4_flex = { version = "0.10", default-features = false, features = ["std", "checked-decode"] } static_assertions = "1.1" +tinyset = "0.4" [target.'cfg(target_os = "android")'.dependencies] ndk = "0.7" diff --git a/kubi/src/block_placement.rs b/kubi/src/block_placement.rs index cc0a747..18eb313 100644 --- a/kubi/src/block_placement.rs +++ b/kubi/src/block_placement.rs @@ -30,7 +30,7 @@ fn pick_block_with_number_keys( ) { let Some((_, mut holding)) = (&main_player, &mut holding).iter().next() else { return }; for &(key, block) in BLOCK_KEY_MAP { - if input.keyboard_state.contains(&key) { + if input.keyboard_state.contains(key as u32) { holding.0 = Some(block); return } diff --git a/kubi/src/control_flow.rs b/kubi/src/control_flow.rs index 9dad6e1..4a4b0c3 100644 --- a/kubi/src/control_flow.rs +++ b/kubi/src/control_flow.rs @@ -9,7 +9,7 @@ pub fn exit_on_esc( raw_inputs: UniqueView, mut control_flow: UniqueViewMut ) { - if raw_inputs.keyboard_state.contains(&VirtualKeyCode::Escape) { + if raw_inputs.keyboard_state.contains(VirtualKeyCode::Escape as u32) { control_flow.0 = Some(ControlFlow::Exit); } } diff --git a/kubi/src/events.rs b/kubi/src/events.rs index c9132c2..ba919f8 100644 --- a/kubi/src/events.rs +++ b/kubi/src/events.rs @@ -1,6 +1,6 @@ use glam::UVec2; use shipyard::{World, Component, AllStoragesViewMut, SparseSet, NonSendSync, UniqueView}; -use glium::glutin::event::{Event, DeviceEvent, DeviceId, WindowEvent}; +use glium::glutin::event::{Event, DeviceEvent, DeviceId, WindowEvent, Touch}; use crate::rendering::Renderer; pub mod player_actions; @@ -17,6 +17,10 @@ pub struct InputDeviceEvent{ pub event: DeviceEvent } +#[derive(Component, Clone, Copy, Debug)] +#[repr(transparent)] +pub struct TouchEvent(pub Touch); + #[derive(Component, Clone, Copy, Debug, Default)] pub struct WindowResizedEvent(pub UVec2); @@ -24,10 +28,9 @@ pub fn process_glutin_events(world: &mut World, event: &Event<'_, ()>) { #[allow(clippy::collapsible_match, clippy::single_match)] match event { Event::WindowEvent { window_id: _, event } => match event { - WindowEvent::Resized(size) => { world.add_entity(( - EventComponent, + EventComponent, WindowResizedEvent(UVec2::new(size.width as _, size.height as _)) )); }, @@ -43,6 +46,13 @@ pub fn process_glutin_events(world: &mut World, event: &Event<'_, ()>) { )); } + WindowEvent::Touch(touch) => { + world.add_entity(( + EventComponent, + TouchEvent(*touch) + )); + } + _ => () }, diff --git a/kubi/src/input.rs b/kubi/src/input.rs index 37eb0fb..d1e65b5 100644 --- a/kubi/src/input.rs +++ b/kubi/src/input.rs @@ -1,10 +1,14 @@ use gilrs::{Gilrs, GamepadId, Button, Event, Axis}; -use glam::{Vec2, DVec2, vec2}; -use glium::glutin::event::{DeviceEvent, VirtualKeyCode, ElementState}; -use hashbrown::HashSet; +use glam::{Vec2, DVec2, vec2, dvec2}; +use glium::glutin::event::{DeviceEvent, DeviceId, VirtualKeyCode, ElementState, TouchPhase}; +use hashbrown::HashMap; +use tinyset::{SetU32, SetU64}; use nohash_hasher::BuildNoHashHasher; use shipyard::{AllStoragesView, Unique, View, IntoIter, UniqueViewMut, Workload, IntoWorkload, UniqueView, NonSendSync}; -use crate::events::InputDeviceEvent; +use crate::{ + events::{InputDeviceEvent, TouchEvent}, + rendering::WindowSize +}; #[derive(Unique, Clone, Copy, Default, Debug)] pub struct Inputs { @@ -19,11 +23,62 @@ pub struct PrevInputs(pub Inputs); #[derive(Unique, Clone, Default, Debug)] pub struct RawKbmInputState { - pub keyboard_state: HashSet>, + pub keyboard_state: SetU32, pub button_state: [bool; 32], pub mouse_delta: DVec2 } +#[derive(Clone, Copy, Debug, Default)] +pub enum FingerCheck { + #[default] + Start, + Current, + StartOrCurrent, + StartAndCurrent, + NotMoved, +} + +#[derive(Clone, Copy, Debug)] +pub struct Finger { + pub id: u64, + pub device_id: DeviceId, + pub prev_position: DVec2, + pub start_position: DVec2, + pub current_position: DVec2, + pub has_moved: bool, +} +impl Finger { + pub fn within_area(&self, area_pos: DVec2, area_size: DVec2, check: FingerCheck) -> bool { + let within_area = |pos: DVec2| -> bool { + ((pos - area_pos).min_element() >= 0.) && + ((pos - (area_pos + area_size)).max_element() <= 0.) + }; + let start = within_area(self.start_position); + let current = within_area(self.current_position); + match check { + FingerCheck::Start => start, + FingerCheck::Current => current, + FingerCheck::StartOrCurrent => start || current, + FingerCheck::StartAndCurrent => start && current, + FingerCheck::NotMoved => current && !self.has_moved, + } + } +} + +#[derive(Unique, Clone, Default, Debug)] +pub struct RawTouchState { + //TODO: handle multiple touch devices somehow + pub fingers: HashMap> +} + +impl RawTouchState { + pub fn query_area(&self, area_pos: DVec2, area_size: DVec2, check: FingerCheck) -> impl Iterator + '_ { + self.fingers.iter().filter_map(move |(_, &finger)| { + finger.within_area(area_pos, area_size, check).then_some(finger) + }) + } +} + #[derive(Unique)] pub struct GilrsWrapper(Option); @@ -46,8 +101,8 @@ fn process_events( DeviceEvent::Key(input) => { if let Some(keycode) = input.virtual_keycode { match input.state { - ElementState::Pressed => input_state.keyboard_state.insert(keycode), - ElementState::Released => input_state.keyboard_state.remove(&keycode), + ElementState::Pressed => input_state.keyboard_state.insert(keycode as u32), + ElementState::Released => input_state.keyboard_state.remove(keycode as u32), }; } }, @@ -61,6 +116,39 @@ fn process_events( } } +fn process_touch_events( + touch_events: View, + mut touch_state: UniqueViewMut, +) { + for (_, finger) in &mut touch_state.fingers { + finger.prev_position = finger.current_position; + } + for event in touch_events.iter() { + let position = dvec2(event.0.location.x, event.0.location.y); + match event.0.phase { + TouchPhase::Started => { + touch_state.fingers.insert(event.0.id, Finger { + id: event.0.id, + device_id: event.0.device_id, + start_position: position, + current_position: position, + prev_position: position, + has_moved: false + }); + }, + TouchPhase::Moved => { + if let Some(finger) = touch_state.fingers.get_mut(&event.0.id) { + finger.has_moved = true; + finger.current_position = position; + } + }, + TouchPhase::Ended | TouchPhase::Cancelled => { + touch_state.fingers.remove(&event.0.id); + }, + } + } +} + fn process_gilrs_events( mut gilrs: NonSendSync>, mut active_gamepad: UniqueViewMut @@ -85,10 +173,10 @@ fn update_input_state ( mut inputs: UniqueViewMut, ) { inputs.movement += Vec2::new( - raw_inputs.keyboard_state.contains(&VirtualKeyCode::D) as u32 as f32 - - raw_inputs.keyboard_state.contains(&VirtualKeyCode::A) as u32 as f32, - raw_inputs.keyboard_state.contains(&VirtualKeyCode::W) as u32 as f32 - - raw_inputs.keyboard_state.contains(&VirtualKeyCode::S) as u32 as f32 + raw_inputs.keyboard_state.contains(VirtualKeyCode::D as u32) as u32 as f32 - + raw_inputs.keyboard_state.contains(VirtualKeyCode::A as u32) as u32 as f32, + raw_inputs.keyboard_state.contains(VirtualKeyCode::W as u32) as u32 as f32 - + raw_inputs.keyboard_state.contains(VirtualKeyCode::S as u32) as u32 as f32 ); inputs.look += raw_inputs.mouse_delta.as_vec2(); inputs.action_a |= raw_inputs.button_state[1]; @@ -112,6 +200,60 @@ fn update_input_state_gamepad ( } } +fn update_input_state_touch ( + touch_state: UniqueView, + win_size: UniqueView, + mut inputs: UniqueViewMut, +) { + let w = win_size.0.as_dvec2(); + + //Movement + if let Some(finger) = touch_state.query_area( + dvec2(0., 0.), + dvec2(w.x / 2., w.y), + FingerCheck::Start + ).next() { + inputs.movement += (((finger.current_position - finger.start_position) / (w.x / 4.)) * dvec2(1., -1.)).as_vec2(); + } + + //Action buttons + let action_button_fingers = { + let mut action_button_fingers = SetU64::new(); + + //Creates iterator of fingers that started within action button area + let action_finger_iter = || touch_state.query_area( + dvec2(w.x * 0.75, w.y * 0.666), + dvec2(w.x * 0.25, w.y * 0.333), + FingerCheck::Start + ); + + //Action button A + inputs.action_a |= action_finger_iter().filter(|finger| finger.within_area( + dvec2(w.x * (0.75 + 0.125), w.y * 0.666), + dvec2(w.x * 0.125, w.y * 0.333), + FingerCheck::StartOrCurrent + )).map(|x| action_button_fingers.insert(x.id)).next().is_some(); + + //Action button B + inputs.action_b |= action_finger_iter().filter(|finger| finger.within_area( + dvec2(w.x * 0.75, w.y * 0.666), + dvec2(w.x * 0.125, w.y * 0.333), + FingerCheck::StartOrCurrent + )).map(|x| action_button_fingers.insert(x.id)).next().is_some(); + + action_button_fingers + }; + + //Camera controls + if let Some(finger) = touch_state.query_area( + dvec2(w.x / 2., 0.), + dvec2(w.x / 2., w.y), + FingerCheck::Start + ).find(|x| !action_button_fingers.contains(x.id)) { + inputs.look += (((finger.current_position - finger.prev_position) / (w.x / 4.)) * 300.).as_vec2(); + } +} + fn input_end( mut inputs: UniqueViewMut, ) { @@ -133,14 +275,17 @@ pub fn init_input ( storages.add_unique(Inputs::default()); storages.add_unique(PrevInputs::default()); storages.add_unique(RawKbmInputState::default()); + storages.add_unique(RawTouchState::default()); } pub fn process_inputs() -> Workload { ( process_events, + process_touch_events, process_gilrs_events, input_start, update_input_state, + update_input_state_touch, update_input_state_gamepad, input_end, ).into_sequential_workload() diff --git a/kubi/src/loading_screen.rs b/kubi/src/loading_screen.rs index 210acc2..e3e3e99 100644 --- a/kubi/src/loading_screen.rs +++ b/kubi/src/loading_screen.rs @@ -76,7 +76,7 @@ fn override_loading( kbm_state: UniqueView, mut state: UniqueViewMut ) { - if kbm_state.keyboard_state.contains(&VirtualKeyCode::F) { + if kbm_state.keyboard_state.contains(VirtualKeyCode::F as u32) { state.0 = Some(GameState::InGame); } }