Logika permainan umum pada klien dan server

Di Pixonic DevGAMM Talks, DTO kami Anton Grigoriev juga berbicara. Kami di perusahaan telah mengatakan bahwa kami sedang mengerjakan penembak PvP baru dan Anton berbagi beberapa nuansa arsitektur proyek ini. Dia memberi tahu cara membangun pengembangan sehingga perubahan dalam logika permainan klien muncul di server secara otomatis (dan sebaliknya), dan apakah mungkin untuk tidak menulis kode, tetapi meminimalkan lalu lintas. Di bawah ini adalah catatan dan transkrip laporan.


Saya tidak akan belajar bagaimana melakukan sesuatu, saya akan berbicara tentang bagaimana kami melakukannya. Agar Anda tidak menginjak menyapu yang sama dan dapat menggunakan pengalaman kami. Setahun setengah yang lalu, kami di perusahaan tidak tahu cara membuat penembak di ponsel. Anda bilang begini, Anda memiliki War Robots, 100 juta unduhan, 1,5 juta DAU. Tetapi dalam game ini, robot sangat lambat, dan kami ingin membuat penembak cepat, dan arsitektur Robot Perang tidak memungkinkan ini.

Kami tahu bagaimana dan apa yang harus dilakukan, tetapi kami tidak memiliki pengalaman. Kemudian kami mempekerjakan seseorang yang memiliki pengalaman ini dan berkata: lakukan hal yang sama yang telah Anda lakukan ratusan kali, hanya lebih baik. Kemudian mereka duduk dan mulai berpikir tentang arsitektur.



Datang ke Entity Component System (ECS). Saya pikir banyak orang tahu apa itu. Semua benda di dunia diwakili oleh entitas. Misalnya, seorang pemain, senjatanya, beberapa benda di peta. Mereka memiliki properti yang dijelaskan oleh komponen. Misalnya, komponen Transform adalah posisi pemain di luar angkasa, dan komponen Kesehatan adalah kesehatannya. Ada logika - itu terpisah dan diwakili oleh sistem. Biasanya, sistem adalah metode Execute (), yang melewati komponen dari tipe tertentu dan melakukan sesuatu dengannya, dengan dunia game. Sebagai contoh, MoveSystem melewati semua komponen Gerakan, melihat kecepatan dalam komponen ini, parameter dan atas dasar ini menghitung posisi objek yang baru, mis. menulisnya ke Transform.

Arsitektur seperti itu memiliki karakteristiknya sendiri. Ketika mengembangkan ECS, Anda perlu berpikir dan melakukan berbagai hal secara berbeda. Salah satu kelebihannya adalah komposisi daripada pewarisan berganda. Ingat ini belah ketupat dengan banyak warisan dalam C ++? Semua masalahnya. Ini tidak terjadi di ECS.



Fitur kedua adalah pemisahan logika dan data, yang sudah saya bicarakan. Apa yang ini berikan pada kita? Kita dapat menyimpan keadaan dunia dan sejarahnya dalam batch, kita dapat membuat cerita bersambung, kita dapat mengirim data ini melalui jaringan dan mengubahnya secara waktu nyata. Ini hanya data dalam memori - kami dapat mengubah nilai apa pun kapan saja. Dengan demikian, sangat mudah untuk mengubah logika permainan (atau untuk debug).

Ini juga sangat penting untuk melacak urutan panggilan sistem. Semua sistem berjalan satu demi satu, dipanggil oleh metode Execute () dan, idealnya, harus independen. Dalam praktiknya, ini tidak terjadi. Satu sistem mengubah sesuatu di dunia, sistem lain kemudian menggunakannya. Dan jika kita melanggar perintah ini, permainan akan berjalan berbeda. Mungkin tidak banyak, tapi jelas tidak sama seperti sebelumnya.

Akhirnya, salah satu fitur utama dan paling penting bagi kami adalah kami dapat menjalankan kode yang sama baik pada klien maupun di server.

Beri pengembang kesempatan, dan dia akan menemukan 99 cara dan alasan untuk membuat keputusan, dan tidak menggunakan yang sudah ada. Saya pikir banyak yang melakukannya. Kami sedang mencari Kerangka ECS pada waktu itu. Kami mempertimbangkan Entitas, Artemis C #, Ash.net dan solusi kami sendiri, yang dapat ditulis dari pengalaman seorang spesialis yang mendatangi kami.



