Player State
Player state contains data controlled by an individual player. Each player sends their own updates to the host, and the host distributes the resulting state map to the room.
Typical player state fields include position, direction, animation, health, and score.
Method Listβ
Actions:
gp.multiplayer.definePlayerSchema()- define synchronized player fields. FREEgp.multiplayer.setPlayerInitializer()- initialize players on the host. FREEgp.multiplayer.setPlayerState()- update the current player's state. FREEgp.multiplayer.setMode()- select the synchronization mode. FREEgp.multiplayer.onTick()andoffTick()- manage the game logic loop. FREE
Properties:
gp.multiplayer.myState- get the current player's state. FREEgp.multiplayer.playersState- get the state map for all players. FREE
Events:
playersUpdated- track player state map updates.becameHost- configure player initialization after host migration.
Define a Player Schemaβ
Define the schema before starting the game:
- JavaScript
- Unity
gp.multiplayer.definePlayerSchema({
x: { interpolate: true },
y: { interpolate: true },
direction: { interpolate: false },
hp: { interpolate: false },
color: { readonly: true },
});
GP_Multiplayer.definePlayerSchema(new GP_Data(@"{
""x"": { ""interpolate"": true },
""y"": { ""interpolate"": true },
""direction"": { ""interpolate"": false },
""hp"": { ""interpolate"": false },
""color"": { ""readonly"": true }
}"));
Interpolation smoothly transitions a numeric value between received updates. For example, if the coordinate changes from x: 100 to x: 150, the SDK displays the intermediate positions instead of moving the object instantly.
Enable interpolation for values that should change smoothly, such as coordinates. Disable it for values that should be applied immediately, such as health, animation state, or an item identifier.
Initialize Players on the Hostβ
The player initializer runs on the host and returns the initial state for each player:
- JavaScript
- Unity
function getPlayerColor(playerId) {
const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#f7dc6f'];
return colors[playerId % colors.length];
}
function initializePlayer(playerId, playerInfo) {
return {
x: 100 + (playerId % 5) * 60,
y: 200,
direction: 1,
hp: 100,
color: getPlayerColor(playerId),
};
}
async function configurePlayerInitialization() {
if (!gp.multiplayer.isHost) {
return;
}
await gp.multiplayer.setPlayerInitializer(initializePlayer);
}
gp.multiplayer.on('becameHost', configurePlayerInitialization);
GP_Data InitializePlayer(int playerId, MultiplayerConnectedPlayerData player)
{
return new GP_Data($@"{{
""x"": {100 + playerId % 5 * 60},
""y"": 200,
""direction"": 1,
""hp"": 100,
""color"": ""#4ecdc4""
}}");
}
async void ConfigurePlayerInitialization()
{
if (!GP_Multiplayer.isHost)
return;
await GP_Multiplayer.setPlayerInitializer(InitializePlayer);
}
GP_Multiplayer.on("becameHost", ConfigurePlayerInitialization);
Host selection is asynchronous. Calling setPlayerInitializer() after becoming the host also initializes players who are already connected but do not have state yet.
After connecting, handle the case where the role has already been selected:
- JavaScript
- Unity
await gp.multiplayer.connect({ channelId });
await configurePlayerInitialization();
await GP_Multiplayer.connect(new MultiplayerChannelQuery { channelId = channelId });
ConfigurePlayerInitialization();
Keep the becameHost subscription because the current player may become the host later.
The initializer itself may also return a promise:
- JavaScript
- Unity
async function initializePlayer(playerId) {
const spawn = await loadSpawnPoint(playerId);
return {
x: spawn.x,
y: spawn.y,
hp: 100,
color: getPlayerColor(playerId),
};
}
async Task<GP_Data> InitializePlayerAsync(
int playerId,
MultiplayerConnectedPlayerData player)
{
SpawnPoint spawn = await LoadSpawnPoint(playerId);
return new GP_Data($@"{{
""x"": {spawn.x},
""y"": {spawn.y},
""hp"": 100,
""color"": ""#4ecdc4""
}}");
}
await GP_Multiplayer.setPlayerInitializer(InitializePlayerAsync);
Update the Current Playerβ
Use the SDK tick loop for gameplay. The following example waits for the initial state, applies local movement, and sends only the fields controlled by the player:
- JavaScript
- Unity
const keys = new Set();
window.addEventListener('keydown', (event) => keys.add(event.code));
window.addEventListener('keyup', (event) => keys.delete(event.code));
let localX = null;
let localY = null;
let direction = 1;
function updateLocalPlayer(delta) {
const myState = gp.multiplayer.myState;
if (localX === null || localY === null) {
if (!myState) {
return;
}
localX = myState.x;
localY = myState.y;
}
const speed = 200;
const distance = speed * (delta / 1000);
if (keys.has('ArrowLeft')) {
localX -= distance;
direction = -1;
}
if (keys.has('ArrowRight')) {
localX += distance;
direction = 1;
}
gp.multiplayer.setPlayerState({
x: localX,
y: localY,
direction,
});
}
gp.multiplayer.onTick(updateLocalPlayer);
void UpdateLocalPlayer(float delta)
{
float direction = Input.GetAxisRaw("Horizontal");
if (direction == 0)
return;
transform.position += Vector3.right * direction * 200f * (delta / 1000f);
GP_Multiplayer.setPlayerState(new GP_Data($@"{{
""x"": {transform.position.x.ToString(CultureInfo.InvariantCulture)},
""y"": {transform.position.y.ToString(CultureInfo.InvariantCulture)},
""direction"": {Mathf.Sign(direction)}
}}"));
}
GP_Multiplayer.onTick(UpdateLocalPlayer);
setPlayerState() accepts partial state. The SDK merges it with the existing state and does not send an update when the supplied values have not changed.
The current player's local state is available through:
- JavaScript
- Unity
const myState = gp.multiplayer.myState;
GP_Data myState = GP_Multiplayer.myState;
Debug.Log(myState.Data);
Render All Playersβ
playersState is a Map where the key is playerId and the value is the player's latest state:
- JavaScript
- Unity
function renderPlayers() {
for (const [playerId, state] of gp.multiplayer.playersState) {
drawPlayer({
id: playerId,
x: state.x,
y: state.y,
color: state.color,
hp: state.hp,
});
}
requestAnimationFrame(renderPlayers);
}
requestAnimationFrame(renderPlayers);
GP_Data playersState = GP_Multiplayer.playersState;
Debug.Log(playersState.Data);
Remote player coordinates have already passed through the SDK interpolation buffer. The current player uses their local state and is not delayed by interpolation.
Use playersUpdated when the game needs to react to a state-map change outside the render loop:
- JavaScript
- Unity
function onPlayersUpdated(players) {
updatePlayersPanel(players);
}
gp.multiplayer.on('playersUpdated', onPlayersUpdated);
void OnPlayersUpdated(GP_Data players)
{
Debug.Log(players.Data);
}
GP_Multiplayer.on("playersUpdated", OnPlayersUpdated);
It is not necessary to subscribe to playersUpdated only to render players. Reading playersState during rendering is sufficient.
Select a Synchronization Modeβ
Choose a mode before starting the game loop:
- JavaScript
- Unity
gp.multiplayer.setMode('smooth');
GP_Multiplayer.setMode(MultiplayerMode.SMOOTH);
See Synchronization Modes for the difference between fast and smooth.
Stop the Player Loopβ
Pass the same function reference to offTick():
- JavaScript
- Unity
gp.multiplayer.offTick(updateLocalPlayer);
gp.multiplayer.off('playersUpdated', onPlayersUpdated);
gp.multiplayer.off('becameHost', configurePlayerInitialization);
GP_Multiplayer.offTick(UpdateLocalPlayer);
GP_Multiplayer.off("playersUpdated", OnPlayersUpdated);
GP_Multiplayer.off("becameHost", ConfigurePlayerInitialization);
Stay in Touchβ
Other documents of this chapter are available here. To get started with GamePush, see the Tutorials chapter.
GamePush Community Telegram: @gs_community.
For your suggestions e-mail: official@gamepush.com
We Wish you Success!