Izstrādājiet jaudīgu renderēšanas cauruļvadu jūsu Python spēļu dzinējam. Koncentrējieties uz starpplatformu saderību un modernām renderēšanas metodēm.
Python spēļu dzinējs: Renderēšanas cauruļvada ieviešana starpplatformu veiksmei
Spēļu dzinēja izveide ir sarežģīts, bet atalgojošs pasākums. Jebkura spēļu dzinēja pamatā ir renderēšanas cauruļvads, kas atbild par spēļu datu pārveidošanu vizuālajā informācijā, ko redz spēlētāji. Šis raksts pēta renderēšanas cauruļvada ieviešanu uz Python balstītā spēļu dzinējā, īpaši koncentrējoties uz starpplatformu saderības sasniegšanu un moderno renderēšanas tehniku izmantošanu.
Renderēšanas cauruļvada izpratne
Renderēšanas cauruļvads ir secīgu posmu virkne, kas ņem 3D modeļus, tekstūras un citus spēles datus un pārveido tos 2D attēlā, kas tiek rādīts ekrānā. Tipisks renderēšanas cauruļvads sastāv no vairākiem posmiem:
- Ievades montāža: Šis posms apkopo virsotņu datus (pozīcijas, normāles, tekstūras koordinātes) un apvieno tos primitīvos (trijstūros, līnijās, punktos).
- Virsotņu šaders: Programma, kas apstrādā katru virsotni, veicot transformācijas (piemēram, modeļa-skata-projekcijas), aprēķinot apgaismojumu un modificējot virsotņu atribūtus.
- Ģeometrijas šaders (pēc izvēles): Darbojas ar veseliem primitīviem (trijstūriem, līnijām vai punktiem) un var radīt jaunus primitīvus vai atmest esošos. Mūsdienu cauruļvados tiek izmantots retāk.
- Rasterizācija: Pārveido primitīvus fragmentos (potenciālos pikseļos). Tas ietver noteikšanu, kuri pikseļi ir pārklāti ar katru primitīvu, un virsotņu atribūtu interpolāciju pa primitīva virsmu.
- Fragmentu šaders: Programma, kas apstrādā katru fragmentu, nosakot tā galīgo krāsu. Tas bieži ietver sarežģītus apgaismojuma aprēķinus, tekstūras meklēšanu un citus efektus.
- Izvades apvienošana: Apvieno fragmentu krāsas ar esošajiem pikseļu datiem kadru buferī, veicot tādas darbības kā dziļuma pārbaude un sajaukšana.
Grafikas API izvēle
Jūsu renderēšanas cauruļvada pamats ir izvēlētais grafikas API. Ir pieejamas vairākas iespējas, katrai no tām ir savas stiprās puses un vājības:
- OpenGL: Plaši atbalstīts starpplatformu API, kas pastāv jau daudzus gadus. OpenGL nodrošina lielu daudzumu piemēru koda un dokumentācijas. Tā ir laba izvēle projektiem, kuriem jādarbojas uz plaša spektra platformām, ieskaitot vecāku aparatūru. Tomēr tā vecākās versijas var būt mazāk efektīvas nekā modernākie API.
- DirectX: Microsoft patentētais API, ko galvenokārt izmanto Windows un Xbox platformās. DirectX piedāvā izcilu veiktspēju un piekļuvi jaunākajām aparatūras funkcijām. Tomēr tas nav starpplatformu. Apsveriet to, ja Windows ir jūsu galvenā vai vienīgā mērķa platforma.
- Vulkan: Mūsdienīgs, zema līmeņa API, kas nodrošina smalku kontroli pār GPU. Vulkan piedāvā izcilu veiktspēju un efektivitāti, taču tas ir sarežģītāks lietošanā nekā OpenGL vai DirectX. Tas nodrošina labākas vairākpavedienu iespējas.
- Metal: Apple patentētais API iOS un macOS. Tāpat kā DirectX, Metal piedāvā izcilu veiktspēju, taču tas ir ierobežots tikai Apple platformām.
- WebGPU: Jauns API, kas paredzēts tīmeklim, piedāvājot mūsdienīgas grafikas iespējas tīmekļa pārlūkprogrammās. Starpplatformu saderība tīmeklī.
Starpplatformu Python spēļu dzinējam OpenGL vai Vulkan parasti ir labākās izvēles. OpenGL piedāvā plašāku saderību un vieglāku iestatīšanu, savukārt Vulkan nodrošina labāku veiktspēju un lielāku kontroli. Vulkan sarežģītību var mazināt, izmantojot abstrakcijas bibliotēkas.
Python saites grafikas API
Lai izmantotu grafikas API no Python, jums būs jāizmanto saites (bindings). Ir pieejamas vairākas populāras iespējas:
- PyOpenGL: Plaši izmantota saite OpenGL. Tā nodrošina salīdzinoši plānu apli ap OpenGL API, ļaujot tieši piekļūt lielākajai daļai tā funkcionalitātes.
- glfw: (OpenGL Framework) Viegls, starpplatformu bibliotēka logu izveidei un ievades apstrādei. Bieži tiek izmantota kopā ar PyOpenGL.
- PyVulkan: Saite Vulkan. Vulkan ir jaunāks un sarežģītāks API nekā OpenGL, tāpēc PyVulkan prasa dziļāku izpratni par grafikas programmēšanu.
- sdl2: (Simple DirectMedia Layer) Starpplatformu bibliotēka multivides izstrādei, ieskaitot grafiku, audio un ievadi. Lai gan tā nav tieša saite uz OpenGL vai Vulkan, tā var izveidot logus un kontekstus šiem API.
Šajā piemērā mēs koncentrēsimies uz PyOpenGL ar glfw izmantošanu, jo tas nodrošina labu līdzsvaru starp lietošanas ērtumu un funkcionalitāti.
Renderēšanas konteksta iestatīšana
Pirms varat sākt renderēt, jums jāiestata renderēšanas konteksts. Tas ietver loga izveidi un grafikas API inicializēšanu.
````python import glfw from OpenGL.GL import * # Inicializēt GLFW if not glfw.init(): raise Exception("GLFW inicializācija neizdevās!") # Izveidot logu window = glfw.create_window(800, 600, "Python spēļu dzinējs", None, None) if not window: glfw.terminate() raise Exception("GLFW loga izveide neizdevās!") # Padarīt logu par pašreizējo kontekstu glfw.make_context_current(window) # Iespējot v-sync (pēc izvēles) glfw.swap_interval(1) print(f"OpenGL versija: {glGetString(GL_VERSION).decode()}") ````Šis koda fragments inicializē GLFW, izveido logu, padara logu par pašreizējo OpenGL kontekstu un iespējo v-sync (vertikālo sinhronizāciju), lai novērstu ekrāna plīsumus. `print` paziņojums parāda pašreizējo OpenGL versiju atkļūdošanas nolūkos.
Virsotņu bufera objektu (VBO) izveide
Virsotņu bufera objekti (VBO) tiek izmantoti, lai uzglabātu virsotņu datus GPU. Tas ļauj GPU tieši piekļūt datiem, kas ir daudz ātrāk nekā pārsūtīt tos no CPU katrā kadrā.
````python # Virsotņu dati trijstūrim vertices = [ -0.5, -0.5, 0.0, 0.5, -0.5, 0.0, 0.0, 0.5, 0.0 ] # Izveidot VBO vbo = glGenBuffers(1) glBindBuffer(GL_ARRAY_BUFFER, vbo) glBufferData(GL_ARRAY_BUFFER, len(vertices) * 4, (GLfloat * len(vertices))(*vertices), GL_STATIC_DRAW) ````Šis kods izveido VBO, piesaista to `GL_ARRAY_BUFFER` mērķim un augšupielādē virsotņu datus VBO. `GL_STATIC_DRAW` karodziņš norāda, ka virsotņu dati netiks bieži modificēti. `len(vertices) * 4` daļa aprēķina nepieciešamo izmēru baitos, lai saglabātu virsotņu datus.
Virsotņu masīvu objektu (VAO) izveide
Virsotņu masīvu objekti (VAO) glabā virsotņu atribūtu rādītāju stāvokli. Tas ietver VBO, kas saistīts ar katru atribūtu, atribūta izmēru, atribūta datu tipu un atribūta nobīdi VBO iekšienē. VAO vienkāršo renderēšanas procesu, ļaujot ātri pārslēgties starp dažādiem virsotņu izkārtojumiem.
````python # Izveidot VAO vao = glGenVertexArrays(1) glBindVertexArray(vao) # Norādīt virsotņu datu izkārtojumu glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None) glEnableVertexAttribArray(0) ````Šis kods izveido VAO, piesaista to un norāda virsotņu datu izkārtojumu. `glVertexAttribPointer` funkcija paskaidro OpenGL, kā interpretēt virsotņu datus VBO. Pirmais arguments (0) ir atribūta indekss, kas atbilst atribūta `location` virsotņu šaderī. Otrais arguments (3) ir atribūta izmērs (3 peldošā komata skaitļi x, y, z). Trešais arguments (GL_FLOAT) ir datu tips. Ceturtais arguments (GL_FALSE) norāda, vai dati jānormalizē. Piektais arguments (0) ir solis (baitu skaits starp secīgiem virsotņu atribūtiem). Sestais arguments (None) ir pirmā atribūta nobīde VBO iekšienē.
Šaderu izveide
Šaderi ir programmas, kas darbojas uz GPU un veic faktisko renderēšanu. Ir divi galvenie šaderu veidi: virsotņu šaderi un fragmentu šaderi.
````python # Virsotņu šadera pirmkods vertex_shader_source = """ \n#version 330 core\nlayout (location = 0) in vec3 aPos;\nvoid main()\n{\n gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n}\n""" # Fragmentu šadera pirmkods fragment_shader_source = """\n#version 330 core\nout vec4 FragColor;\nvoid main()\n{\n FragColor = vec4(1.0, 0.5, 0.2, 1.0); // Oranža krāsa\n}\n""" # Izveidot virsotņu šaderi vertex_shader = glCreateShader(GL_VERTEX_SHADER) glShaderSource(vertex_shader, vertex_shader_source) glCompileShader(vertex_shader) # Pārbaudīt virsotņu šadera kompilācijas kļūdas success = glGetShaderiv(vertex_shader, GL_COMPILE_STATUS) if not success: info_log = glGetShaderInfoLog(vertex_shader) print(f"KĻŪDA::ŠADERS::VIRSOTNES::KOMPILĀCIJA_NEIZDEVĀS\n{info_log.decode()}") # Izveidot fragmentu šaderi fragment_shader = glCreateShader(GL_FRAGMENT_SHADER) glShaderSource(fragment_shader, fragment_shader_source) glCompileShader(fragment_shader) # Pārbaudīt fragmentu šadera kompilācijas kļūdas success = glGetShaderiv(fragment_shader, GL_COMPILE_STATUS) if not success: info_log = glGetShaderInfoLog(fragment_shader) print(f"KĻŪDA::ŠADERS::FRAGMENTA::KOMPILĀCIJA_NEIZDEVĀS\n{info_log.decode()}") # Izveidot šaderu programmu shader_program = glCreateProgram() glAttachShader(shader_program, vertex_shader) glAttachShader(shader_program, fragment_shader) glLinkProgram(shader_program) # Pārbaudīt šaderu programmas sasaistes kļūdas success = glGetProgramiv(shader_program, GL_LINK_STATUS) if not success: info_log = glGetProgramInfoLog(shader_program) print(f"KĻŪDA::ŠADERS::PROGRAMMA::SASAISTE_NEIZDEVĀS\n{info_log.decode()}") glDeleteShader(vertex_shader) glDeleteShader(fragment_shader) ````Šis kods izveido virsotņu šaderi un fragmentu šaderi, kompilē tos un sasaista tos šaderu programmā. Virsotņu šaders vienkārši pārsūta virsotnes pozīciju, un fragmentu šaders izvada oranžu krāsu. Iekļauta kļūdu pārbaude, lai fiksētu kompilācijas vai sasaistes problēmas. Šaderu objekti tiek dzēsti pēc sasaistes, jo tie vairs nav nepieciešami.
Renderēšanas cikls
Renderēšanas cikls ir spēļu dzinēja galvenais cikls. Tas nepārtraukti renderē ainu uz ekrāna.
````python # Renderēšanas cikls while not glfw.window_should_close(window): # Apstrādāt notikumus (tastatūra, pele utt.) glfw.poll_events() # Iztīrīt krāsu buferi glClearColor(0.2, 0.3, 0.3, 1.0) glClear(GL_COLOR_BUFFER_BIT) # Izmantot šaderu programmu glUseProgram(shader_program) # Piesaistīt VAO glBindVertexArray(vao) # Uzzīmēt trijstūri glDrawArrays(GL_TRIANGLES, 0, 3) # Apmainīt priekšējos un aizmugurējos buferus glfw.swap_buffers(window) # Pārtraukt GLFW darbību glfw.terminate() ````Šis kods notīra krāsu buferi, izmanto šaderu programmu, piesaista VAO, uzzīmē trijstūri un apmaina priekšējos un aizmugurējos buferus. Funkcija `glfw.poll_events()` apstrādā notikumus, piemēram, tastatūras ievadi un peles kustību. Funkcija `glClearColor` iestata fona krāsu un funkcija `glClear` notīra ekrānu ar norādīto krāsu. Funkcija `glDrawArrays` zīmē trijstūri, izmantojot norādīto primitīva veidu (GL_TRIANGLES), sākot no pirmās virsotnes (0), un zīmējot 3 virsotnes.
Starpplatformu apsvērumi
Starpplatformu saderības sasniegšanai nepieciešama rūpīga plānošana un apsvēršana. Šeit ir dažas galvenās jomas, kurām jāpievērš uzmanība:
- Grafikas API abstrakcija: Vissvarīgākais solis ir abstrahēt pamatā esošo grafikas API. Tas nozīmē izveidot koda slāni, kas atrodas starp jūsu spēļu dzinēju un API, nodrošinot konsekventu saskarni neatkarīgi no platformas. Bibliotēkas, piemēram, bgfx vai pielāgoti implementācijas varianti, ir laba izvēle šim nolūkam.
- Šaderu valoda: OpenGL izmanto GLSL, DirectX izmanto HLSL, un Vulkan var izmantot SPIR-V vai GLSL (ar kompilatoru). Izmantojiet starpplatformu šaderu kompilatoru, piemēram, glslangValidator vai SPIRV-Cross, lai pārvērstu jūsu šaderus atbilstošā formātā katrai platformai.
- Resursu pārvaldība: Dažādām platformām var būt dažādi resursu izmēru un formātu ierobežojumi. Ir svarīgi šīs atšķirības apstrādāt graciozi, piemēram, izmantojot tekstūru saspiešanas formātus, kas tiek atbalstīti visās mērķa platformās, vai vajadzības gadījumā samazinot tekstūru izmērus.
- Būvēšanas sistēma: Izmantojiet starpplatformu būvēšanas sistēmu, piemēram, CMake vai Premake, lai ģenerētu projektu failus dažādām IDE un kompilatoriem. Tas atvieglos jūsu spēļu dzinēja būvēšanu dažādās platformās.
- Ievades apstrāde: Dažādām platformām ir dažādas ievades ierīces un ievades API. Izmantojiet starpplatformu ievades bibliotēku, piemēram, GLFW vai SDL2, lai apstrādātu ievadi konsekventi dažādās platformās.
- Failu sistēma: Failu sistēmas ceļi var atšķirties starp platformām (piemēram, "/" pret "\"). Izmantojiet starpplatformu failu sistēmas bibliotēkas vai funkcijas, lai nodrošinātu pārnesamu piekļuvi failiem.
- Baitu secība: Dažādās platformās var tikt izmantotas dažādas baitu secības (endianness). Esiet uzmanīgi, strādājot ar bināriem datiem, lai nodrošinātu to pareizu interpretāciju visās platformās.
Modernās renderēšanas tehnikas
Modernās renderēšanas tehnikas var ievērojami uzlabot jūsu spēļu dzinēja vizuālo kvalitāti un veiktspēju. Šeit ir daži piemēri:
- Atliktā renderēšana: Renderē ainu vairākās piegājienos, vispirms rakstot virsmas īpašības (piemēram, krāsu, normāli, dziļumu) buferu kopā (G-buferī), un pēc tam veicot apgaismojuma aprēķinus atsevišķā piegājienā. Atliktā renderēšana var uzlabot veiktspēju, samazinot apgaismojuma aprēķinu skaitu.
- Fiziski balstīta renderēšana (PBR): Izmanto fiziski balstītus modeļus, lai simulētu gaismas mijiedarbību ar virsmām. PBR var radīt reālistiskākus un vizuāli pievilcīgākus rezultātus. Teksturēšanas darbplūsmas var prasīt specializētu programmatūru, piemēram, Substance Painter vai Quixel Mixer, kas ir programmatūras piemēri, kas pieejami māksliniekiem dažādos reģionos.
- Ēnu kartēšana: Izveido ēnu kartes, renderējot ainu no gaismas perspektīvas. Ēnu kartēšana var pievienot ainai dziļumu un reālismu.
- Globālais apgaismojums: Simulē netiešo gaismas apgaismojumu ainā. Globālais apgaismojums var ievērojami uzlabot ainas reālismu, taču tas ir computationally dārgs. Tehnikas ietver staru izsekošanu, ceļu izsekošanu un ekrāna telpas globālo apgaismojumu (SSGI).
- Pēcapstrādes efekti: Lieto efektus renderētam attēlam pēc tā renderēšanas. Pēcapstrādes efektus var izmantot, lai pievienotu ainai vizuālu krāšņumu vai labotu attēla nepilnības. Piemēri ietver ziedēšanu (bloom), lauka dziļumu un krāsu gradāciju.
- Skaitļošanas šaderi: Izmanto vispārējas nozīmes aprēķiniem GPU. Skaitļošanas šaderus var izmantot plašam uzdevumu klāstam, piemēram, daļiņu simulācijai, fizikas simulācijai un attēlu apstrādei.
Piemērs: Pamata apgaismojuma ieviešana
Lai demonstrētu modernu renderēšanas tehniku, pievienosim mūsu trijstūrim pamata apgaismojumu. Vispirms mums jāmodificē virsotņu šaders, lai aprēķinātu normāles vektoru katrai virsotnei un nodotu to fragmentu šaderim.
````glsl // Virsotņu šaders #version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; out vec3 Normal; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { Normal = mat3(transpose(inverse(model))) * aNormal; gl_Position = projection * view * model * vec4(aPos, 1.0); } ````Pēc tam mums jāmodificē fragmentu šaders, lai veiktu apgaismojuma aprēķinus. Mēs izmantosim vienkāršu difūzās gaismas modeli.
````glsl // Fragmentu šaders #version 330 core out vec4 FragColor; in vec3 Normal; uniform vec3 lightPos; uniform vec3 lightColor; uniform vec3 objectColor; void main() { // Normalizēt normāles vektoru vec3 normal = normalize(Normal); // Aprēķināt gaismas virzienu vec3 lightDir = normalize(lightPos - vec3(0.0)); // Aprēķināt difūzo komponentu float diff = max(dot(normal, lightDir), 0.0); vec3 diffuse = diff * lightColor; // Aprēķināt galīgo krāsu vec3 result = diffuse * objectColor; FragColor = vec4(result, 1.0); } ````Visbeidzot, mums jāatjaunina Python kods, lai nodotu normāles datus virsotņu šaderim un iestatītu uniform mainīgos gaismas pozīcijai, gaismas krāsai un objekta krāsai.
````python # Virsotņu dati ar normālēm vertices = [ # Pozīcijas # Normāles -0.5, -0.5, 0.0, 0.0, 0.0, 1.0, 0.5, -0.5, 0.0, 0.0, 0.0, 1.0, 0.0, 0.5, 0.0, 0.0, 0.0, 1.0 ] # Izveidot VBO vbo = glGenBuffers(1) glBindBuffer(GL_ARRAY_BUFFER, vbo) glBufferData(GL_ARRAY_BUFFER, len(vertices) * 4, (GLfloat * len(vertices))(*vertices), GL_STATIC_DRAW) # Izveidot VAO vao = glGenVertexArrays(1) glBindVertexArray(vao) # Pozīcijas atribūts glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * 4, ctypes.c_void_p(0)) glEnableVertexAttribArray(0) # Normāles atribūts glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * 4, ctypes.c_void_p(3 * 4)) glEnableVertexAttribArray(1) # Iegūt uniformu atrašanās vietas light_pos_loc = glGetUniformLocation(shader_program, "lightPos") light_color_loc = glGetUniformLocation(shader_program, "lightColor") object_color_loc = glGetUniformLocation(shader_program, "objectColor") # Iestatīt uniformu vērtības glUniform3f(light_pos_loc, 1.0, 1.0, 1.0) glUniform3f(light_color_loc, 1.0, 1.0, 1.0) glUniform3f(object_color_loc, 1.0, 0.5, 0.2) ````Šis piemērs demonstrē, kā ieviest pamata apgaismojumu jūsu renderēšanas cauruļvadā. Jūs varat paplašināt šo piemēru, pievienojot sarežģītākus apgaismojuma modeļus, ēnu kartēšanu un citas renderēšanas tehnikas.
Paplašinātās tēmas
Papildus pamatiem, vairākas paplašinātās tēmas var vēl vairāk uzlabot jūsu renderēšanas cauruļvadu:
- Instancēšana: Vairāku tā paša objekta instanču renderēšana ar dažādām transformācijām, izmantojot vienu zīmēšanas izsaukumu.
- Ģeometrijas šaderi: Dinamiska jaunas ģeometrijas ģenerēšana GPU.
- Teselācijas šaderi: Virsmu sadalīšana, lai izveidotu gludākus un detalizētākus modeļus.
- Skaitļošanas šaderi: GPU izmantošana vispārējas nozīmes skaitļošanas uzdevumiem, piemēram, fizikas simulācijai un attēlu apstrādei.
- Staru izsekošana: Gaismas staru ceļa simulēšana, lai radītu reālistiskākus attēlus. (Nepieciešams saderīgs GPU un API)
- Virtuālās realitātes (VR) un papildinātās realitātes (AR) renderēšana: Tehnikas stereoskopisku attēlu renderēšanai un virtuālā satura integrēšanai ar reālo pasauli.
Renderēšanas cauruļvada atkļūdošana
Renderēšanas cauruļvada atkļūdošana var būt sarežģīta. Šeit ir daži noderīgi rīki un metodes:
- OpenGL atkļūdotājs: Rīki, piemēram, RenderDoc vai iebūvētie grafikas draiveru atkļūdotāji, var palīdzēt pārbaudīt GPU stāvokli un identificēt renderēšanas kļūdas.
- Šaderu atkļūdotājs: IDE un atkļūdotāji bieži nodrošina šaderu atkļūdošanas funkcijas, ļaujot jums pakāpeniski iziet cauri šaderu kodam un pārbaudīt mainīgo vērtības.
- Kadru atkļūdotāji: Uztveriet un analizējiet atsevišķus kadrus, lai identificētu veiktspējas vājās vietas un renderēšanas problēmas.
- Žurnālrakstīšana un kļūdu pārbaude: Pievienojiet žurnālrakstīšanas paziņojumus savam kodam, lai izsekotu izpildes plūsmu un identificētu potenciālās problēmas. Vienmēr pārbaudiet OpenGL kļūdas pēc katra API izsaukuma, izmantojot `glGetError()`.
- Vizuālā atkļūdošana: Izmantojiet vizuālās atkļūdošanas metodes, piemēram, ainas dažādu daļu renderēšanu dažādās krāsās, lai izolētu renderēšanas problēmas.
Secinājums
Renderēšanas cauruļvada ieviešana Python spēļu dzinējam ir sarežģīts, bet atalgojošs process. Izprotot dažādus cauruļvada posmus, izvēloties pareizo grafikas API un izmantojot modernas renderēšanas tehnikas, jūs varat izveidot vizuāli satriecošas un veiktspējīgas spēles, kas darbojas uz plaša spektra platformām. Atcerieties par prioritāti noteikt starpplatformu saderību, abstrahējot grafikas API un izmantojot starpplatformu rīkus un bibliotēkas. Šī apņemšanās paplašinās jūsu auditorijas sasniedzamību un veicinās jūsu spēļu dzinēja ilgstošu veiksmi.
Šis raksts nodrošina sākumpunktu jūsu pašu renderēšanas cauruļvada izveidei. Eksperimentējiet ar dažādām tehnikām un pieejām, lai atrastu to, kas vislabāk der jūsu spēļu dzinējam un mērķa platformām. Veiksmi!