Jangan mencoba membaca apa yang tertulis di slide, itu tidak begitu penting. Yang penting adalah seberapa banyak hijau dan merah di kolom. Hijau berarti bahwa solusi mendukung persyaratan, merah - tidak mendukung, kuning - mendukung, tetapi tidak cukup.

Di kolom, ECS berpotensi menjadi solusi kami. Seperti yang Anda lihat, ini lebih keren - kami dapat mendukung lebih banyak persyaratan. Akibatnya, kami tidak mendukung beberapa dari mereka (terutama karena mereka tidak diperlukan), dan beberapa, yang tanpanya kami tidak dapat bekerja lebih lanjut, harus dilakukan. Kami memilih arsitektur, bekerja untuk waktu yang lama, membuat versi yang dapat dimainkan secara minimal dan ... fakap.



Ternyata versi yang paling tidak bisa dimainkan. Pemain terus bergulir, rem, server tergantung di tengah pertandingan. Tidak mungkin memainkannya. Apa alasan kegagalan itu?

Alasan # 1 dan yang paling penting adalah pengalaman. Tapi bagaimana caranya? Kami mempekerjakan orang berpengalaman yang seharusnya melakukan semuanya dengan indah. Ya, tetapi dalam kenyataannya kami hanya memberinya sebagian dari pekerjaan. Kami berkata: "Ini server game untuk Anda, kerjakanlah." Dan dalam arsitektur kami (lebih lanjut tentang ini nanti), klien memainkan peran yang sangat penting. Dan bagian inilah yang kami berikan kepada seorang pria yang tidak memiliki pengalaman yang diperlukan. Tidak, dia adalah programmer yang baik, senor - sama sekali tidak ada pengalaman. Yaitu dia tidak tahu jenis rake apa yang mungkin ada.

Alasan # 2 - alokasi tidak realistis. 80 KB / bingkai. Banyak atau tidak? Jika kita memperhitungkan bahwa kita memiliki 30 frame per detik, maka dalam satu detik kita mendapatkan 2,5 MB, dan untuk pertandingan 5 menit sudah ada lebih dari 600 MB. Singkatnya, banyak. Pengumpul sampah mulai secara intens mencoba membebaskan semua ingatan ini (ketika kami menuntut lebih dan lebih dari itu), yang mengarah pada paku. Mengingat bahwa kami menginginkan 30 frame per detik, paku-paku ini sangat mengganggu kami. Apalagi di klien dan di server.

Alasan utama pengalokasian adalah kami terus-menerus mengalokasikan data array. Hampir setiap waktu setiap frame. LINQ, ekspresi lambda dan Photon yang digunakan. Photon adalah perpustakaan jaringan yang kita kenal dan gunakan di War Robots. Dan semuanya tampak baik-baik saja, tetapi itu mengalokasikan memori setiap kali mengirim data atau menerimanya.

Jika kami menyelesaikan masalah pertama (menulis ulang koleksi kustom kami, melakukan caching), maka praktis tidak ada yang bisa dilakukan dengan Photon, karena itu adalah perpustakaan pihak ketiga. Itu hanya mungkin untuk mengurangi ukuran paket, dan kami memiliki 5 Kbytes. Banyak? Ya Ada MTU - ini adalah ukuran paket aktual minimum yang dikirim melalui UDP, tanpa memecah paket menjadi bagian-bagian kecil. Itu sekitar 1,5 Kbytes, dan kami memiliki 5 (rata-rata, ada lebih banyak).

Oleh karena itu, Photon memotong paket kami menjadi yang kecil dan mengirim masing-masing bagian dengan andal, mis. dengan pengiriman terjamin. Setiap kali bagian itu tidak mencapai, ia mengirimnya lagi dan lagi. Kami mendapat latensi lebih banyak dan jaringannya bekerja dengan buruk.

Semua alokasi ini mengarah pada fakta bahwa kami menerima bingkai sekitar 100 milidetik ketika dibutuhkan 33. Dan di sana, rendering, simulasi, dan tindakan lainnya - semua ini membutuhkan CPU. Semua masalah ini rumit, mis. tidak mungkin untuk memutuskan satu, dan semuanya akan baik-baik saja. Itu perlu untuk menyelesaikannya sekaligus.

