Як встановити мережу цієї системи?


33

Я створив систему сутності для FPS. Це в основному працює так:

У нас є "світ" -об'єкт, який називається GameWorld. Тут міститься масив GameObject, а також масив ComponentManager.

GameObject містить масив компонентів. Він також забезпечує механізм подій, який дійсно простий. Самі компоненти можуть надсилати подію об'єкту, яка транслюється на всі компоненти.

Компонент - це щось таке, що надає GameObject певні властивості, а оскільки GameObject насправді є лише їх контейнером, все, що стосується ігрового об’єкта, відбувається в компонентах. Приклади включають ViewComponent, PhysicsComponent та LogicComponent. Якщо комунікація між ними потрібна, це можна зробити за допомогою подій.

ComponentManager - це просто інтерфейс, як Component, і для кожного класу Component, як правило, повинен бути один клас ComponentManager. Ці менеджери компонентів відповідають за створення компонентів та ініціалізацію їх із властивостями, прочитаними з чогось на зразок XML-файла.

ComponentManager також піклується про масове оновлення компонентів, як-от PhysicsComponent, де я буду використовувати зовнішню бібліотеку (яка робить все у світі відразу).

Для налаштування я буду використовувати фабрику для сутностей, які будуть читати або XML-файл, або сценарій, створювати компоненти, визначені у файлі (що також додає посилання на нього в правильному менеджері компонентів для масових оновлень), і потім введіть їх у об’єкт GameObject.

Зараз виникає моя проблема: я збираюся спробувати використовувати це для багатокористувацьких ігор. Я поняття не маю, як до цього підійти.

По-перше: які суб'єкти повинні мати клієнти з самого початку? Я повинен почати з того, щоб пояснити, як двигун для одного гравця визначав, які сутності створювати.

У редакторі рівнів можна створити "пензлі" та "сутності". Щітки призначені для таких речей, як стіни, підлога і стеля, в основному прості форми. Суб'єкти - це GameObject, про який я вам розповідав. Створюючи об'єкти в редакторі рівнів, ви можете вказати властивості для кожного з його компонентів. Ці властивості передаються безпосередньо на щось на зразок конструктора в сценарії сутності.

Коли ви зберігаєте рівень для завантаження двигуна, він розкладається на перелік об'єктів та пов'язані з ними властивості. Пензлики перетворюються на сутність "світової фарби".

Коли ви завантажуєте цей рівень, він просто інстанціює всі сутності. Звучить просто, так?

Зараз у зв'язку з мережевими структурами у мене виникають численні проблеми. По-перше, які сутності повинні існувати у клієнта з самого початку? Якщо припустити, що і сервер, і клієнт мають файл рівня, клієнт міг би також запрограмувати всі об'єкти на рівні, навіть якщо вони є тільки для цілей правил гри на сервері.

Інша можливість полягає в тому, що клієнт створює об'єкт, як тільки сервер надсилає інформацію про нього, і це означає, що клієнт матиме лише сутності, які йому потрібні.

Інше питання - як надіслати інформацію. Я думаю, що сервер міг би використовувати дельта-компресію, це означає, що він надсилає нову інформацію лише тоді, коли щось змінюється, а не надсилає знімок клієнту в кожен кадр. Хоча це означає, що сервер повинен слідкувати за тим, що кожен клієнт знає на даний момент.

І нарешті, як слід вводити мережу в двигун? Я думаю про компонент NetworkComponent, який вводиться в кожну сутність, яка повинна бути мережевою. Але як повинен мережевий компонент знати, які змінні в мережі та як отримати доступ до них, і нарешті, як відповідний мережевий компонент клієнта повинен знати, як змінити мережеві змінні?

У мене виникають величезні проблеми підходити до цього. Я був би дуже вдячний, якби ви мені допомогли в дорозі. Я відкритий для порад щодо вдосконалення дизайну компонентної системи, тому не бійтеся пропонувати це.

Відповіді:


13

Це проклятий божевільний (пробачте) звір питання з безліччю деталей +1. Однозначно достатньо, щоб допомогти людям, які натрапляють на неї.

