របៀបដែលយើងបង្កើតមេកានិចនៃការគណនាផ្លោងសម្រាប់អ្នកបាញ់តាមទូរស័ព្ទជាមួយនឹងក្បួនដោះស្រាយការពន្យាពេលបណ្តាញ

របៀបដែលយើងបង្កើតមេកានិចនៃការគណនាផ្លោងសម្រាប់អ្នកបាញ់តាមទូរស័ព្ទជាមួយនឹងក្បួនដោះស្រាយការពន្យាពេលបណ្តាញ

សួស្តី ខ្ញុំគឺ Nikita Brizhak អ្នកបង្កើតម៉ាស៊ីនមេពី Pixonic ។ ថ្ងៃនេះខ្ញុំចង់និយាយអំពីការប៉ះប៉ូវសម្រាប់ភាពយឺតយ៉ាវក្នុងអ្នកលេងច្រើនទូរស័ព្ទ។

អត្ថបទជាច្រើនត្រូវបានសរសេរអំពីសំណងភាពយឺតយ៉ាវរបស់ម៉ាស៊ីនមេ រួមទាំងជាភាសារុស្សីផងដែរ។ នេះមិនមែនជារឿងគួរឱ្យភ្ញាក់ផ្អើលនោះទេ ចាប់តាំងពីបច្ចេកវិទ្យានេះត្រូវបានប្រើប្រាស់យ៉ាងសកម្មក្នុងការបង្កើត FPS អ្នកលេងច្រើនចាប់តាំងពីចុងទសវត្សរ៍ទី 90 ។ ជាឧទាហរណ៍ អ្នកអាចចងចាំនូវ mod QuakeWorld ដែលជាកម្មវិធីដំបូងគេដែលប្រើវា។

យើងក៏ប្រើវានៅក្នុងឧបករណ៍បាញ់ប្រហារតាមទូរស័ព្ទចល័តជាច្រើនរបស់ Dino Squad ។

នៅក្នុងអត្ថបទនេះ គោលដៅរបស់ខ្ញុំគឺមិនមែនចង់និយាយឡើងវិញនូវអ្វីដែលបានសរសេររួចហើយមួយពាន់ដងនោះទេ ប៉ុន្តែដើម្បីប្រាប់ពីរបៀបដែលយើងអនុវត្តសំណងយឺតយ៉ាវនៅក្នុងហ្គេមរបស់យើង ដោយគិតគូរពីជង់បច្ចេកវិទ្យា និងមុខងារសំខាន់ៗនៃការលេងហ្គេមរបស់យើង។

ពាក្យពីរបីអំពី Cortex និងបច្ចេកវិទ្យារបស់យើង។

Dino Squad គឺជាអ្នកបាញ់ PvP ចល័តតាមបណ្តាញ។ អ្នកលេងគ្រប់គ្រងដាយណូស័រដែលបំពាក់ដោយអាវុធជាច្រើនប្រភេទ ហើយប្រយុទ្ធគ្នាជាក្រុម 6v6។

ទាំងម៉ាស៊ីនភ្ញៀវ និងម៉ាស៊ីនមេគឺផ្អែកលើ Unity ។ ស្ថាបត្យកម្មគឺមានលក្ខណៈបុរាណសម្រាប់អ្នកបាញ់ប្រហារ៖ ម៉ាស៊ីនមេគឺផ្តាច់ការ ហើយការទស្សន៍ទាយអតិថិជនដំណើរការលើអតិថិជន។ ការក្លែងធ្វើហ្គេមត្រូវបានសរសេរដោយប្រើ ECS ក្នុងផ្ទះ ហើយត្រូវបានប្រើប្រាស់ទាំងនៅលើម៉ាស៊ីនមេ និងម៉ាស៊ីនភ្ញៀវ។

ប្រសិនបើនេះជាលើកដំបូងដែលអ្នកបានឮអំពីសំណងយឺតយ៉ាវ នេះគឺជាដំណើរកំសាន្តខ្លីៗអំពីបញ្ហានេះ។

នៅក្នុងហ្គេម FPS ដែលមានអ្នកលេងច្រើន ការប្រកួតជាធម្មតាត្រូវបានក្លែងធ្វើនៅលើម៉ាស៊ីនមេពីចម្ងាយ។ អ្នកលេងផ្ញើការបញ្ចូលរបស់ពួកគេ (ព័ត៌មានអំពីគ្រាប់ចុចដែលបានចុច) ទៅម៉ាស៊ីនមេ ហើយជាការឆ្លើយតប សឺវើរបញ្ជូនពួកគេនូវស្ថានភាពហ្គេមដែលបានធ្វើបច្ចុប្បន្នភាពដោយគិតគូរពីទិន្នន័យដែលទទួលបាន។ ជាមួយនឹងគ្រោងការណ៍អន្តរកម្មនេះ ការពន្យាពេលរវាងការចុចគ្រាប់ចុចទៅមុខ និងពេលដែលតួអង្គអ្នកលេងផ្លាស់ទីនៅលើអេក្រង់នឹងតែងតែធំជាង ping ។

