Я пытаюсь создать архитектуру для MMO-игры, и я не могу понять, как я могу хранить столько переменных, сколько мне нужно, в GameObjects без большого количества вызовов, чтобы отправлять их по проводам, в то же время я обновляю их.
Что у меня сейчас есть:
Game::ChangePosition(Vector3 newPos) {
gameobject.ChangePosition(newPos);
SendOnWireNEWPOSITION(gameobject.id, newPos);
}
Это делает код мусором, трудно поддерживать, понимать, расширять. Так что подумайте о примере Чемпиона:
Мне пришлось бы сделать много функций для каждой переменной. И это всего лишь обобщение для этого Чемпиона, у меня может быть 1-2 других переменных-члена для каждого типа / класса Чемпиона.
Было бы идеально, если бы я мог иметь OnPropertyChange из .NET или что-то подобное. Архитектура, которую я пытаюсь угадать, будет хорошо работать, если у меня будет что-то похожее на:
Для HP: когда я обновляю его, автоматически SendFloatOnWire("HP", hp);
Для положения: когда я обновляю его, автоматически SendVector3OnWire("Position", Position)
Для имени: когда я обновляю его, автоматически SendSOnWire("Name", Name);
Что именно SendFloatOnWire
, SendVector3OnWire
, SendSOnWire
? Функции, которые сериализуют эти типы в буфере символов.
ИЛИ МЕТОД 2 (предпочтительно), но может быть дорогим
Обновите Hp, Позиционируйте нормально, а затем каждую галочку Network Thread просканируйте все экземпляры GameObject на сервере на предмет измененных переменных и отправьте их.
Как это будет реализовано на высококлассном игровом сервере и какие у меня варианты? Любая полезная книга для таких случаев?
Макросы окажутся полезными? Я думаю, что я был знаком с некоторым исходным кодом чего-то подобного, и я думаю, что он использовал макросы.
Заранее спасибо.
РЕДАКТИРОВАТЬЯ думаю, что нашел решение, но я не знаю, насколько оно действительно надежно. Я собираюсь пойти на это и посмотреть, где я стою после этого. https://developer.valvesoftware.com/wiki/Networking_Entities
По способу 1:
Такой подход может быть относительно «легким» для реализации с использованием карт, доступ к которым осуществляется через геттеры / сеттеры. Общая идея будет выглядеть примерно так:
class GameCharacter {
map<string, int> myints;
// same for doubles, floats, strings
public:
GameCharacter() {
myints["HP"]=100;
myints["FP"]=50;
}
int getInt(string fld) { return myints[fld]; };
void setInt(string fld, int val) { myints[fld]=val; sendIntOnWire(fld,val); }
};
Если вы предпочитаете сохранять свойства в своем классе, вместо значений вы должны использовать карту с указателями или указателями элементов. При построении вы должны инициализировать карту с соответствующими указателями. Если вы решите изменить переменную-член, вы должны всегда проходить через установщик.
Вы могли бы даже пойти дальше и абстрагировать Champion
сделав его просто набором свойств и поведений, которые будут доступны через карту. Эта архитектура компонента представлена Майк МакШаффри в Кодирование игры завершено (обязательная книга для любого разработчика игр). Есть сайт сообщества для книги с некоторым исходным кодом для загрузки. Вы можете взглянуть на actor.h
а также actor.cpp
файл. Тем не менее, я действительно рекомендую прочитать полные объяснения в книге.
Преимущество компонентной компоновки состоит в том, что вы можете встроить логику сетевой пересылки в базовый класс всех свойств: это может упростить ваш код на порядок.
По способу 2:
Я думаю, что базовая идея идеально подходит, за исключением того, что полный анализ (или, что еще хуже, передача) всех объектов был бы излишним.
Хорошей альтернативой будет иметь маркер, который устанавливается при внесении изменений и сбрасывается при передаче изменений. Если вы передаете помеченные объекты (и, возможно, только помеченные их свойства), вы минимизируете рабочую нагрузку потока синхронизации и снижаете нагрузку на сеть, объединяя передачу нескольких изменений, влияющих на один и тот же объект.
Общий вывод, к которому я пришел: еще один звонок после того, как я обновлю позицию, не так уж и плох. Это строка кода длиннее, но лучше по разным мотивам:
Методы, которые я пробовал:
В конце концов, я использовал смесь 4 и 5. И теперь я внедряю это в свою игру. Одним из огромных преимуществ protobuf является возможность генерировать структуры из файла .proto, а также предлагать сериализацию для структуры. Это невероятно быстро.
Для тех специальных именованных переменных, которые появляются в подклассах, у меня есть другая структура. В качестве альтернативы, с помощью protobuf я мог бы получить массив свойств, таких как: ENUM_KEY_BYTE VALUE
, куда ENUM_KEY_BYTE
это просто байт, который ссылается на enum
к свойствам, таким как IS_FLYING
, IS_UP
, IS_POISONED
, а также VALUE
это string
,
Самая важная вещь, которую я узнал из этого, — это иметь как можно больше сериализации. Лучше использовать больше процессоров на обоих концах, чем иметь больше ввода&Выход.
Если у кого-то есть какие-либо вопросы, прокомментируйте, и я сделаю все возможное, чтобы помочь вам.
ioanb7