About
The Interpolation Buffer is by default 3 server frames long (Interpolation between 4 Snapshots).
So if the latency is 50ms and one server frame is 50ms, the Interpolation Buffer would be 200ms long.
If you are interested to learn a bit more about Snapshot Interpolation, watch this video.
Features
- Easily add Client-Side Prediction and Server Reconciliation.
- Easily add Lag Compensation.
- Easily compress/encode your snapshots before sending/receiving.
Introduction
Take a look at these YouTube videos:
Game Example
The github repository contains a nice example. Take a look!
$ git clone https://github.com/geckosio/snapshot-interpolation.git
$ cd snapshot-interpolation
$ npm install
$ npm start
http://localhost:8080/
Install
Install from npm.
npm install @geckos.io/snapshot-interpolation
Or use the bundled version.
<script src="https://unpkg.com/@geckos.io/snapshot-interpolation@1.0.1/bundle/snapshot-interpolation.js"></script>
<script>
const { SnapshotInterpolation, Vault } = Snap
</script>
How to use
server.js
import { SnapshotInterpolation } from '@geckos.io/snapshot-interpolation'
const SI = new SnapshotInterpolation()
update() {
const snapshot = SI.snapshot.create(worldState)
SI.vault.add(snapshot)
this.emit('update', snapshot)
}
client.js
import { SnapshotInterpolation } from '@geckos.io/snapshot-interpolation'
const SI = new SnapshotInterpolation(serverFPS)
this.on('update', (snapshot) => {
SI.snapshot.add(snapshot)
}
update() {
const snapshot = SI.calcInterpolation('x y')
const { state } = snapshot
const { id, x, y } = state[0]
if (hero.id === id) {
hero.x = x
hero.y = y
}
}
World State
The World State has to be an Array with non nested Objects (except for Quaternions). You can name you keys as you want. For degree, radian or quaternion values add key(deg)
, key(rad)
or key(quat)
.
Linear Interpolation
const worldState = [
{ id: 'heroBlue', x: 23, y: 14, z: 47 },
{ id: 'heroRed', x: 23, y: 14, z: 47 },
{ id: 'heroGreen', x: 23, y: 14, z: 47 }
]
SI.calcInterpolation('x y z')
Degrees, Radians and Quaternions
const worldState = [
{
id: 'myHero',
direction: Math.PI / 2,
rotationInDeg: 90,
q: { x: 0, y: 0.707, z: 0, w: 0.707 }
}
]
SI.calcInterpolation('direction(rad) rotationInDeg(deg) q(quat)')
Multiple States
const playersState = [
{ id: 0, x: 65, y: -17 },
{ id: 1, x: 32, y: 9 }
]
const towersState = [
{ id: 0, health: 100, type: 'blizzardCannon' },
{ id: 1, health: 89, type: 'flameThrower' }
]
const worldState = {
players: playersState,
towers: towersState
}
SI.snapshot.create(worldState)
SI.calcInterpolation('x y', 'players')
Vault
The Vault holds and secures all your Snapshots. Each SnapshotInterpolation instance holds one Vault, but you can easily create more if you need:
import { Vault } from '@geckos.io/snapshot-interpolation'
const customVault = new Vault()
Compression
You can use the package @geckos.io/typed-array-buffer-schema to compress your snapshots before sending them to the client.
const playerSchema = BufferSchema.schema('player', {
id: uint8,
x: { type: int16, digits: 1 },
y: { type: int16, digits: 1 }
})
const snapshotSchema = BufferSchema.schema('snapshot', {
id: { type: string8, length: 6 },
time: uint64,
state: { players: [playerSchema] }
})
export const snapshotModel = new Model(snapshotSchema)
const snapshot = SI.snapshot.create({
players: [
{ id: 0, x: 17.5, y: -1.1 },
{ id: 1, x: 5.8, y: 18.9 }
]
})
const buffer = snapshotModel.toBuffer(snapshot)
send(buffer)
receive(buffer => {
snapshot = snapshotModel.fromBuffer(buffer)
SI.addSnapshot(snapshot)
})
API
This looks very TypeScriptisch, but you can of course use it in JavaScript as well.
import { SnapshotInterpolation, Vault, Types } from '@geckos.io/snapshot-interpolation'
type Value = number | string | Quat | undefined
type ID = string
type Time = number
type State = Entity[]
type Quat = { x: number; y: number; z: number; w: number }
export interface Entity {
id: string
[key: string]: Value
}
export interface Snapshot {
id: ID
time: Time
state: State | { [key: string]: State }
}
export interface InterpolatedSnapshot {
state: State
percentage: number
older: ID
newer: ID
}
SnapshotInterpolation.CreateSnapshot(state: State): Snapshot
SnapshotInterpolation.NewId(): string
SnapshotInterpolation.Now(): number
const SI = new SnapshotInterpolation(serverFPS?: number)
SI.vault: Vault
SI.interpolationBuffer.get(): number
SI.interpolationBuffer.set(milliseconds: number): void
SI.snapshot.create(state: State): Snapshot
SI.snapshot.add(snapshot: Snapshot): void
SI.interpolate(snapshotA: Snapshot, snapshotB: Snapshot, timeOrPercentage: number, parameters: string, deep: string): InterpolatedSnapshot
SI.calcInterpolation(parameters: string, deep: string): InterpolatedSnapshot | undefined
const vault = new Vault()
vault.getById(id: ID): Snapshot
vault.get(): Snapshot | undefined;
vault.get(time: number): { older: Snapshot; newer: Snapshot; } | undefined
vault.get(time: number, closest: boolean): Snapshot | undefined
vault.add(snapshot: Snapshot): void
vault.size(): number
vault.setMaxSize(size: number): void
vault.getMaxSize(): number