Перейти к основному содержимому

Глобальное состояние

Глобальное состояние содержит общие игровые сущности и значения, которыми управляет только хост:

  • враги и NPC;
  • предметы на карте;
  • состояние и таймеры раунда;
  • общий счёт и цели.

Глобальное состояние синхронизируется отдельно от состояния игроков.

Список методов​

Действия:

Свойства:

События:

  • globalStateUpdated - отслеживать обновления глобального состояния на пирах.
  • becameHost и becamePeer - запускать и останавливать логику хоста.

Определение глобальной схемы​

Глобальное состояние использует те же настройки схемы, что и состояние игрока:

gp.multiplayer.defineGlobalSchema({
enemies: {
id: { readonly: true },
type: { readonly: true },
x: { interpolate: true },
y: { interpolate: true },
hp: { interpolate: false },
},
items: {
id: { readonly: true },
type: { readonly: true },
x: { interpolate: false },
y: { interpolate: false },
},
round: {
number: { interpolate: false },
timeLeft: { interpolate: true },
status: { interpolate: false },
},
});

Включайте интерполяцию для значений, которые должны изменяться плавно, например координат. Отключайте её для значений, которые должны применяться сразу, например здоровья, состояния анимации или идентификатора предмета.

Создание состояния на хосте​

Вызывать setGlobalState() может только хост:

let hostState = null;

function startHostLogic() {
hostState = gp.multiplayer.globalState ?? {
enemies: [],
items: [],
round: {
number: 1,
timeLeft: 60,
status: 'playing',
},
};

gp.multiplayer.setGlobalState(hostState);
}

function stopHostLogic() {
hostState = null;
}

gp.multiplayer.on('becameHost', startHostLogic);
gp.multiplayer.on('becamePeer', stopHostLogic);

В JavaScript setGlobalState() сохраняет ссылку на переданный объект, поэтому хост может изменять этот объект напрямую. В Unity после изменения C#-модели вызовите setGlobalState(), чтобы передать SDK её последнее сериализованное значение.

function spawnEnemy() {
if (!gp.multiplayer.isHost || !hostState) {
return;
}

hostState.enemies.push({
id: crypto.randomUUID(),
type: 'slime',
x: 100,
y: 200,
hp: 50,
});
}

function damageEnemy(enemyId, damage) {
if (!gp.multiplayer.isHost || !hostState) {
return;
}

const enemy = hostState.enemies.find((item) => item.id === enemyId);

if (enemy) {
enemy.hp = Math.max(0, enemy.hp - damage);
}
}

В JavaScript повторно вызывать setGlobalState() после каждого push, splice или изменения поля не требуется. Хост отправляет содержимое объекта с частотой выбранного режима. В Unity после изменения C#-модели снова передайте её сериализованное состояние.

Обновление общих сущностей​

Выполняйте авторитетную логику общего мира только на хосте:

function updateGlobalState(delta) {
if (!gp.multiplayer.isHost || !hostState) {
return;
}

const seconds = delta / 1000;

for (const enemy of hostState.enemies) {
enemy.x += 40 * seconds;
}

hostState.round.timeLeft = Math.max(
0,
hostState.round.timeLeft - seconds,
);
}

gp.multiplayer.onTick(updateGlobalState);

Пиры не должны изменять gp.multiplayer.globalState. Они получают интерполированный снимок для отрисовки.

Чтение глобального состояния​

Последнее глобальное состояние доступно хосту и пирам:

function renderWorld() {
const globalState = gp.multiplayer.globalState;

if (!globalState) {
return;
}

for (const enemy of globalState.enemies) {
drawEnemy(enemy);
}

for (const item of globalState.items) {
drawItem(item);
}
}

Пиры могут реагировать на изменения через globalStateUpdated:

function onGlobalStateUpdated(globalState) {
updateRoundInterface(globalState.round);
}

gp.multiplayer.on('globalStateUpdated', onGlobalStateUpdated);

Обработчик события получает непосредственно объект глобального состояния. Событие вызывается на пирах после интерполяции; хост уже владеет актуальной ссылкой на состояние.

Миграция хоста​

Когда пир становится новым хостом, gp.multiplayer.globalState содержит последний полученный им снимок. Передайте этот объект в setGlobalState(), чтобы сделать его новой живой ссылкой хоста:

function startHostLogic() {
const latestState = gp.multiplayer.globalState;

hostState = latestState ?? {
enemies: [],
items: [],
round: {
number: 1,
timeLeft: 60,
status: 'playing',
},
};

gp.multiplayer.setGlobalState(hostState);
}

Так общий мир продолжит работу с последнего известного состояния, а не перезапустится после миграции хоста.

После подключения также обработайте случай, когда роль уже выбрана:

await gp.multiplayer.connect({ channelId });

if (gp.multiplayer.isHost) {
startHostLogic();
}

Сохраняйте подписку на becameHost, поскольку миграция хоста может произойти позднее.

Очистка​

gp.multiplayer.offTick(updateGlobalState);
gp.multiplayer.off('globalStateUpdated', onGlobalStateUpdated);
gp.multiplayer.off('becameHost', startHostLogic);
gp.multiplayer.off('becamePeer', stopHostLogic);

hostState = null;

Оставайтесь на связи​

С другими документами раздела вы можете ознакомиться здесь. Для начала работы с GamePush ознакомьтесь с нашими туториалами.

Сообщество GamePush в Telegram: @gs_community.

Для ваших обращений e-mail: official@gamepush.com

Желаем вам успехов!