gamebridge: Twitch Control #2
|
@ -20,6 +20,7 @@ pub(crate) struct State {
|
|||
controller: [u8; 4],
|
||||
last_got: Box<Instant>,
|
||||
ok: bool,
|
||||
frame: u64,
|
||||
}
|
||||
|
||||
pub(crate) type MTState = Arc<RwLock<State>>;
|
||||
|
@ -36,6 +37,7 @@ fn main() -> Result<()> {
|
|||
controller: [0; 4],
|
||||
last_got: Box::new(Instant::now()),
|
||||
ok: true,
|
||||
frame: 0,
|
||||
};
|
||||
|
||||
Arc::new(RwLock::new(st))
|
||||
|
@ -48,6 +50,10 @@ fn main() -> Result<()> {
|
|||
spawn(move || twitch::run(st));
|
||||
}
|
||||
|
||||
const LERP_TIME: f64 = 1.0; // 15 frames to lerp stick positions down to 0
|
||||
let mut xmax_frame: u64 = 0;
|
||||
let mut ymax_frame: u64 = 0;
|
||||
|
||||
loop {
|
||||
let mut data = [0; 3];
|
||||
debug!("waiting for vblank");
|
||||
|
@ -58,9 +64,56 @@ fn main() -> Result<()> {
|
|||
match str {
|
||||
"OK\n" => {
|
||||
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 {
|
||||
0 => stickx,
|
||||
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 stickx {
|
||||
0 => sticky,
|
||||
127 => {
|
||||
ymax_frame = data.frame;
|
||||
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)?;
|
||||
data.controller[0] = 0;
|
||||
data.controller[1] = 0;
|
||||
data.controller[2] = stickx as u8;
|
||||
data.controller[3] = sticky as u8;
|
||||
}
|
||||
"BYE" => {
|
||||
warn!("asked to exit by the game");
|
||||
|
@ -73,3 +126,42 @@ 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 {
|
||||
let x = (x as f64).powi(2);
|
||||
let y = (y as f64).powi(2);
|
||||
(x + y).sqrt() as i8
|
||||
}
|
||||
|
||||
#[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!(crate::lerp(start as i8, 0, t), case.1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stick_distance() {
|
||||
for case in [
|
||||
(0, 0, 0),
|
||||
(127, 0, 127),
|
||||
(64, 64, 90),
|
||||
(-64, 64, 90),
|
||||
(-64, -64, 90),
|
||||
]
|
||||
.iter()
|
||||
{
|
||||
let x = case.0;
|
||||
let y = case.1;
|
||||
assert_eq!(crate::stick_distance(x, y), case.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,8 +70,6 @@ async fn run_loop(
|
|||
}
|
||||
|
||||
wait_and_join(&mut control, &mut dispatcher, channels).await;
|
||||
let mut stickx: i8 = 0;
|
||||
let mut sticky: i8 = 0;
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
|
@ -84,6 +82,14 @@ async fn run_loop(
|
|||
Some(msg) = pmsg.next() => {
|
||||
let mut hi = HiButtons::NONE;
|
||||
let mut lo = LoButtons::NONE;
|
||||
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();
|
||||
|
|
Loading…
Reference in New Issue