Tutorials

Многопользовательские игры в реальном времени с Photon

Кликните по полу, чтобы переместиться

Полный проект, включая матчмейкинг, можно найти здесь.

Photon (также известный как PUN) используется во многих играх и имеет доступный JavaScript SDK для игр на HTML5.

Photon бесплатен для проектов с не более чем 20 онлайн-игроками (CCU).

Вы узнаете

Настройка

Проект PlayCanvas

Мы начинаем с создания форка учебного проекта здесь.

Пустой проект

Учетная запись Photon

Для использования SDK и просмотра документации требуется регистрация учетной записи.

Создайте свою учетную запись Photon здесь - (Photon Engine).

Создать новое приложение

Нажмите CREATE NEW APP на панели инструментов

Создать новое приложение

Выберите тип Photon и имя приложения.

Введите следующее

Создать проект Real Time

Копирование AppID

Пожалуйста, запишите этот AppId, так как он вам понадобится в будущем.

App Id

Загрузка SDK

Загрузите SDK с панели инструментов.

Нажмите SDK на панели инструментов

SDK

Выберите RealTime JavaScript

JavaScript SDK

Нажмите Загрузить SDK

Загрузить SDK

Распакуйте SDK

Распаковать SDK

SDK будет загружен в формате ZIP, распакуйте его: photon-javascript-sdk_vX-X-X-XlibPhoton-Javascript_SDK.min.js.

Импорт SDK

Импортируйте только что загруженный SDK в редактор PlayCanvas.

Загрузите SDK в редакторе

Загрузить SDK

Перетащите и поместите SDK в редакторе ассетов.

Измените тип загрузки "Asset" на "Before Engine"

Изменить тип загрузки

Реализация многопользовательской игры

Реализация многопользовательской игры будет делать следующее:

  1. Использовать класс Photon для общения в реальном времени и балансировки нагрузки
  2. Подключение к мастер-серверу Photon
  3. Создание или присоединение к комнате
  4. Синхронизация действий и движений других игроков

Справочник API и глоссарий доступны на сайте Photon.

Использование Photon с PlayCanvas

Создание экземпляров классов из PlayCanvas для использования Photon

Создайте скриптовый ассет с именем photon-loadbalancing-playcanvas.js в проекте для инициализации Photon.

// photon-loadbalancing-playcanvas.js
const PhotonLoadBalancingPlayCanvas = pc.createScript("PhotonLoadBalancingPlayCanvas");
PhotonLoadBalancingPlayCanvas.attributes.add("appId", { type: "string" });
PhotonLoadBalancingPlayCanvas.attributes.add("appVersion", { type: "string", default: "1.0" });
PhotonLoadBalancingPlayCanvas.attributes.add("wss", { type: "boolean", default: true });
PhotonLoadBalancingPlayCanvas.attributes.add("region", {
    type: "string", default: "jp",
    description: "Photon Cloud имеет серверы в нескольких регионах, распределенных по нескольким хостинг-центрам по всему миру. Вы можете выбрать оптимизированный регион для себя.",
    enum: [
        { "Выберите регион": "default" },
        { "Азия, Сингапур": "asia" },
        { "Австралия, Мельбурн": "au" },
        { "Канада, Восток Монреаль": "cae" },
        { "Китайский материк (См. инструкции) Шанхай": "cn" },
        { "Европа, Амстердам": "eu" },
        { "Индия, Ченнай": "in" },
        { "Япония, Токио": "jp" },
        { "Россия Москва": "ru" },
        { "Россия, Восток Хабаровск": "rue" },
        { "Южная Африка Йоханнесбург": "za" },
        { "Южная Америка, Сан-Паулу": "sa" },
        { "Южная Корея, Сеул": "kr" },
        { "Турция Стамбул": "tr" },
        { "США, Восток Вашингтон": "us" },
        { "США, Запад Сан-Хосе": "usw" },
    ],
});