ខណៈពេលដែលនៅលើបណ្តាញក្នុងស្រុក ការពន្យារពេលនេះ (ដែលគេនិយមហៅថា input lag) ប្រហែលជាមិនអាចកត់សម្គាល់បាន នៅពេលដែលលេងតាមរយៈអ៊ីនធឺណិត វាបង្កើតអារម្មណ៍ "រអិលលើទឹកកក" នៅពេលគ្រប់គ្រងតួអក្សរ។ បញ្ហានេះមានភាពពាក់ព័ន្ធទ្វេដងសម្រាប់បណ្តាញទូរស័ព្ទចល័ត ដែលករណីនៅពេលដែល ping របស់អ្នកលេងគឺ 200 ms នៅតែត្រូវបានចាត់ទុកថាជាការតភ្ជាប់ដ៏ល្អ។ ជាញឹកញាប់ ping អាចមាន 350, 500, ឬ 1000 ms ។ បន្ទាប់មកវាស្ទើរតែមិនអាចទៅរួចទេក្នុងការលេងឧបករណ៍បាញ់រហ័សជាមួយនឹងភាពយឺតយ៉ាវនៃការបញ្ចូល។

ដំណោះស្រាយចំពោះបញ្ហានេះគឺការទស្សន៍ទាយការក្លែងធ្វើផ្នែកខាងអតិថិជន។ នៅទីនេះ អតិថិជនខ្លួនវាអនុវត្តការបញ្ចូលទៅតួអក្សរអ្នកលេង ដោយមិនរង់ចាំការឆ្លើយតបពីម៉ាស៊ីនមេ។ ហើយនៅពេលដែលចម្លើយត្រូវបានទទួល វាគ្រាន់តែប្រៀបធៀបលទ្ធផល និងធ្វើបច្ចុប្បន្នភាពទីតាំងរបស់គូប្រជែង។ ការពន្យាពេលរវាងការចុចគ្រាប់ចុច និងការបង្ហាញលទ្ធផលនៅលើអេក្រង់ក្នុងករណីនេះគឺតិចតួចបំផុត។

វាមានសារៈសំខាន់ណាស់ក្នុងការយល់ដឹងអំពីភាពខុសប្លែកគ្នានៅទីនេះ៖ ម៉ាស៊ីនភ្ញៀវតែងតែទាញខ្លួនវាទៅតាមការបញ្ចូលចុងក្រោយរបស់វា និងសត្រូវ - ដោយមានការពន្យាពេលបណ្តាញ យោងទៅតាមស្ថានភាពមុនពីទិន្នន័យពីម៉ាស៊ីនមេ។ នោះគឺនៅពេលដែលបាញ់ទៅលើសត្រូវ អ្នកលេងមើលឃើញគាត់កាលពីអតីតកាលទាក់ទងនឹងខ្លួនគាត់។ បន្ថែមទៀតអំពីការព្យាករណ៍របស់អតិថិជន យើងបានសរសេរមុន។.

ដូច្នេះ ការទស្សន៍ទាយរបស់អតិថិជនដោះស្រាយបញ្ហាមួយ ប៉ុន្តែបង្កើតមួយទៀត៖ ប្រសិនបើអ្នកលេងបាញ់នៅចំណុចដែលសត្រូវកាលពីអតីតកាល នៅលើម៉ាស៊ីនមេ នៅពេលបាញ់នៅចំណុចដូចគ្នា សត្រូវអាចនឹងលែងនៅកន្លែងនោះ។ សំណងភាពយឺតយ៉ាវរបស់ម៉ាស៊ីនមេព្យាយាមដោះស្រាយបញ្ហានេះ។ នៅពេលដែលអាវុធត្រូវបានបាញ់ នោះម៉ាស៊ីនមេនឹងស្ដារស្ថានភាពហ្គេមដែលអ្នកលេងបានឃើញនៅក្នុងមូលដ្ឋាននៅពេលបាញ់ ហើយពិនិត្យមើលថាតើគាត់ពិតជាអាចវាយប្រហារសត្រូវឬអត់។ ប្រសិនបើចម្លើយគឺ “បាទ/ចាស” ការវាយត្រូវបានរាប់ បើទោះបីជាសត្រូវលែងនៅលើម៉ាស៊ីនមេនៅពេលនេះក៏ដោយ។

ប្រដាប់ដោយចំណេះដឹងនេះ យើងបានចាប់ផ្តើមអនុវត្តសំណងលើការយឺតយ៉ាវរបស់ម៉ាស៊ីនមេនៅក្នុងក្រុម Dino ។ ជាដំបូងយើងត្រូវយល់ពីរបៀប Restore នៅលើ Server នូវអ្វីដែលអតិថិជនបានឃើញ? ហើយ​ត្រូវ​ស្ដារ​អ្វី​ឲ្យ​ប្រាកដ? នៅក្នុងហ្គេមរបស់យើង ការវាយលុកពីអាវុធ និងសមត្ថភាពត្រូវបានគណនាតាមរយៈការបាញ់កាំរស្មី និងការត្រួតលើគ្នា ពោលគឺតាមរយៈអន្តរកម្មជាមួយនឹងការប៉ះទង្គិចគ្នារវាងសត្រូវ។ ដូច្នោះហើយ យើងត្រូវបង្កើតទីតាំងនៃការប៉ះទង្គិចទាំងនេះឡើងវិញ ដែលអ្នកលេង "បានឃើញ" នៅក្នុងមូលដ្ឋាននៅលើម៉ាស៊ីនមេ។ នៅពេលនោះយើងកំពុងប្រើ Unity កំណែ 2018.x ។ API រូបវិទ្យានៅទីនោះគឺឋិតិវន្ត ពិភពរូបវន្តមាននៅក្នុងច្បាប់ចម្លងតែមួយ។ មិនមានវិធីដើម្បីរក្សាទុកស្ថានភាពរបស់វាហើយបន្ទាប់មកស្ដារវាពីប្រអប់នោះទេ។ ដូច្នេះតើត្រូវធ្វើអ្វី?