Dan masalah kecil lain yang selama pengembangan - sejumlah besar repositori. 5 tertulis di slide, tetapi bagi saya tampaknya ada lebih banyak lagi. Semua repositori ini (untuk klien, server game, kode umum, pengaturan, dan yang lainnya) dihubungkan oleh submodula ke dalam dua repositori utama untuk klien dan server game. Sulit untuk dikerjakan. Pemrogram dapat bekerja dengan Git, SVN, tetapi ada juga artis, desainer, dll. Saya pikir banyak yang mencoba mengajari seniman atau perancang cara bekerja dengan sistem kontrol versi. Ini sangat sulit, jadi jika desainer Anda tahu bagaimana melakukannya - jaga dia, dia adalah karyawan yang berharga. Dalam kasus kami, bahkan programmer takut, dan sebagai hasilnya, kami mengurangi segalanya menjadi satu repositori.

Ini adalah solusi yang bagus untuk masalah ini. Kami memiliki folder dengan server di sana dan folder dengan klien. Server terdiri dari proyek server permainan, generator kode, dan alat bantu.



Klien adalah klien Persatuan dan kode umum. Kode umum adalah struktur data dunia, mis. Entitas, komponen, dan simulasi sistem. Kode ini terutama dihasilkan oleh generator server. Ini digunakan oleh server. Yaitu Ini adalah bagian umum untuk klien dan server.

Hidup Kami mengambil TeamCity, mengaturnya di repositori kami, mengumpulkan dan menyebarkan server. Setiap kali klien mengubah logika umum, server game dirakit di sini - sekarang seorang programmer server tidak diperlukan untuk ini. Biasanya ada server, klien, dan beberapa fitur. Klien melihatnya di rumah, server di rumah, dan suatu hari nanti akan bekerja untuk mereka. Dalam kasus kami, tidak demikian - klien dapat menulis fitur ini dan semuanya berfungsi di server.

Pertandingan terdiri dari bagian umum (ditunjuk sebagai ECS) dan presentasi (ini adalah kelas MonoBehavior, GameObjects, model, efek - semua yang diwakili dunia). Mereka tidak terhubung.



Di antara mereka ada Presenter, yang bekerja dengan kedua bagian. Seperti yang Anda pahami, ini adalah MVP (Model-View-Presenter) dan semua bagian ini dapat diganti jika perlu. Ada bagian lain yang berfungsi dengan jaringan (pada slide - Jaringan). Ini adalah serialisasi informasi tentang dunia, serialisasi input, pengiriman ke server, penerimaan oleh server, koneksi ke server, dll.

Lebih banyak suka. Kami mengambil dan mengganti bagian ini dengan paket yang tidak nyata, melalui jaringan, tetapi virtual. Kami membuat objek di dalam klien dan mengiriminya pesan. Ini mengimplementasikan simulasi server - sekarang objek ini melakukan semua yang terjadi pada server game. Pemain yang tersisa digantikan oleh bot.



Selesai Kami mendapatkan game dan kemampuan untuk mengujinya tanpa server game. Apa artinya ini? Ini berarti bahwa artis, yang telah membuat efek baru, dapat mengklik tombol Play di editor, segera menuju ke pertandingan di peta dan melihat cara kerjanya. Atau debug untuk programmer klien apa yang mereka tulis.

Tapi kami melangkah lebih jauh dan melekat pada emulasi layer ini dari jitter ping dari keterlambatan jaringan (saat ini paket-paket pada jaringan tidak tiba dalam urutan pengirimannya) dan hal-hal jaringan lainnya. Sebagai hasilnya, kami mendapatkan pertandingan nyata yang nyata tanpa server game. Berhasil, diverifikasi.

Mari kita kembali ke pembuatan kode.



Saya sudah mengatakan bahwa kita memiliki generator kode di server permainan. Ada bahasa khusus domain, yang sebenarnya merupakan kelas C # sederhana. Dalam hal ini, kelas Kesehatan. Kami menandainya dengan atribut kami. Misalnya, ada atribut komponen. Dia mengatakan bahwa Kesehatan adalah komponen di dunia kita. Berdasarkan atribut ini, generator akan membuat kelas C # baru di mana akan ada banyak hal. Mereka dapat ditulis dengan tangan, tetapi itu akan menghasilkan. Misalnya, metode menambahkan komponen ke Entity, metode mencari komponen, membuat serialisasi data, dll. Ada atribut tipe DontSend, yang mengatakan bahwa tidak perlu mengirim bidang melalui jaringan - server tidak memerlukannya atau klien tidak membutuhkannya. Atau atribut Mach, yang melaporkan bahwa pemain memiliki nilai kesehatan maksimum seribu. Apa yang ini berikan pada kita? Alih-alih bidang yang menempati 32 bit (int), kami mengirim 10 bit - tiga kali lebih sedikit. Pembuat kode semacam itu memungkinkan kami mengurangi ukuran paket dari 5 KB menjadi 1.