PhotonLoadBalancingPlayCanvas.prototype.initialize = function () {

    // Настройки Photon
    this.loadBalancingClient = new Photon.LoadBalancing.LoadBalancingClient(this.wss ? 1 : 0, this.appId, this.appVersion);

    // pc.Application
    this.loadBalancingClient.app = this.app;
};

Установить скрипт для корневой сущности

Создайте новый ассет скрипта photon-loadbalancing-playcanvas.js и прикрепите его к корневой сущности в редакторе.

Root Entity - Inspector

Вставьте AppId в атрибут скрипта.

Введите AppId в качестве атрибута скрипта.

Script Attributes

this.loadBalancingClient = new Photon.LoadBalancing.LoadBalancingClient( this.wss ? 1 : 0, this.appId, this.appVersion );

Подключение к мастер-серверу Photon

Подключение к мастер-серверу с использованием connectToRegionMaster

PhotonLoadBalancingPlayCanvas.prototype.initialize = function () {

    // Настройки Photon
    this.loadBalancingClient = new Photon.LoadBalancing.LoadBalancingClient(this.wss ? 1 : 0, this.appId, this.appVersion);

    // pc.Application
    this.loadBalancingClient.app = this.app;

    // Подключение к мастер-серверу
    if (!this.loadBalancingClient.isInLobby()) {
        this.loadBalancingClient.connectToRegionMaster(this.region);
    }
};

Если вы успешно подключитесь к лобби, выполнив connectToRegionMaster, в журнале будет отображаться JoinedLobby.

Console Log

Создать или присоединиться к комнате

Функция onRoomList вызывается при подключении к лобби.

JoinRandomOrCreateRoom для присоединения к комнате, если она существует, или случайного присоединения к комнате, если она не существует.

PhotonLoadBalancingPlayCanvas.prototype.initialize = function () {

    // Настройки Photon
    this.loadBalancingClient = new Photon.LoadBalancing.LoadBalancingClient(this.wss ? 1 : 0, this.appId, this.appVersion);

    // pc.Application
    this.loadBalancingClient.app = this.app;

    // Подключение к мастер-серверу
    if (!this.loadBalancingClient.isInLobby()) {
        this.loadBalancingClient.connectToRegionMaster(this.region);
    }

    // Добавлено
    this.loadBalancingClient.onRoomList = this.onRoomList;
    this.loadBalancingClient.onJoinRoom = this.onJoinRoom;
};

PhotonLoadBalancingPlayCanvas.prototype.onRoomList = function () {
    this.joinRandomOrCreateRoom();
};

PhotonLoadBalancingPlayCanvas.prototype.onJoinRoom = function (createdByMe) {
    console.log("Присоединились к комнате.");
};

Присоединение и выход

Когда игрок присоединяется к комнате, это синхронизируется с другими игроками. Используйте onActorJoin и onActorLeave.

PhotonLoadBalancingPlayCanvas.prototype.initialize = function () {

    // Настройки Photon
    this.loadBalancingClient = new Photon.LoadBalancing.LoadBalancingClient(this.wss ? 1 : 0, this.appId, this.appVersion);

    // pc.Application
    this.loadBalancingClient.app = this.app;

    // Подключение к мастер-серверу
    if (!this.loadBalancingClient.isInLobby()) {
        this.loadBalancingClient.connectToRegionMaster(this.region);
    }

    this.loadBalancingClient.onRoomList = this.onRoomList;
    this.loadBalancingClient.onJoinRoom = this.onJoinRoom;

    // Добавлено
    this.loadBalancingClient.onActorJoin = this.onActorJoin;
    this.loadBalancingClient.onActorLeave = this.onActorLeave;
};

PhotonLoadBalancingPlayCanvas.prototype.onRoomList = function () {
    this.joinRandomOrCreateRoom();
};

PhotonLoadBalancingPlayCanvas.prototype.onJoinRoom = function (createdByMe) {
    console.log("Присоединился к комнате.");
};