ដំណោះ​ស្រាយ​គឺ​ស្ថិត​នៅ​លើ​ផ្ទៃ ធាតុ​ទាំង​អស់​របស់​វា​ត្រូវ​បាន​យើង​ប្រើ​រួច​ហើយ​ដើម្បី​ដោះស្រាយ​បញ្ហា​ផ្សេង​ទៀត៖

  1. សម្រាប់អតិថិជនម្នាក់ៗ យើងត្រូវដឹងថានៅពេលណាដែលគាត់ឃើញគូប្រជែងនៅពេលគាត់ចុចគ្រាប់ចុច។ យើងបានសរសេរព័ត៌មាននេះទៅក្នុងកញ្ចប់បញ្ចូលរួចហើយ ហើយបានប្រើវាដើម្បីកែតម្រូវការព្យាករណ៍របស់អតិថិជន។
  2. យើងត្រូវតែអាចរក្សាទុកប្រវត្តិនៃរដ្ឋហ្គេម។ វាគឺនៅក្នុងវាដែលយើងនឹងកាន់តំណែងរបស់គូប្រជែងរបស់យើង (ហើយដូច្នេះការប៉ះទង្គិចរបស់ពួកគេ) ។ យើងមានប្រវត្តិរដ្ឋរួចហើយនៅលើម៉ាស៊ីនមេ យើងបានប្រើវាដើម្បីបង្កើត ដីសណ្ត. ដោយដឹងពីពេលវេលាត្រឹមត្រូវ យើងអាចស្វែងរកស្ថានភាពត្រឹមត្រូវក្នុងប្រវត្តិសាស្ត្របានយ៉ាងងាយស្រួល។
  3. ឥឡូវនេះយើងមានស្ថានភាពហ្គេមពីប្រវត្តិសាស្ត្រនៅក្នុងដៃ យើងត្រូវមានលទ្ធភាពធ្វើសមកាលកម្មទិន្នន័យរបស់អ្នកលេងជាមួយនឹងស្ថានភាពនៃពិភពរូបវន្ត។ ការប៉ះទង្គិចដែលមានស្រាប់ - ផ្លាស់ទី, បាត់ - បង្កើត, មិនចាំបាច់ - បំផ្លាញ។ តក្កវិជ្ជានេះក៏ត្រូវបានសរសេររួចហើយ និងមានប្រព័ន្ធ ECS ជាច្រើន។ យើងបានប្រើវាដើម្បីរៀបចំបន្ទប់ហ្គេមជាច្រើននៅក្នុងដំណើរការ Unity មួយ។ ហើយចាប់តាំងពីពិភពរូបវន្តគឺមួយក្នុងដំណើរការមួយ វាត្រូវតែប្រើឡើងវិញរវាងបន្ទប់។ មុនពេលគូសនីមួយៗនៃការពិសោធន៏ យើង "កំណត់" ស្ថានភាពនៃពិភពរូបវន្ត និងចាប់ផ្តើមវាឡើងវិញជាមួយនឹងទិន្នន័យសម្រាប់បន្ទប់បច្ចុប្បន្ន ដោយព្យាយាមប្រើវត្ថុហ្គេម Unity ឡើងវិញតាមដែលអាចធ្វើទៅបានតាមរយៈប្រព័ន្ធរួមបញ្ចូលគ្នាដ៏ឆ្លាតវៃ។ អ្វីដែលនៅសេសសល់គឺដើម្បីហៅតក្កវិជ្ជាដូចគ្នាសម្រាប់ស្ថានភាពហ្គេមពីអតីតកាល។

ដោយ​ការ​ដាក់​ធាតុ​ទាំង​អស់​នេះ​ចូល​គ្នា យើង​ទទួល​បាន "ម៉ាស៊ីន​ពេល​វេលា" ដែល​អាច​រំកិល​ស្ថានភាព​នៃ​ពិភព​រូប​កាយ​ត្រឡប់​ទៅ​ពេល​ត្រឹម​ត្រូវ។ កូដបានប្រែទៅជាសាមញ្ញ៖

public class TimeMachine : ITimeMachine
{
     //История игровых состояний
     private readonly IGameStateHistory _history;

     //Текущее игровое состояние на сервере
     private readonly ExecutableSystem[] _systems;

     //Набор систем, расставляющих коллайдеры в физическом мире 
     //по данным из игрового состояния
     private readonly GameState _presentState;

