Interpolation
Introduction
Interpolation means that we will store replicated entities in a buffer, and then interpolate between the last two states to get a smoother movement.
See this excellent explanation from Valve: link or this one from Gabriel Gambetta: link
Implementation
In lightyear, interpolation can be automatically managed for you.
Every replicated entity can specify to which clients it should be interpolated to:
Replicate {
interpolation_target: NetworkTarget::AllExcept(vec![id]),
..default()
},
This means that all clients except for the one with id id
will interpolate this entity.
In practice, it means that they will store in a buffer the history for all components that are enabled for Interpolation.
Component Sync Mode
Not all components in the protocol are necessarily interpolated.
Each component can implement a ComponentSyncMode
that defines how it gets handled for the Predicted
and Interpolated
entities.
Only components that have ComponentSyncMode::Full
will be interpolated.
Interpolation function
By default, the implementation function for a given component will be linear interpolation. It is also possibly to override this behaviour by implementing a custom interpolation function.
Here is an example:
#[derive(Component, Message, Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct Component1(pub f32);
#[derive(Component, Message, Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct Component2(pub f32);
#[component_protocol(protocol = "MyProtocol")]
pub enum MyComponentProtocol {
#[sync(full)]
Component1(Component1),
#[sync(full, lerp = "MyCustomInterpFn")]
Component2(Component2),
}
// custom interpolation logic
pub struct MyCustomInterpFn;
impl<C> InterpFn<C> for MyCustomInterpFn {
fn lerp(start: C, _other: C, _t: f32) -> C {
start
}
}
You will have to add the attribute lerp = "TYPE_NAME"
to the component.
The TYPE_NAME
must be a type that implements the InterpFn
trait.
pub trait InterpFn<C> {
fn lerp(start: C, other: C, t: f32) -> C;
}
Complex interpolation
In some cases, the interpolation logic can be more complex than a simple linear interpolation. For example, we might want to have different interpolation functions for different entities, even if they have the same component type. Or we might want to do interpolation based on multiple comments (applying some cubic spline interpolation that relies not only on the position, but also on the velocity and acceleration).
In those cases, you can disable the default per-component interpolation logic and provide your own custom logic.
rust,noplayground