PhotonLoadBalancingPlayCanvas.prototype.onActorJoin = function (actor) {
    const { actorNr } = actor;
    if (actor.isLocal) return;
    const otherPlayer = new pc.Entity();
    otherPlayer.addComponent("render", { type: "capsule" });
    otherPlayer.setLocalPosition(0, 1, 0);
    otherPlayer.name = actorNr;
    this.app.root.children[0].addChild(otherPlayer);
};

PhotonLoadBalancingPlayCanvas.prototype.onActorLeave = function (actor) {
    const { actorNr } = actor;
    const otherPlayer = this.app.root.children[0].findByName(actorNr);
    if (actor.isLocal || !otherPlayer) return;
    otherPlayer.destroy();
};

Actor

Если успешно, сущность добавляется при присоединении игрока. Console log - Actors

Движение игрока

Создайте новый player.js для движения персонажа.

const Player = pc.createScript("player");

Player.prototype.update = function (dt) {
    const pos = new pc.Vec3(0, 0, 0);
    if (this.app.keyboard.isPressed(pc.KEY_LEFT)) {
        pos.x = -dt;
    }
    if (this.app.keyboard.isPressed(pc.KEY_RIGHT)) {
        pos.x = dt;
    }
    if (this.app.keyboard.isPressed(pc.KEY_UP)) {
        pos.z = -dt;
    }
    if (this.app.keyboard.isPressed(pc.KEY_DOWN)) {
        pos.z = dt;
    }
    if (!pos.equals(new pc.Vec3(0, 0, 0))) {
        this.entity.translate(pos);
    }
};

Синхронизация других игроков

Используйте raiseEvent и onEvent для синхронизации местоположения игрока.

Синхронизация позиции с использованием raiseEvent.

const PhotonLoadBalancingPlayCanvas = pc.createScript("PhotonLoadBalancingPlayCanvas");
PhotonLoadBalancingPlayCanvas.attributes.add("appId", { type: "string" });
PhotonLoadBalancingPlayCanvas.attributes.add("appVersion", {
    type: "string",
    default: "1.0",
});
PhotonLoadBalancingPlayCanvas.attributes.add("wss", {
    type: "boolean",
    default: true,
});
PhotonLoadBalancingPlayCanvas.attributes.add("region", {
    type: "string",
    default: "jp",
    description:
        "Photon Cloud имеет серверы в нескольких регионах, распределенных по нескольким хостинг-центрам по всему миру. Вы можете выбрать оптимизированный регион для себя.",
    enum: [
        { "Выберите регион": "default" },
        { "Азия, Сингапур": "asia" },
        { "Австралия, Мельбурн": "au" },
        { "Канада, Восток Монреаль": "cae" },
        { "Китайский материк (См. инструкции) Шанхай": "cn" },
        { "Европа, Амстердам": "eu" },
        { "Индия, Ченнай": "in" },
        { "Япония, Токио": "jp" },
        { "Россия Москва": "ru" },
        { "Россия, Восток Хабаровск": "rue" },
        { "Южная Африка Йоханнесбург": "za" },
        { "Южная Америка, Сан-Паулу": "sa" },
        { "Южная Корея, Сеул": "kr" },
        { "Турция Стамбул": "tr" },
        { "США, Восток Вашингтон": "us" },
        { "США, Запад Сан-Хосе": "usw" },
    ],
});