     public TimeMachine(IGameStateHistory history, GameState presentState, ExecutableSystem[] timeInitSystems)
     {
         _history = history; 
         _presentState = presentState;
         _systems = timeInitSystems;  
     }

     public GameState TravelToTime(int tick)
     {
         var pastState = tick == _presentState.Time ? _presentState : _history.Get(tick);
         foreach (var system in _systems)
         {
             system.Execute(pastState);
         }
         return pastState;
     }
}

អ្វីដែលនៅសេសសល់គឺត្រូវស្វែងយល់ពីរបៀបប្រើម៉ាស៊ីននេះ ដើម្បីងាយស្រួលទូទាត់សងសម្រាប់ការបាញ់ប្រហារ និងសមត្ថភាព។

ក្នុងករណីដ៏សាមញ្ញបំផុត នៅពេលដែលមេកានិកផ្អែកលើការវាយស្កែនតែមួយ អ្វីៗហាក់ដូចជាច្បាស់៖ មុនពេលអ្នកលេងបាញ់ គាត់ត្រូវបង្វិលពិភពរូបវន្តទៅកាន់ស្ថានភាពដែលចង់បាន ធ្វើកាំរស្មី រាប់ចំនួនបុក ឬខកខាន និង ត្រឡប់ពិភពលោកទៅសភាពដើមវិញ។

ប៉ុន្តែមានមេកានិចបែបនេះតិចតួចណាស់នៅក្នុងក្រុម Dino! អាវុធភាគច្រើននៅក្នុងហ្គេមបង្កើតជាគ្រាប់កាំភ្លើង - គ្រាប់កាំភ្លើងដែលមានអាយុកាលយូរដែលហោះហើរសម្រាប់ឆ្កពិសោធជាច្រើន (ក្នុងករណីខ្លះមានឆ្ករាប់សិប) ។ តើត្រូវធ្វើអ្វីជាមួយពួកគេ តើពួកគេគួរហោះហើរម៉ោងប៉ុន្មាន?

В អត្ថបទបុរាណ អំពីជង់បណ្តាញ Half-Life បុរសមកពី Valve បានសួរសំណួរដូចគ្នា ហើយចម្លើយរបស់ពួកគេគឺនេះ៖ សំណងភាពយឺតយ៉ាវរបស់ projectile គឺមានបញ្ហា ហើយវាជាការប្រសើរក្នុងការជៀសវាងវា។

យើងមិនមានជម្រើសនេះទេ៖ អាវុធដែលមានមូលដ្ឋានលើគ្រាប់កាំភ្លើងគឺជាលក្ខណៈសំខាន់នៃការរចនាហ្គេម។ ដូច្នេះ​យើង​ត្រូវ​តែ​មក​រក​អ្វី​មួយ។ បន្ទាប់ពីការបំផុសគំនិតបន្តិច យើងបានបង្កើតជម្រើសពីរដែលហាក់ដូចជាដំណើរការ៖

1. យើងចងកាំជ្រួចទៅនឹងពេលវេលារបស់អ្នកលេងដែលបានបង្កើតវា។ រាល់សញ្ញាធីកនៃការក្លែងធ្វើម៉ាស៊ីនមេ សម្រាប់រាល់ចំណុចនៃអ្នកលេងទាំងអស់ យើងបង្វិលពិភពរូបវន្តទៅកាន់ស្ថានភាពអតិថិជន ហើយធ្វើការគណនាចាំបាច់។ វិធីសាស្រ្តនេះបានធ្វើឱ្យវាអាចធ្វើទៅបានដើម្បីឱ្យមានការចែកចាយបន្ទុកនៅលើម៉ាស៊ីនមេ និងពេលវេលាហោះហើរដែលអាចព្យាករណ៍បាននៃគ្រាប់។ ការទស្សន៍ទាយមានសារៈសំខាន់ជាពិសេសសម្រាប់យើង ដោយសារយើងមានគ្រាប់ផ្លោងទាំងអស់ រួមទាំងគ្រាប់ផ្លោងរបស់សត្រូវ ដែលបានព្យាករណ៍លើម៉ាស៊ីនភ្ញៀវ។

របៀបដែលយើងបង្កើតមេកានិចនៃការគណនាផ្លោងសម្រាប់អ្នកបាញ់តាមទូរស័ព្ទជាមួយនឹងក្បួនដោះស្រាយការពន្យាពេលបណ្តាញ
នៅក្នុងរូបភាព អ្នកលេងនៅសញ្ញាធីក 30 បាញ់កាំជ្រួចដោយស្មានទុកជាមុន៖ គាត់មើលឃើញថាសត្រូវកំពុងរត់ក្នុងទិសដៅណា ហើយដឹងពីល្បឿនប្រហាក់ប្រហែលនៃកាំជ្រួច។ ក្នុង​មូលដ្ឋាន​គាត់​មើល​ឃើញ​ថា​គាត់​បាន​វាយ​ចំ​គោលដៅ​នៅ​សញ្ញា​ទី ៣៣។ សូមអរគុណចំពោះសំណងយឺតយ៉ាវ វាក៏នឹងបង្ហាញនៅលើម៉ាស៊ីនមេផងដែរ។

