lerp all inputs with a lerper

This commit is contained in:
Cadey Ratio 2020-05-09 09:16:19 -04:00
parent b9fd048aad
commit a88fd9aa82
4 changed files with 199 additions and 157 deletions

99
gamebridge/src/au.rs Normal file
View File

@ -0,0 +1,99 @@
#[derive(Copy, Clone)]
pub(crate) struct Lerper {
extended_tick: u64,
lerp_time: f64,
goal: i64,
pub(crate) scalar: i64,
max: i64,
min: i64,
}
impl Lerper {
pub(crate) fn init(lerp_time: f64, max: i64, min: i64, goal: i64) -> Lerper {
Lerper {
extended_tick: 0,
lerp_time: lerp_time,
goal: goal,
scalar: 0, // I hope to GOD that 0 is the resting point
max: max,
min: min,
}
}
pub(crate) fn add(&mut self, new_scalar: i64) {
self.scalar += new_scalar;
}
pub(crate) fn update(&mut self, new_scalar: i64) {
self.scalar = new_scalar;
}
pub(crate) fn apply(&mut self, now: u64) -> i64 {
let scalar = self.scalar;
self.scalar = match scalar {
_ if scalar == self.goal => self.goal,
_ if scalar == self.max => {
self.extended_tick = now;
scalar -1
},
_ if scalar == self.min => {
self.extended_tick = now;
scalar - 1
},
_ => {
let t = (now - self.extended_tick) as f64 / self.lerp_time;
lerp(self.scalar, 0, t)
},
};
if self.scalar >= self.max {
return self.max;
}
if self.scalar <= self.min {
return self.min;
}
log::info!("before: {}, after: {}", scalar, self.scalar);
self.scalar
}
pub(crate) fn pressed(&mut self, threshold: i64) -> bool {
if self.scalar <= threshold {
self.scalar = 0;
}
self.scalar >= threshold
}
}
fn lerp(start: i64, end: i64, t: f64) -> i64 {
(start as f64 * (1.0 - t) + (end as f64) * t) as i64
}
#[cfg(test)]
mod test {
#[test]
fn lerp_scale() {
for case in [(0.1, 10), (0.5, 31)].iter() {
let t = case.0;
let start = 127.0 * t;
assert_eq!(super::lerp(start as i64, 0, t), case.1);
}
}
#[test]
fn lerper() {
use super::Lerper;
let mut lerper = Lerper::init(15.0, 127, -128, 0);
for case in [(127, 3, 126), (100, 8, 66)].iter() {
let scalar = case.0;
let now = case.1;
let want = case.2;
lerper.update(scalar);
assert_eq!(lerper.apply(now), want);
}
}
}

View File

@ -20,12 +20,6 @@ bitflags! {
} }
} }
impl HiButtons {
pub(crate) fn clear(&mut self) {
self.bits = 0;
}
}
bitflags! { bitflags! {
// 0x0001 C-Right // 0x0001 C-Right
// 0x0002 C-Left // 0x0002 C-Left
@ -45,42 +39,3 @@ bitflags! {
const L_BUTTON = 0x20; const L_BUTTON = 0x20;
} }
} }
impl LoButtons {
pub(crate) fn clear(&mut self) {
self.bits = 0;
}
}
pub(crate) fn test(st: crate::MTState) {
let mut lo: LoButtons = LoButtons::NONE;
let mut hi: HiButtons = HiButtons::NONE;
loop {
use std::{thread::sleep, time::Duration};
let one_second = Duration::new(1, 0);
hi = HiButtons::A_BUTTON | HiButtons::START;
{
println!("pressing a + start");
let mut data = st.write().unwrap();
data.controller[0] = hi.bits as u8;
data.controller[1] = lo.bits as u8;
}
sleep(one_second);
hi.clear();
lo.clear();
{
println!("releasing a + start");
let mut data = st.write().unwrap();
data.controller[0] = hi.bits as u8;
data.controller[1] = lo.bits as u8;
}
sleep(one_second);
}
}

View File

