gamebridge: Twitch Control #2
|
@ -20,6 +20,7 @@ pub(crate) struct State {
|
||||||
controller: [u8; 4],
|
controller: [u8; 4],
|
||||||
last_got: Box<Instant>,
|
last_got: Box<Instant>,
|
||||||
ok: bool,
|
ok: bool,
|
||||||
|
frame: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) type MTState = Arc<RwLock<State>>;
|
pub(crate) type MTState = Arc<RwLock<State>>;
|
||||||
|
@ -36,6 +37,7 @@ fn main() -> Result<()> {
|
||||||
controller: [0; 4],
|
controller: [0; 4],
|
||||||
last_got: Box::new(Instant::now()),
|
last_got: Box::new(Instant::now()),
|
||||||
ok: true,
|
ok: true,
|
||||||
|
frame: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
Arc::new(RwLock::new(st))
|
Arc::new(RwLock::new(st))
|
||||||
|
@ -48,6 +50,10 @@ fn main() -> Result<()> {
|
||||||
spawn(move || twitch::run(st));
|
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 {
|
loop {
|
||||||
let mut data = [0; 3];
|
let mut data = [0; 3];
|
||||||
debug!("waiting for vblank");
|
debug!("waiting for vblank");
|
||||||
|
@ -58,9 +64,56 @@ fn main() -> Result<()> {
|
||||||
match str {
|
match str {
|
||||||
"OK\n" => {
|
"OK\n" => {
|
||||||
let mut data = st.write().unwrap();
|
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)?;
|
input.write(&data.controller)?;
|
||||||
data.controller[0] = 0;
|
data.controller[0] = 0;
|
||||||
data.controller[1] = 0;
|
data.controller[1] = 0;
|
||||||
|
data.controller[2] = stickx as u8;
|
||||||
|
data.controller[3] = sticky as u8;
|
||||||
}
|
}
|
||||||
"BYE" => {
|
"BYE" => {
|
||||||
warn!("asked to exit by the game");
|
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;
|
wait_and_join(&mut control, &mut dispatcher, channels).await;
|
||||||
let mut stickx: i8 = 0;
|
|
||||||
let mut sticky: i8 = 0;
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
|
@ -84,6 +82,14 @@ async fn run_loop(
|
||||||
Some(msg) = pmsg.next() => {
|
Some(msg) = pmsg.next() => {
|
||||||
let mut hi = HiButtons::NONE;
|
let mut hi = HiButtons::NONE;
|
||||||
let mut lo = LoButtons::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 mut data = msg.data.to_string();
|
||||||
let data = data.to_ascii_lowercase();
|
let data = data.to_ascii_lowercase();
|
||||||
|
|
Loading…
Reference in New Issue