2. យើងធ្វើអ្វីគ្រប់យ៉ាងដូចគ្នានឹងជម្រើសទីមួយដែរ ប៉ុន្តែដោយបានរាប់សញ្ញាធីកមួយនៃការក្លែងធ្វើគ្រាប់កាំភ្លើង យើងមិនឈប់ទេ ប៉ុន្តែបន្តក្លែងធ្វើការហោះហើររបស់វានៅក្នុងម៉ាស៊ីនមេដូចគ្នា រាល់ពេលដែលនាំពេលវេលារបស់វាខិតទៅជិតម៉ាស៊ីនមេ។ ម្តងមួយៗគូស និងធ្វើបច្ចុប្បន្នភាពទីតាំងបុក។ យើងធ្វើបែបនេះរហូតដល់រឿងមួយក្នុងចំណោមរឿងពីរកើតឡើង៖

  • គ្រាប់កាំភ្លើងបានផុតកំណត់ហើយ។ មាន​ន័យ​ថា​ការ​គណនា​ចប់​ហើយ​យើង​អាច​រាប់​បាន​មួយ​នឹក​ឬ​បុក។ ហើយនេះគឺជាសញ្ញាធីកដូចគ្នាដែលការបាញ់ត្រូវបានបាញ់! សម្រាប់ពួកយើង នេះគឺទាំងបូក និងដក។ បូកមួយ - ដោយសារតែសម្រាប់អ្នកលេងបាញ់នេះកាត់បន្ថយការពន្យាពេលរវាងការវាយនិងការថយចុះយ៉ាងខ្លាំងនៃសុខភាពរបស់សត្រូវ។ គុណវិបត្តិគឺថាឥទ្ធិពលដូចគ្នាត្រូវបានគេសង្កេតឃើញនៅពេលដែលគូប្រជែងបានបាញ់ទៅលើអ្នកលេង: សត្រូវ វាហាក់ដូចជាគ្រាន់តែបាញ់រ៉ុក្កែតយឺត ហើយការខូចខាតត្រូវបានរាប់រួចហើយ។
  • គ្រាប់កាំភ្លើងបានដល់ម៉ោងម៉ាស៊ីនមេ។ ក្នុងករណីនេះ ការក្លែងធ្វើរបស់វានឹងបន្តនៅក្នុងម៉ាស៊ីនមេបន្ទាប់ ដោយមិនមានសំណងយឺតយ៉ាវណាមួយឡើយ។ សម្រាប់​ការ​បាញ់​យឺត វា​អាច​កាត់​បន្ថយ​តាម​ទ្រឹស្ដី​ចំនួន​នៃ​ការ​វិល​ត្រឡប់​ខាង​រូបវិទ្យា បើ​ធៀប​នឹង​ជម្រើស​ដំបូង។ ក្នុងពេលជាមួយគ្នានោះ ការផ្ទុកមិនស្មើគ្នាលើការក្លែងធ្វើបានកើនឡើង៖ ម៉ាស៊ីនមេនៅទំនេរ ឬនៅក្នុងម៉ាស៊ីនមេមួយ ធីកវាកំពុងគណនាសញ្ញាក្លែងធ្វើរាប់សិបសម្រាប់គ្រាប់ជាច្រើន។

របៀបដែលយើងបង្កើតមេកានិចនៃការគណនាផ្លោងសម្រាប់អ្នកបាញ់តាមទូរស័ព្ទជាមួយនឹងក្បួនដោះស្រាយការពន្យាពេលបណ្តាញ
សេណារីយ៉ូដូចគ្នានឹងរូបភាពមុនដែរ ប៉ុន្តែគណនាតាមគ្រោងការណ៍ទីពីរ។ កាំជ្រួច "ចាប់ឡើង" ជាមួយនឹងម៉ោងម៉ាស៊ីនមេនៅសញ្ញាធីកដូចគ្នាដែលការបាញ់បានកើតឡើង ហើយការវាយលុកអាចត្រូវបានរាប់នៅដើមឆ្នាំបន្ទាប់។ នៅសញ្ញាទី 31 ក្នុងករណីនេះ សំណងភាពយឺតយ៉ាវមិនត្រូវបានអនុវត្តទៀតទេ

នៅក្នុងការអនុវត្តរបស់យើង វិធីសាស្រ្តទាំងពីរនេះខុសគ្នាត្រឹមតែពីរបីបន្ទាត់នៃកូដ ដូច្នេះយើងបានបង្កើតទាំងពីរ ហើយសម្រាប់រយៈពេលដ៏យូរមួយដែលពួកគេមានស្របគ្នា។ អាស្រ័យលើមេកានិចនៃអាវុធ និងល្បឿននៃគ្រាប់កាំភ្លើង យើងបានជ្រើសរើសជម្រើសមួយ ឬមួយផ្សេងទៀតសម្រាប់ដាយណូស័រនីមួយៗ។ ចំណុចរបត់នៅទីនេះគឺរូបរាងនៅក្នុងហ្គេមមេកានិកដូចជា "ប្រសិនបើអ្នកវាយសត្រូវច្រើនដងក្នុងពេលវេលាបែបនេះ នឹងទទួលបានប្រាក់រង្វាន់បែបនេះ" ។ មេកានិកណាមួយដែលពេលវេលាដែលអ្នកលេងវាយសត្រូវដើរតួយ៉ាងសំខាន់បដិសេធមិនធ្វើការជាមួយវិធីសាស្រ្តទីពីរ។ ដូច្នេះ យើងបានបញ្ចប់ជាមួយនឹងជម្រើសទីមួយ ហើយឥឡូវនេះវាអនុវត្តចំពោះអាវុធទាំងអស់ និងសមត្ថភាពសកម្មទាំងអស់នៅក្នុងហ្គេម។