Я просто в основному хотів додати свої 2 центи про не надсилання фізичних даних !! Я, чесно кажучи, не можу наголосити на цьому достатньо. Навіть якщо ви до сих пір оптимізовані, що ви можете практично надіслати 40 сфер, які підстрибують навколо мікро зіткнення, і це може перейти на повну швидкість у тремтячому приміщенні, яке навіть не зменшує частоту кадрів. Я маю на увазі виконання вашої "дельта-стиснення / кодування", також відомої як диференціація даних, про яку ви говорили. Це досить схоже на те, що я збирався виховувати.

Dead Dead Reckoning VS Differencing: Вони досить різні і дійсно не займають однакові методи, це означає, що ви могли б застосувати обидва, щоб ще більше збільшити оптимізацію! Примітка: я не використовував їх обох разом, але працював з обома.

Кодування Delta або розрізнення даних: сервер несе дані про те, що знають клієнти, і надсилає лише розбіжності між старими даними та тим, що слід змінити клієнту. наприклад, псевдо-> в одному прикладі ви можете надіслати дані "315 435 222 3546 33", коли дані вже "310 435 210 4000 40" Деякі лише незначно змінені, а один зовсім не змінений! Замість цього ви б надіслали (у дельті) "5 0 12 -454 -7", що значно коротше.

Кращими прикладами може бути щось, що змінюється набагато далі, ніж, наприклад, скажімо, що у мене зараз пов'язаний список із 45 пов'язаними об'єктами. Я хочу вбити 30 з них, тож я це роблю, а потім надсилаю всім, що таке нові пакетні дані, які б уповільнили сервер, якщо він ще не був побудований для таких дій, і це сталося, тому що він намагався щоб виправити себе, наприклад. У дельта-кодуванні ви просто покладете (псевдо) "list.kill 30 на 5", і це видалить 30 об'єктів зі списку після 5-го, потім автентифікуйте дані, але на кожному клієнті, а не на сервері.

Плюси: (зараз можна думати лише про одного з кожного)

  1. Швидкість: Очевидно, в моєму останньому прикладі, який я описав. Це було б набагато більше різниці, ніж попередній приклад. Взагалі, я не можу чесно сказати з досвіду, хто з них був би більш поширеним, оскільки я працюю набагато більше з мертвими рахунками

Мінуси:

  1. Якщо ви оновлюєте свою систему і хочете додати ще кілька даних, які слід редагувати через дельту, вам доведеться створити нові функції, щоб змінити ці дані! (наприклад, як раніше "list.kill 30 в 5" О, лайно, мені потрібен метод скасування, доданий до клієнта! "list.kill скасувати")

Померлий розплата: просто сказано, ось аналогія. Я пишу карту для когось про те, як дістатися до місця, і я включаю лише пункти, куди поїхати взагалі, тому що це досить добре (зупинка на будівлі, поверніть ліворуч). Мапа когось містить інші назви вулиць, а також скільки градусів повернути ліворуч, це взагалі потрібно? (Ні...)

Мертвий розрахунок - це те, коли кожен клієнт має постійний алгоритм для кожного клієнта. Дані в значній мірі просто змінені, сказавши, які дані потрібно змінити, і як це зробити. Клієнт самостійно змінює дані. Наприклад, якщо у мене є персонаж, який не є моїм гравцем, але його переміщує інша людина, яка грає зі мною, я не повинен би оновлювати дані кожен кадр, оскільки багато даних узгоджуються!

Скажімо, у мене персонаж рухається в якомусь напрямку, багато серверів надсилають клієнтам дані, які говорять (майже за кадром), де знаходиться плеєр, і що він рухається (з анімаційних причин). Ось стільки зайвих даних! Чому, пекло, мені потрібно оновлювати кожен кадр, де знаходиться блок і в якому напрямку він стикається І що він рухається? Простіше кажучи: я ні. Ви оновлюєте клієнтів лише тоді, коли змінюється напрямок, коли змінюється дієслово (isMoving = true?) Та який об’єкт! Тоді кожен клієнт перемістить об’єкт відповідно.