@ -1,9 +1,12 @@
#[macro_use] #[macro_use]
extern crate bitflags; extern crate bitflags;
pub(crate) mod au;
pub(crate) mod controller; pub(crate) mod controller;
pub(crate) mod twitch; pub(crate) mod twitch;
use crate::au::Lerper;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use std::{ use std::{
@ -12,15 +15,22 @@ use std::{
str::from_utf8, str::from_utf8,
sync::{Arc, RwLock}, sync::{Arc, RwLock},
thread::spawn, thread::spawn,
time::Instant,
}; };
#[derive(Debug)]
pub(crate) struct State { pub(crate) struct State {
controller: [u8; 4],
last_got: Box<Instant>,
ok: bool,
frame: u64, frame: u64,
stickx: Lerper,
sticky: Lerper,
a_button: Lerper,
b_button: Lerper,
z_button: Lerper,
r_button: Lerper,
start: Lerper,
c_left: Lerper,
c_right: Lerper,
c_up: Lerper,
c_down: Lerper,
} }
pub(crate) type MTState = Arc<RwLock<State>>; pub(crate) type MTState = Arc<RwLock<State>>;
@ -32,12 +42,24 @@ fn main() -> Result<()> {
let mut vblank = File::open("vblank")?; let mut vblank = File::open("vblank")?;
let mut input = OpenOptions::new().write(true).open("input")?; let mut input = OpenOptions::new().write(true).open("input")?;
const STICK_LERP_TIME: f64 = 330.0; // 330 frames to lerp stick positions down to 0
const BUTTON_LERP_TIME: f64 = 20.0; // 20 frames to lerp button inputs down to 0
let st = { let st = {
let st = State { let st = State {
controller: [0; 4],
last_got: Box::new(Instant::now()),
ok: true,
frame: 0, frame: 0,
stickx: Lerper::init(STICK_LERP_TIME, 127, -128, 0),
sticky: Lerper::init(STICK_LERP_TIME, 127, -128, 0),
a_button: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0),
b_button: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0),
z_button: Lerper::init(BUTTON_LERP_TIME / 4.0, 64, -1, 0), // z button is special
r_button: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0),
start: Lerper::init(BUTTON_LERP_TIME / 4.0, 64, -1, 0), // z button is special
c_left: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0),
c_right: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0),
c_up: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0),
c_down: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0),
}; };
Arc::new(RwLock::new(st)) Arc::new(RwLock::new(st))
@ -50,10 +72,6 @@ fn main() -> Result<()> {
spawn(move || twitch::run(st)); spawn(move || twitch::run(st));
} }
const LERP_TIME: f64 = 330.0; // 330 frames to lerp stick positions down to 0
let mut xmax_frame: u64 = 0;
let mut ymax_frame: u64 = 0;
loop { loop {
let mut data = [0; 3]; let mut data = [0; 3];
debug!("waiting for vblank"); debug!("waiting for vblank");
@ -61,59 +79,63 @@ fn main() -> Result<()> {
let str = from_utf8(&data)?; let str = from_utf8(&data)?;
debug!("got data: {}", str); debug!("got data: {}", str);
let mut controller = [0; 4];
match str { match str {
"OK\n" => { "OK\n" => {
let mut data = st.write().unwrap(); {
data.frame += 1; let mut data = st.write().unwrap();
data.frame += 1;
let mut stickx = data.controller[2] as i8;
let mut sticky = data.controller[3] as i8;
let dist = stick_distance(stickx, sticky);
if dist <= 10 {
stickx = 0;
sticky = 0;
xmax_frame = 0;
ymax_frame = 0;
} }
stickx = match stickx { let mut data = st.write().unwrap();
0 => stickx, let frame = data.frame + 1;
127 => {
xmax_frame = data.frame;
stickx - 10
},
-128 => {
xmax_frame = data.frame;
stickx + 10
},
_ => {
let t = (data.frame - xmax_frame) as f64 / (LERP_TIME as f64);
lerp(stickx, 0, t)
},
};
sticky = match sticky { //data.stickx.update(data.controller[2] as i64);
0 => sticky, //data.sticky.update(data.controller[3] as i64);
127 => { let mut stickx_scalar = data.stickx.apply(frame) as i8;
ymax_frame = data.frame; let mut sticky_scalar = data.sticky.apply(frame) as i8;
sticky - 10
},
-128 => {
ymax_frame = data.frame;
sticky + 10
},
_ => {
let t = (data.frame - ymax_frame) as f64 / (LERP_TIME as f64);
lerp(sticky, 0, t)
},
};
input.write(&data.controller)?; let dist = stick_distance(stickx_scalar, sticky_scalar);
data.controller[0] = 0; if dist <= 10 {
data.controller[1] = 0; stickx_scalar = 0;
data.controller[2] = stickx as u8; sticky_scalar = 0;
data.controller[3] = sticky as u8; }
use controller::{HiButtons, LoButtons};
let mut hi = HiButtons::NONE;
let mut lo = LoButtons::NONE;
const BUTTON_PUSH_THRESHOLD: i64 = 16;
// high buttons
data.a_button.apply(frame);
if data.a_button.pressed(BUTTON_PUSH_THRESHOLD) { hi = hi | HiButtons::A_BUTTON; }
data.b_button.apply(frame);
if data.b_button.pressed(BUTTON_PUSH_THRESHOLD) { hi = hi | HiButtons::B_BUTTON; }
data.z_button.apply(frame);
if data.z_button.pressed(BUTTON_PUSH_THRESHOLD) { hi = hi | HiButtons::Z_BUTTON; }
data.start.apply(frame);
if data.start.pressed(BUTTON_PUSH_THRESHOLD) { hi = hi | HiButtons::START; }
info!("start: {}", data.start.scalar);
data.r_button.apply(frame);
if data.r_button.pressed(BUTTON_PUSH_THRESHOLD) { lo = lo | LoButtons::R_BUTTON; }
data.c_up.apply(frame);
if data.c_up.pressed(BUTTON_PUSH_THRESHOLD) { lo = lo | LoButtons::C_UP; }
data.c_down.apply(frame);
if data.c_down.pressed(BUTTON_PUSH_THRESHOLD) { lo = lo | LoButtons::C_DOWN; }
data.c_left.apply(frame);
if data.c_left.pressed(BUTTON_PUSH_THRESHOLD) { lo = lo | LoButtons::C_LEFT; }
data.c_right.apply(frame);
if data.c_right.pressed(BUTTON_PUSH_THRESHOLD) { lo = lo | LoButtons::C_RIGHT; }
controller[0] = hi.bits() as u8;
controller[1] = lo.bits() as u8;
controller[2] = stickx_scalar as u8;
controller[3] = sticky_scalar as u8;
input.write(&controller)?;
} }
"BYE" => { "BYE" => {
warn!("asked to exit by the game"); warn!("asked to exit by the game");
@ -127,10 +149,6 @@ fn main() -> Result<()> {
} }
} }
fn lerp(start: i8, end: i8, t: f64) -> i8 {
(start as f64 * (1.0 - t) + (end as f64) * t) as i8
}
fn stick_distance(x: i8, y: i8) -> i8 { fn stick_distance(x: i8, y: i8) -> i8 {
let x = (x as f64).powi(2); let x = (x as f64).powi(2);
let y = (y as f64).powi(2); let y = (y as f64).powi(2);
@ -139,15 +157,6 @@ fn stick_distance(x: i8, y: i8) -> i8 {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
#[test]
fn lerp_scale() {
for case in [(0.1, 10), (0.5, 31)].iter() {
let t = case.0;
let start = 127.0 * t;
assert_eq!(crate::lerp(start as i8, 0, t), case.1);
}
}
#[test] #[test]
fn stick_distance() { fn stick_distance() {
for case in [ for case in [

View File

@ -1,7 +1,4 @@
use crate::{ use crate::MTState;
controller::{HiButtons, LoButtons},
MTState,
};
use tokio::stream::StreamExt as _; use tokio::stream::StreamExt as _;
use twitchchat::{events, Control, Dispatcher, Runner, Status}; use twitchchat::{events, Control, Dispatcher, Runner, Status};
@ -80,46 +77,28 @@ async fn run_loop(
eprintln!("{} left {}", msg.name, msg.channel); eprintln!("{} left {}", msg.name, msg.channel);
} }
Some(msg) = pmsg.next() => { Some(msg) = pmsg.next() => {
let mut hi = HiButtons::NONE; let chatline = msg.data.to_string();
let mut lo = LoButtons::NONE; let chatline = chatline.to_ascii_lowercase();
let mut stickx: i8 = 0; let mut data = st.write().unwrap();
let mut sticky: i8 = 0;
{ match chatline.as_str() {
let data = st.read().unwrap(); "a" => data.a_button.add(1024),
stickx = data.controller[2] as i8; "b" => data.b_button.add(1024),
sticky = data.controller[3] as i8; "z" => data.z_button.add(1024),
} "r" => data.r_button.add(1024),
"cup" => data.c_up.add(1024),
let mut data = msg.data.to_string(); "cdown" => data.c_down.add(1024),
let data = data.to_ascii_lowercase(); "cleft" => data.c_left.add(1024),
"cright" => data.c_right.add(1024),
match data.as_str() { "start" => data.start.add(1024),
"a" => hi = hi | HiButtons::A_BUTTON, "up" => data.stickx.add(127),
"b" => hi = hi | HiButtons::B_BUTTON, "down" => data.sticky.add(-128),
"z" => hi = hi | HiButtons::Z_BUTTON, "left" => data.stickx.add(-128),
"r" => lo = lo | LoButtons::R_BUTTON, "right" => data.stickx.add(127),
"cup" => lo = lo | LoButtons::C_UP, "stop" => {data.stickx.update(0); data.sticky.update(0);},
"cdown" => lo = lo | LoButtons::C_DOWN,
"cleft" => lo = lo | LoButtons::C_LEFT,
"cright" => lo = lo | LoButtons::C_RIGHT,
"start" => hi = hi | HiButtons::START,
"up" => sticky = 127,
"down" => sticky = -128,
"left" => stickx = -128,
"right" => stickx = 127,
"stop" => {stickx = 0; sticky = 0;},
_ => {}, _ => {},
} }
{
let mut data = st.write().unwrap();
data.controller[0] = hi.bits() as u8;
data.controller[1] = lo.bits() as u8;
data.controller[2] = stickx as u8;
data.controller[3] = sticky as u8;
}
eprintln!("[{}] {}: {}", msg.channel, msg.name, msg.data); eprintln!("[{}] {}: {}", msg.channel, msg.name, msg.data);
} }