lerp all inputs with a lerper
This commit is contained in:
parent
b9fd048aad
commit
a88fd9aa82
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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();
|
let mut data = st.write().unwrap();
|
||||||
data.frame += 1;
|
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 [
|
||||||
|
|
|
@ -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,44 +77,26 @@ 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 sticky: i8 = 0;
|
|
||||||
|
|
||||||
{
|
|
||||||
let data = st.read().unwrap();
|
|
||||||
stickx = data.controller[2] as i8;
|
|
||||||
sticky = data.controller[3] as i8;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut data = msg.data.to_string();
|
|
||||||
let data = data.to_ascii_lowercase();
|
|
||||||
|
|
||||||
match data.as_str() {
|
|
||||||
"a" => hi = hi | HiButtons::A_BUTTON,
|
|
||||||
"b" => hi = hi | HiButtons::B_BUTTON,
|
|
||||||
"z" => hi = hi | HiButtons::Z_BUTTON,
|
|
||||||
"r" => lo = lo | LoButtons::R_BUTTON,
|
|
||||||
"cup" => lo = lo | LoButtons::C_UP,
|
|
||||||
"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();
|
let mut data = st.write().unwrap();
|
||||||
data.controller[0] = hi.bits() as u8;
|
|
||||||
data.controller[1] = lo.bits() as u8;
|
match chatline.as_str() {
|
||||||
data.controller[2] = stickx as u8;
|
"a" => data.a_button.add(1024),
|
||||||
data.controller[3] = sticky as u8;
|
"b" => data.b_button.add(1024),
|
||||||
|
"z" => data.z_button.add(1024),
|
||||||
|
"r" => data.r_button.add(1024),
|
||||||
|
"cup" => data.c_up.add(1024),
|
||||||
|
"cdown" => data.c_down.add(1024),
|
||||||
|
"cleft" => data.c_left.add(1024),
|
||||||
|
"cright" => data.c_right.add(1024),
|
||||||
|
"start" => data.start.add(1024),
|
||||||
|
"up" => data.stickx.add(127),
|
||||||
|
"down" => data.sticky.add(-128),
|
||||||
|
"left" => data.stickx.add(-128),
|
||||||
|
"right" => data.stickx.add(127),
|
||||||
|
"stop" => {data.stickx.update(0); data.sticky.update(0);},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
eprintln!("[{}] {}: {}", msg.channel, msg.name, msg.data);
|
eprintln!("[{}] {}: {}", msg.channel, msg.name, msg.data);
|
||||||
|
|
Loading…
Reference in New Issue