1 KB <1,5 - mis. Kami bertemu MTU. Foton berhenti memotong dan jaringan menjadi jauh lebih baik. Hampir semua masalahnya telah hilang. Tapi kami melangkah lebih jauh dan melakukan kompresi delta.



Ini adalah saat Anda mengirim satu status penuh, dan hanya perubahannya saja. Tidak terjadi bahwa seluruh dunia segera benar-benar berubah. Hanya beberapa bagian yang terus berubah dan ukuran ini jauh lebih kecil daripada negara bagian itu sendiri. Kami menerima rata-rata 300 byte, mis. 17 kali lebih sedikit dari aslinya.

Mengapa ini perlu jika Anda sudah masuk ke MTU? Permainan ini terus berkembang, fitur-fitur baru muncul, dan dengan mereka muncul objek, entitas, komponen baru. Ukuran data bertambah. Jika kami berhenti pada 1 KB, kami akan segera kembali ke masalah yang sama. Sekarang, setelah menulis ulang untuk kompresi delta, kami tidak akan mencapai ini segera.

Sekarang bagian termanis. Sinkronkan Jika Anda memainkan penembak, Anda tahu apa itu Input Lag - ketika Anda mengklik tombol, dan karakter mulai bergerak setelah beberapa waktu, misalnya, setengah detik. Untuk beberapa game dalam genre mob, ini normal. Tapi di penembak, Anda ingin pahlawan menembak dan melakukan kerusakan di sana.



Mengapa Input Lag terjadi? Klien mengumpulkan input (input) pemain dan mengirimkannya ke server game (pengiriman membutuhkan waktu). Kemudian server game memprosesnya (lagi, waktu) dan mengirimkan hasilnya kembali (lagi, waktu). Ini penundaan. Bagaimana cara menghapusnya? Ada hal yang disebut prediksi - klien tidak menunggu respons dari server dan segera mulai mencoba melakukan hal yang sama seperti yang dilakukan oleh server game, mis. berpura-pura. Mengambil input pemain dan memulai simulasi. Kami hanya mensimulasikan klien lokal, karena kami tidak tahu input dari pemain lain - mereka tidak datang kepada kami. Karena itu, kami menjalankan sistem simulasi hanya pada pemain kami.

Pertama, ini memungkinkan untuk mengurangi waktu simulasi. Klien memulai simulasi segera setelah menerima input dan beberapa langkah di depan relatif terhadap server game. Katakanlah dalam gambar ini dia mensimulasikan tick # 20. Pada titik ini, server game mensimulasikan centang # 15 di masa lalu. Klien melihat seluruh dunia, lagi, di masa lalu, dirinya sendiri - di masa depan. Ketika ia mengirimkan centang ke-20 ke server, sementara input ini mencapai, server permainan sudah akan mulai mensimulasikan centang ke-18 atau sudah ke-20. Jika tanggal 18, maka ia meletakkannya di buffer, mencapai tanggal 20, memproses dan mengembalikan hasilnya.

Katakanlah sekarang dia mensimulasikan centang No. 15. Diproses, mengembalikan hasilnya ke klien. Klien memiliki semacam simulasi tick ke-15, kondisi game ke-15 dan dunia game yang ia prediksi. Perbandingan dengan server dimulai. Faktanya, dia tidak membandingkan seluruh dunia, tetapi hanya kliennya, karena kita tidak bertanggung jawab atas seluruh dunia. Kami hanya bertanggung jawab untuk diri kami sendiri. Jika pemain bertepatan, semuanya baik-baik saja, yang berarti kami disimulasikan dengan benar, fisika bekerja dengan benar dan tidak ada tabrakan yang muncul. Kemudian kami terus mensimulasikan centang ke 20, 21 dan seterusnya.

Jika klien / pemain tidak cocok, itu berarti kami salah di suatu tempat. Contoh: karena fisika tidak deterministik, itu tidak benar menghitung posisi kita atau sesuatu terjadi. Mungkin hanya bug. Kemudian klien mengambil status dari server game, karena server game telah mengonfirmasinya (dia mempercayai server - jika dia tidak percaya, para pemain akan menipu), dan akan mensimulasikan ulang sisanya dari tanggal 15 hingga 20. Karena cabang waktu ini sekarang salah.