Особисто це тактика здорового глузду. Це те, що я вважав розумним у тому, щоб придумати давно, що, як виявилося, було використано весь час.

Відповіді

Якщо бути відвертим, прочитайте допис Джеймса і прочитайте, що я сказав про дані. Так, ви, безумовно, повинні використовувати дельта-кодування, але подумайте і про використання мертвого рахунку.

Особисто я б створив дані про клієнта, коли він отримує інформацію про нього з сервера (щось, що ви запропонували).

Тільки об'єкти, які можуть змінюватися, коли-небудь слід відзначати як редаговані, чи не так? Мені подобається ваша ідея включення того, що об’єкт повинен мати мережеві дані через вашу складову та сутність системи! Це розумно, і повинно працювати просто чудово. Але ніколи не слід давати пензлі (або будь-які дані, що абсолютно відповідають) будь-яким способам мереж взагалі. Їм вони не потрібні, оскільки це щось, що навіть неможливо змінити (клієнт до клієнта, який є).

Якщо це щось на кшталт дверей, я б надавав їм мережеві дані, але лише булеві дані щодо того, відкриті вони чи ні, то очевидно, що це за об’єкт. Клієнт повинен знати, як його змінити, наприклад, він відкритий, закрити його, кожен клієнт отримує, що вони повинні його закрити, тому ви змінюєте булі дані, а потім анімуєте двері, щоб вони були закриті.

Щодо того, як він повинен знати, які змінні в мережі, я можу мати компонент, який справді є SUB-об'єктом, і надати йому компоненти, які ви хотіли б з'єднати. Інша ідея - це не просто мати, AddComponent("whatever")а й AddNetComponent("and what have you")лише тому, що це звучить розумніше особисто.


Це смішно довга відповідь! Мені страшенно шкода цього. Оскільки я мав намір подати лише невелику кількість знань, а потім свої 2 центи про деякі речі. Тож я розумію, що багато чого з цього питання може бути трохи зайвим.
Джошуа Хеджес

3

Збирався написати коментар, але вирішив, що це може бути достатньо інформації для відповіді.

По-перше, +1 за таке добре написане запитання з тоннами деталей, щоб оцінити відповідь.

Для завантаження даних я б змусив клієнта завантажити світ із світового файлу. Якщо у ваших організацій є ідентифікатори, які надходять з файлу даних, я б також завантажував їх за замовчуванням, щоб ваша мережна система могла просто посилатися на них, щоб знати, про які об’єкти йдеться. Кожен, що завантажує однакові вихідні дані, повинен означати, що всі вони мають однакові ідентифікатори для цих об'єктів.

По-друге, не створюйте компонент NetworkComponent, оскільки це не зробить нічого, крім копіювання даних в інших існуючих компонентах (фізика, анімація тощо) - це звичайні речі, які потрібно надіслати поперек). Для використання власного іменування ви можете створити NetworkComponentManager. Це було б трохи відірвано від інших відносин компонент-компонент-менеджер, але це може бути створено при запуску ігрової мережі та будь-якому типі компонентів, які мають мережевий аспект, передають свої дані менеджеру, щоб він міг упакувати їх і відправити його. Тут можна використовувати вашу функцію збереження / завантаження, якщо у вас є якийсь механізм серіалізації / десеріалізації, який ви також можете використовувати для упаковки даних для, як згадувалося,

Зважаючи на ваше запитання та рівень інформації, я не думаю, що мені потрібно більше деталізуватись, але якщо щось не зрозуміло, напишіть коментар, і я оновлю відповідь, щоб вирішити це.

Сподіваюся, це допомагає.


Отже, те, що ви говорите, полягає в тому, що компоненти, які повинні бути мережевими, повинні реалізувати такий інтерфейс, як цей ?: void SetNetworkedVariable (назва рядка, значення NetworkedVariable); NetworkedVariable GetNetworkedVariable (назва рядка); Якщо NetworkedVariable використовується для інтерполяції та інших мережевих матеріалів. Я не знаю, як визначити, які компоненти реалізують це, хоча. Я міг би використати ідентифікацію типу виконання, але це здається мені некрасивим.
Картер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.