PhotonLoadBalancingPlayCanvas.prototype.initialize = function () {

    // Настройки Photon
    this.loadBalancingClient = new Photon.LoadBalancing.LoadBalancingClient(this.wss ? 1 : 0, this.appId, this.appVersion);

    // pc.Application
    this.loadBalancingClient.app = this.app;

    // Подключение к мастер-серверу
    if (!this.loadBalancingClient.isInLobby()) {
        this.loadBalancingClient.connectToRegionMaster(this.region);
    }
    this.loadBalancingClient.onRoomList = this.onRoomList;
    this.loadBalancingClient.onJoinRoom = this.onJoinRoom;
    this.loadBalancingClient.onActorJoin = this.onActorJoin;
    this.loadBalancingClient.onActorLeave = this.onActorLeave;

    // Добавлено
    this.loadBalancingClient.onEvent = this.onEvent;
    this.app.on("createOtherPlayerEntity", this.createOtherPlayerEntity, this);
    this.app.on("loadbalancing:sendPlayerPosition", this.sendPlayerPosition, this);
};

PhotonLoadBalancingPlayCanvas.prototype.onRoomList = function () {
    this.joinRandomOrCreateRoom();
};

PhotonLoadBalancingPlayCanvas.prototype.onJoinRoom = function (createdByMe) {
    this.myRoomActorsArray().forEach((actor) => {
        if (actor.isLocal) return;
        this.app.fire("createOtherPlayerEntity", actor);
    });
};

PhotonLoadBalancingPlayCanvas.prototype.onActorJoin = function (actor) {
    if (actor.isLocal) return;
    this.app.fire("createOtherPlayerEntity", actor);
    const { x, y, z } = this.app.root.findByName("Player").getLocalPosition();
    this.app.fire("loadbalancing:sendPlayerPosition", { x, y, z });
};

PhotonLoadBalancingPlayCanvas.prototype.onActorLeave = function (actor) {
    const { actorNr } = actor;
    const otherPlayer = this.app.root.findByName(actorNr);
    if (actor.isLocal || !otherPlayer) return;
    otherPlayer.destroy();
};

PhotonLoadBalancingPlayCanvas.prototype.createOtherPlayerEntity = function (actor) {
    const { actorNr } = actor;
    const entity = new pc.Entity();
    entity.addComponent("render", { type: "capsule" });
    entity.setLocalPosition(0, 1, 0);
    entity.name = actorNr;
    this.app.root.children[0].addChild(entity);
};

PhotonLoadBalancingPlayCanvas.prototype.sendPlayerPosition = function (position) {
    this.loadBalancingClient.raiseEvent(1, { position });
};

PhotonLoadBalancingPlayCanvas.prototype.onEvent = function (code, content, actorNr) {
    switch (code) {
        case 1: {
            const otherPlayer = this.app.root.findByName(actorNr);
            if (otherPlayer) {
                const { x, y, z } = content.position;
                otherPlayer.setLocalPosition(x, y, z);
            }
            break;
        }
        default:
    }
};

Изменено на отправку событий при перемещении игрока

const Player = pc.createScript("игрок");

Player.prototype.update = function (dt) {
    const pos = new pc.Vec3(0, 0, 0);
    if (this.app.keyboard.isPressed(pc.KEY_LEFT)) {
        pos.x = -dt;
    }
    if (this.app.keyboard.isPressed(pc.KEY_RIGHT)) {
        pos.x = dt;
    }
    if (this.app.keyboard.isPressed(pc.KEY_UP)) {
        pos.z = -dt;
    }
    if (this.app.keyboard.isPressed(pc.KEY_DOWN)) {
        pos.z = dt;
    }
    if (!pos.equals(new pc.Vec3(0, 0, 0))) {
        this.entity.translate(pos);

        // Добавлено
        const { x, y, z } = this.entity.getPosition();
        this.app.fire("loadbalancing:sendPlayerPosition", { x, y, z });
    }
};

Готово!

Теперь вы можете играть в многопользовательский режим в Photon!

Проект

Вы можете создать комнату с помощью Photon и синхронизировать позиции игроков друг с другом.

Хотя этот проект был лишь простым обменом сообщениями в реальном времени между игроками, вы также можете создать проект, включающий матчмейкинг. Для полного проекта, включая создание комнат и списки комнат, пожалуйста, нажмите здесь.

This site is translated by the community. If you want to get involved visit this page