Buat cabang waktu baru, mis. dunia paralel. Kami menirukan lima kutu ini dalam satu kutu. Setelah simulasi kami mengambil 5 milidetik, tetapi jika kita perlu mensimulasikan 10 kutu, itu sudah 50 milidetik dan kita tidak jatuh ke dalam 30 milidetik kita. Mereka dioptimalkan dan mendapat satu milidetik - sekarang 10 kutu diproses dalam 10 milidetik. Karena masih ada rendering.

Semua hal ini bekerja pada klien, dan kami memberikannya kepada orang tersebut tanpa pengalaman yang diperlukan. Minus - kami punya fakap, dan ditambah - bahwa programmer sekarang tahu bagaimana melakukannya dengan benar.



Skema ini memiliki karakteristik sendiri. Klien di gambar kiri sedang mencoba melacak musuh. Dia berada di tick ke-20, lawan di tick ke-15. Karena ping dan klien adalah 5 ticks di depan server. Klien menembak dan harus secara akurat mengenai dan menyebabkan kerusakan, mungkin bahkan headshot. Tetapi gambar berbeda di server - ketika server mulai mensimulasikan centang ke-20, musuh mungkin sudah bergerak. Misalnya saja jika musuh sedang bergerak. Secara teori, kita seharusnya tidak mendapatkannya. Tetapi jika itu bekerja seperti itu, maka tidak ada yang akan bermain penembak online karena kesalahan terus-menerus. Bergantung pada ping, kemungkinan memukul juga berubah: semakin buruk ping, semakin buruk yang Anda dapatkan. Karena itu, mereka melakukannya secara berbeda.

Server mengambil dan memutar seluruh dunia ke jati di mana pemain melihat dunia. Server tahu kapan itu, gulung kembali ke centang ke-15 dan melihat gambar kiri. Dia melihat bahwa pemain seharusnya memukul, dan memberikan damage kepada lawannya yang sudah berada di tick ke-20. Semuanya baik-baik saja. Hampir. Jika musuh melarikan diri dan berlari di belakang penghalang, maka kita sudah menembak melalui dinding. Tapi ini masalah yang diketahui, pemain tahu tentang itu dan jangan khawatir. Jadi itu berhasil, tidak ada yang bisa dilakukan tentang itu.



Jadi, kami telah mencapai 30 ticks per detik, 30 frame per detik. Sekarang sekitar 600 pemain bermain di server kami pada saat yang bersamaan. Ada 6 pemain dalam pertandingan, mis. sekitar 100 pertandingan. Kami tidak memiliki pemrogram server, kami tidak membutuhkannya. Klien menulis semua logika di editor Unity, Rider, di C # dan berfungsi pada server game. Hampir selalu. Kami mengurangi ukuran paket sebanyak 17 kali dan mengurangi alokasi memori hingga 80 kali - sekarang bahkan kurang dari satu kilobita pada klien dan server. Ping rata-rata adalah 200-250 ms, sekarang 150. 200 adalah standar untuk permainan jaringan seluler, tidak seperti PC, di mana semuanya terjadi jauh lebih cepat, terutama di jaringan lokal.



Kami berencana untuk mengisolasi apa yang tertulis dalam kerangka kerja terpisah untuk menggunakannya pada proyek lain. Namun sejauh ini, tidak ada pembicaraan tentang Open Source. Dan tambahkan interpolasi di sana. Sekarang kita memiliki 30 ticks per detik, kita dapat menggambar karena ticks. Tapi ada permainan di mana 20 ticks per detik atau 10 sudah cukup. Dengan demikian, jika kita menggambar 10 kali per detik, karakter akan bergerak tersentak. Oleh karena itu, diperlukan interpolasi. Kami menulis pustaka jaringan kami sendiri alih-alih Photon - tidak ada alokasi memori di sana.

Masih ada bagian yang Anda tidak bisa menulis dengan tangan Anda, tetapi menghasilkan kode. Misalnya, ketika kami mengirim keadaan dunia ke klien, kami memotong data yang tidak dibutuhkannya. Sementara kami melakukan ini dengan tangan kami dan ketika fitur baru muncul, dan kami lupa untuk memotong data ini, ada yang salah. Bahkan, ini dapat dihasilkan dengan menandai beberapa atribut.

Pertanyaan dari audiens