ដោយឡែក​វា​មានតម្លៃ​លើកឡើង​ពី​បញ្ហា​នៃ​ការអនុវត្ត​។ ប្រសិនបើអ្នកគិតថាអ្វីៗទាំងអស់នេះនឹងថយចុះ ខ្ញុំឆ្លើយថា វាគឺ។ ការរួបរួមគឺមានភាពយឺតយ៉ាវក្នុងការរំកិលការប៉ះទង្គិចគ្នា ហើយបើក និងបិទពួកវា។ នៅក្នុងក្រុម Dino ក្នុងសេណារីយ៉ូ "អាក្រក់បំផុត" វាអាចមានកាំជ្រួចរាប់រយគ្រាប់ដែលមានស្រាប់ក្នុងពេលដំណាលគ្នាក្នុងការប្រយុទ្ធ។ ការ​រំកិល​ឧបករណ៍​បុក​ដើម្បី​រាប់​កាំជ្រួច​នីមួយៗ​ជា​លក្ខណៈ​ប្រណីត​មិន​អាច​កាត់​ថ្លៃ​បាន។ ដូច្នេះ វាពិតជាចាំបាច់សម្រាប់យើងក្នុងការកាត់បន្ថយចំនួនរូបវិទ្យា “វិលថយក្រោយ”។ ដើម្បីធ្វើដូចនេះយើងបានបង្កើតសមាសភាគដាច់ដោយឡែកមួយនៅក្នុង ECS ដែលយើងកត់ត្រាពេលវេលារបស់អ្នកលេង។ យើង​បាន​បន្ថែម​វា​ទៅ​អង្គភាព​ទាំងអស់​ដែល​ទាមទារ​សំណង​យឺតយ៉ាវ (projectiles សមត្ថភាព​។ល។)។ មុនពេលយើងចាប់ផ្តើមដំណើរការអង្គធាតុបែបនេះ យើងដាក់ចង្កោមពួកវាដោយពេលនេះ ហើយដំណើរការពួកវាជាមួយគ្នា ដោយរំកិលពិភពរូបវន្តម្តងសម្រាប់ចង្កោមនីមួយៗ។

នៅដំណាក់កាលនេះយើងមានប្រព័ន្ធការងារជាទូទៅ។ កូដរបស់វាក្នុងទម្រង់សាមញ្ញបន្តិច៖

public sealed class LagCompensationSystemGroup : ExecutableSystem
{
     //Машина времени
     private readonly ITimeMachine _timeMachine;

     //Набор систем лагкомпенсации
     private readonly LagCompensationSystem[] _systems;
     
     //Наша реализация кластеризатора
     private readonly TimeTravelMap _travelMap = new TimeTravelMap();

    public LagCompensationSystemGroup(ITimeMachine timeMachine, 
        LagCompensationSystem[] lagCompensationSystems)
     {
         _timeMachine = timeMachine;
         _systems = lagCompensationSystems;
     }

     public override void Execute(GameState gs)
     {
         //На вход кластеризатор принимает текущее игровое состояние,
         //а на выход выдает набор «корзин». В каждой корзине лежат энтити,
         //которым для лагкомпенсации нужно одно и то же время из истории.
         var buckets = _travelMap.RefillBuckets(gs);

         for (int bucketIndex = 0; bucketIndex < buckets.Count; bucketIndex++)
         {
             ProcessBucket(gs, buckets[bucketIndex]);
         }

         //В конце лагкомпенсации мы восстанавливаем физический мир 
         //в исходное состояние
         _timeMachine.TravelToTime(gs.Time);
     }

     private void ProcessBucket(GameState presentState, TimeTravelMap.Bucket bucket)
     {
         //Откатываем время один раз для каждой корзины
         var pastState = _timeMachine.TravelToTime(bucket.Time);

         foreach (var system in _systems)
         {
               system.PastState = pastState;
               system.PresentState = presentState;

               foreach (var entity in bucket)
               {
                   system.Execute(entity);
               }
          }
     }
}

អ្វីដែលនៅសេសសល់គឺត្រូវកំណត់រចនាសម្ព័ន្ធលម្អិត៖

1. ស្វែងយល់ថាតើត្រូវកំណត់ចម្ងាយអតិបរមានៃចលនាក្នុងពេលវេលាប៉ុន្មាន។

