Состояние игрока
Состояние игрока содержит данные, которыми управляет отдельный игрок. Каждый игрок отправляет свои обновления хосту, а хост распространяет итоговую карту состояний по комнате.
Обычно здесь хранятся координаты, направление, анимация, здоровье и счёт игрока.
Список методов
Действия:
gp.multiplayer.definePlayerSchema()- определить синхронизируемые поля игрока. FREEgp.multiplayer.setPlayerInitializer()- инициализировать игроков на хосте. FREEgp.multiplayer.setPlayerState()- обновить состояние текущего игрока. FREEgp.multiplayer.setMode()- выбрать режим синхронизации. FREEgp.multiplayer.onTick()иoffTick()- управлять циклом игровой логики. FREE
Свойства:
gp.multiplayer.myState- получить состояние текущего игрока. FREEgp.multiplayer.playersState- получить карту состояний всех игроков. FREE
События:
playersUpdated- отслеживать обновления карты состояний.becameHost- настроить инициализацию игроков после миграции хоста.
Определение схемы игрока
Определите схему до запуска игры:
- 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 }
}"));
Интерполяция — это плавный переход числового значения между полученными обновлениями. Например, если координата изменилась с x: 100 на x: 150, SDK покажет промежуточные позиции, а не мгновенно переместит объект.
Включайте интерполяцию для значений, которые должны изменяться плавно, например координат. Отключайте её для значений, которые должны применяться сразу, например здоровья, состояния анимации или идентификатора предмета.
Инициализация игроков на хосте
Инициализатор выполняется на хосте и возвращает начальное состояние каждого игрока:
- 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);
Хост выбирается асинхронно. Вызов setPlayerInitializer() после получения роли хоста также инициализирует уже подключённых игроков, для которых ещё нет состояния.
После подключения обработайте случай, когда роль уже выбрана:
- JavaScript
- Unity
await gp.multiplayer.connect({ channelId });
await configurePlayerInitialization();
await GP_Multiplayer.connect(new MultiplayerChannelQuery { channelId = channelId });
ConfigurePlayerInitialization();
Не удаляйте подписку на becameHost: текущий игрок может стать хостом позднее.
Инициализатор также может возвращать 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);
Обновление текущего игрока
Используйте цикл тиков SDK для игровой логики. Следующий пример ожидает начальное состояние, обрабатывает локальное движение и отправляет только поля, которыми управляет игрок:
- 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() принимает частичное состояние. SDK объединяет его с существующим состоянием и не отправляет обновление, если переданные значения не изменились.
Локальное состояние текущего игрока доступно через:
- JavaScript
- Unity
const myState = gp.multiplayer.myState;
GP_Data myState = GP_Multiplayer.myState;
Debug.Log(myState.Data);
Отрисовка всех игроков
playersState — это Map, где ключом является playerId, а значением — последнее состояние игрока:
- 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);
Координаты удалённых игроков уже прошли через буфер интерполяции SDK. Для текущего игрока используется локальное состояние без задержки интерполяции.
Используйте playersUpdated, если игре нужно реагировать на изменение карты состояний вне цикла отрисовки:
- 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);
Подписываться на playersUpdated только ради отрисовки необязательно. Достаточно читать playersState во время рендера.
Выбор режима синхронизации
Выберите режим до запуска игрового цикла:
- JavaScript
- Unity
gp.multiplayer.setMode('smooth');
GP_Multiplayer.setMode(MultiplayerMode.SMOOTH);
Отличия fast и smooth описаны в разделе Режимы синхронизации.
Остановка цикла игрока
Передайте в 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);
Оставайтесь на связи
С другими документами раздела вы можете ознакомиться здесь. Для начала работы с GamePush ознакомьтесь с нашими туториалами.
Сообщество GamePush в Telegram: @gs_community.
Для ваших обращений e-mail: official@gamepush.com
Желаем вам успехов!