- Apa yang Anda gunakan untuk pembuatan kode? Keputusan Anda sendiri?

- Semuanya sederhana - tangan. Kami berpikir untuk menyiapkan sesuatu, tetapi ternyata lebih cepat hanya dengan menulis dengan tangan kami sendiri. Ikuti jalan ini, itu bekerja dengan baik dulu dan sekarang.

- Anda menolak pengembang server, tetapi Anda tidak hanya mengurangi waktu pengembangan karena fakta bahwa kode yang sama digunakan kembali. Unity tidak mendukung versi terbaru dari C #, ia memiliki mesin sendiri di bawah tenda. Anda tidak dapat menggunakan .NET Core, Anda tidak dapat menggunakan fitur terbaru, struktur tertentu, dan banyak lagi. Tidakkah kinerja menderita sekitar sepertiga dari ini?

- Ketika kami mulai melakukan semua ini, kami berpikir bahwa untuk menggunakan bukan kelas, tetapi struktur, itu seharusnya bekerja lebih cepat. Kami menulis prototipe bagaimana tampilannya dalam kode, bagaimana programmer akan menggunakan struktur ini untuk menulis logika. Dan itu sangat tidak nyaman. Kami memilih kelas dan kinerja yang kami miliki sekarang sudah cukup bagi kami.

- Bagaimana Anda hidup sekarang tanpa interpolasi? Dan bagaimana Anda berpura-pura menjadi pemain jika snapshot tidak datang di frame yang tepat?

- Kami memiliki interpolasi, hanya saja tidak visual, tetapi pada paket-paket yang datang melalui jaringan. Katakanlah kita memiliki negara ke 18, 19 dan 20.Tanggal 18 datang, tanggal 20 datang, dan tanggal 19 hilang atau belum tercapai - di sini kita menyisipkannya. Cukup gunakan pembuatan kode agar tidak menulis kode interpolasi.

- Apakah ada lagi hacks kehidupan untuk menekan angka empat lebih kuat?

- Saya berbicara tentang 2D - hanya ada sudut alfa, dan pada proyek baru angka empat memiliki masalah mereka sendiri di sana. Dari hack kehidupan, saya juga bisa mengatakan ini: karena kita menggunakan UDP sehingga input tidak hilang, kami mengirimkannya dalam batch: dari nol ke input kelima, lalu dari yang pertama ke keenam dan seterusnya. Lebih murah dan pengiriman lebih baik.

"Tapi urutan mereproduksi input ke server berperan?"

"Ya, tentu saja." - ( , , ), 2 : , .

β€” . ? ? 1000 , ? , ?

β€” , . , -, , . , 30 .

β€” , ?

β€” . , ( ), . β€” , , . - , , . , , , , . .

β€” ECS, , ? ?

β€” 30 , . 80 , . .

β€” prediction. 20- - , , - β€” , ? , . - ?

β€” : . (, 15-) 16-,17-,18- .

β€” ?

β€” , . , , . Entity ( ), . β€” , . ID , .

β€” - β€” , , , ? , ?

β€” , , . 3D , β€” , , - . , , . top-down, β€” . . , , , . .

β€” ?

β€” . Ini juga terjadi.

β€” , , - . - . - , , , 500 , , - - . ?

β€” .



, .. 20- 20- , . β€” , . : 20- , ? , . , β€” - . , . , Β« - , 21-, 18-Β». : Β«, - Β». .

β€” .. , ?

β€” , .

β€” reliable UDP β€” - ?

β€” Photon, Photon reliable UDP, unreliable, c .

β€” ?

β€” , -. , . , . , . , . 100%, , 80%, .

β€” ?

β€” , , Photon , MTU.

β€” ? ?

β€” , , , . . , . , , , .

β€” , , ?

β€” , . , . , , . , - . , . , .

β€” / β€” - . , .

β€” , . , ( ), -, , -. , . β€” , .

β€” , - . ?

β€” , . : ECS, . , ECS . , ECS . , . , , , ( , , , , , ). 2D , , 3D β€” . 3D , , . . - , . , - -, .

β€” , ECS , . , , C#?

β€” β€” .

β€” .. ES ? , ECS β€” , , , . .. ECS β€” , .

β€” , , . , . β€” , , . , O - , , .

β€” , ECS- ?

β€” -, ECS , , ( ) β€” , . , β€” . β€” , , . , , , ..

Pixonic DevGAMM Talks


Source: https://habr.com/ru/post/id429312/


All Articles