វាមានសារៈសំខាន់សម្រាប់យើងក្នុងការធ្វើឱ្យហ្គេមអាចចូលប្រើបានតាមដែលអាចធ្វើបាននៅក្នុងលក្ខខណ្ឌនៃបណ្តាញទូរស័ព្ទចល័តមិនល្អ ដូច្នេះយើងកំណត់សាច់រឿងដោយរឹម 30 ធីក (ជាមួយនឹងអត្រាធីក 20 ហឺត)។ នេះអនុញ្ញាតឱ្យអ្នកលេងវាយគូប្រកួតសូម្បីតែនៅ ping ខ្ពស់ខ្លាំងណាស់។

2. កំណត់ថាតើវត្ថុណាអាចផ្លាស់ទីបានទាន់ពេល ហើយវត្ថុណាដែលមិនអាចផ្លាស់ទីបាន។

ជាការពិតណាស់ យើងកំពុងផ្លាស់ប្តូរគូប្រជែងរបស់យើង។ ប៉ុន្តែឧទាហរណ៍ខែលការពារថាមពលដែលអាចដំឡើងបានគឺមិនមែនទេ។ យើងបានសម្រេចចិត្តថា វាជាការប្រសើរជាងក្នុងការផ្តល់អាទិភាពដល់សមត្ថភាពការពារ ដូចដែលតែងតែធ្វើឡើងនៅក្នុងអ្នកបាញ់ប្រហារតាមអ៊ីនធឺណិត។ ប្រសិនបើអ្នកលេងបានដាក់ខែលរួចហើយនៅក្នុងពេលបច្ចុប្បន្ន គ្រាប់កាំភ្លើងដែលផ្តល់សំណងពីអតីតកាលមិនគួរហោះកាត់វាទេ។

3. សម្រេចចិត្តថាតើវាចាំបាច់ដើម្បីទូទាត់សងសម្រាប់សមត្ថភាពរបស់ដាយណូស័រ៖ ខាំ កន្ទុយ ជាដើម។ យើងបានសម្រេចចិត្តនូវអ្វីដែលចាំបាច់ ហើយដំណើរការវាដោយយោងទៅតាមច្បាប់ដូចគ្នានឹងគ្រាប់កាំភ្លើង។

4. កំណត់អ្វីដែលត្រូវធ្វើជាមួយការប៉ះទង្គិចគ្នារបស់អ្នកលេង ដែលសំណងយឺតយ៉ាវកំពុងត្រូវបានអនុវត្ត។ តាមរបៀបដ៏ល្អ ទីតាំងរបស់ពួកគេមិនគួរផ្លាស់ប្តូរទៅអតីតកាលទេ៖ អ្នកលេងគួរតែឃើញខ្លួនឯងក្នុងពេលជាមួយគ្នាដែលឥឡូវនេះគាត់នៅលើម៉ាស៊ីនមេ។ ទោះយ៉ាងណាក៏ដោយ យើងក៏រមៀលថយក្រោយការប៉ះទង្គិចរបស់អ្នកលេងបាញ់ដែរ ហើយមានហេតុផលជាច្រើនសម្រាប់រឿងនេះ។

ទីមួយ វាធ្វើអោយប្រសើរឡើងនូវការចងក្រង៖ យើងអាចប្រើស្ថានភាពរូបវន្តដូចគ្នាសម្រាប់អ្នកលេងទាំងអស់ដែលមាន pings ជិតស្និទ្ធ។

ទីពីរ នៅក្នុងការផ្សាយ និងការត្រួតគ្នាទាំងអស់ យើងតែងតែមិនរាប់បញ្ចូលការប៉ះទង្គិចគ្នារបស់អ្នកលេងដែលជាម្ចាស់សមត្ថភាព ឬ projectiles ។ នៅក្នុងក្រុម Dino អ្នកលេងគ្រប់គ្រងដាយណូស័រដែលមានធរណីមាត្រមិនស្តង់ដារតាមស្តង់ដារអ្នកបាញ់។ ទោះបីជាអ្នកលេងបាញ់នៅមុំមិនធម្មតា ហើយគន្លងរបស់គ្រាប់កាំភ្លើងឆ្លងកាត់ការបុកដាយណូស័ររបស់អ្នកលេងក៏ដោយ គ្រាប់កាំភ្លើងនឹងមិនអើពើឡើយ។

ទីបី យើងគណនាទីតាំងនៃអាវុធរបស់ដាយណូស័រ ឬចំណុចនៃការអនុវត្តសមត្ថភាពដោយប្រើទិន្នន័យពី ECS សូម្បីតែមុនពេលចាប់ផ្តើមនៃសំណងយឺតយ៉ាវក៏ដោយ។

ជាលទ្ធផល ទីតាំងពិតប្រាកដនៃការបុកគ្នារបស់អ្នកលេងដែលផ្តល់សំណងយឺតយ៉ាវគឺមិនសំខាន់សម្រាប់យើង ដូច្នេះហើយយើងបានយកផ្លូវដែលមានផលិតភាពជាងមុន និងក្នុងពេលតែមួយកាន់តែងាយស្រួល។

ភាពយឺតនៃបណ្តាញមិនអាចត្រូវបានដកចេញដោយសាមញ្ញទេ វាអាចត្រូវបានបិទបាំងតែប៉ុណ្ណោះ។ ដូចវិធីសាស្រ្តផ្សេងទៀតនៃការក្លែងបន្លំ សំណងភាពយឺតយ៉ាវរបស់ម៉ាស៊ីនមេមានការដោះដូររបស់វា។ វាធ្វើអោយប្រសើរឡើងនូវបទពិសោធន៍លេងហ្គេមរបស់អ្នកលេងដែលកំពុងបាញ់ដោយចំណាយរបស់អ្នកលេងដែលត្រូវបាញ់។ ទោះយ៉ាងណាក៏ដោយ សម្រាប់ក្រុម Dino ជម្រើសនៅទីនេះគឺជាក់ស្តែង។

ជាការពិតណាស់ អ្វីៗទាំងអស់នេះក៏ត្រូវចំណាយផងដែរ ដោយសារការកើនឡើងនៃភាពស្មុគស្មាញនៃកូដម៉ាស៊ីនមេទាំងមូល - ទាំងសម្រាប់អ្នកសរសេរកម្មវិធី និងអ្នករចនាហ្គេម។ ប្រសិនបើការក្លែងធ្វើមុននេះ គឺជាការហៅតាមលំដាប់លំដោយសាមញ្ញនៃប្រព័ន្ធ នោះជាមួយនឹងសំណងយឺតយ៉ាវ រង្វិលជុំ និងមែកធាងដែលបានដាក់នៅក្នុងនោះ។ យើង​ក៏​បាន​ចំណាយ​ការ​ខិត​ខំ​យ៉ាង​ច្រើន​ដើម្បី​ធ្វើ​ឱ្យ​វា​ងាយ​ស្រួល​ក្នុង​ការ​ធ្វើ​ការ​ជាមួយ។

នៅក្នុងកំណែ 2019 (ហើយប្រហែលជាមុននេះបន្តិច) Unity បានបន្ថែមការគាំទ្រពេញលេញសម្រាប់ឈុតរាងកាយឯករាជ្យ។ យើងបានអនុវត្តពួកវានៅលើម៉ាស៊ីនមេស្ទើរតែភ្លាមៗបន្ទាប់ពីការអាប់ដេត ដោយសារតែយើងចង់កម្ចាត់ពិភពរូបវន្តទូទៅសម្រាប់បន្ទប់ទាំងអស់យ៉ាងឆាប់រហ័ស។

យើងបានផ្តល់ឱ្យបន្ទប់ហ្គេមនីមួយៗនូវទិដ្ឋភាពជាក់ស្តែងរបស់វា ហើយដូច្នេះបានលុបចោលនូវតម្រូវការដើម្បី "ជម្រះ" កន្លែងកើតហេតុពីទិន្នន័យនៃបន្ទប់ជិតខាង មុនពេលគណនាការក្លែងធ្វើ។ ជាដំបូង វាបានផ្តល់នូវការកើនឡើងគួរឱ្យកត់សម្គាល់នៅក្នុងផលិតភាព។ ទីពីរ វាធ្វើឱ្យវាអាចធ្វើទៅបានដើម្បីកម្ចាត់នូវប្រភេទទាំងមូលនៃកំហុសដែលកើតឡើងប្រសិនបើអ្នកសរសេរកម្មវិធីបង្កើតកំហុសនៅក្នុងកូដសម្អាតកន្លែងកើតហេតុនៅពេលបន្ថែមធាតុហ្គេមថ្មី។ កំហុសបែបនេះពិបាកនឹងកែកំហុស ហើយជារឿយៗវាបណ្តាលឱ្យស្ថានភាពនៃវត្ថុក្នុងបន្ទប់មួយ "ហូរ" ចូលទៅក្នុងបន្ទប់មួយទៀត។

លើសពីនេះ យើងបានធ្វើការស្រាវជ្រាវមួយចំនួនថាតើឈុតឆាករូបវន្តអាចត្រូវបានគេប្រើប្រាស់ដើម្បីរក្សាទុកប្រវត្តិសាស្រ្តនៃពិភពរូបវន្ត។ នោះតាមលក្ខខណ្ឌ មិនត្រូវបែងចែកឈុតមួយទៅបន្ទប់នីមួយៗទេ ប៉ុន្តែមាន 30 ឈុត ហើយបង្កើតជាសតិបណ្ដោះអាសន្នចេញពីពួកវា ដើម្បីរក្សាទុកសាច់រឿង។ ជាទូទៅ ជម្រើសបានប្រែទៅជាដំណើរការ ប៉ុន្តែយើងមិនបានអនុវត្តវាទេ៖ វាមិនបង្ហាញពីការកើនឡើងនូវផលិតភាពឆ្កួតៗនោះទេ ប៉ុន្តែទាមទារឱ្យមានការផ្លាស់ប្តូរប្រកបដោយហានិភ័យ។ វាពិបាកក្នុងការទស្សន៍ទាយពីរបៀបដែលម៉ាស៊ីនមេនឹងមានឥរិយាបទនៅពេលធ្វើការរយៈពេលយូរជាមួយនឹងឈុតជាច្រើន។ ដូច្នេះ​ហើយ យើង​ធ្វើ​តាម​ច្បាប់៖ប្រសិនបើវាមិនបែកទេកុំជួសជុលវា"។

ប្រភព: www.habr.com

បន្ថែមមតិយោបល់