Bagaimana cara kerja Lua sebagai bahasa scripting dalam game?


67

Saya agak kabur tentang apa sebenarnya Lua dan bagaimana game yang diprogram dalam C ++ akan menggunakannya. Saya bertanya terutama tentang bagaimana ini dikompilasi dan dijalankan.

Misalnya ketika Anda menggunakan program yang ditulis dalam C ++ yang menggunakan skrip Lua: apakah kode dalam Lua hanya memanggil fungsi di program utama yang ditulis dalam C ++ dan bertindak sebagai kelas yang tidak dikompilasi menunggu untuk dikompilasi dan ditambahkan ke tumpukan memori C ++ program?

Atau apakah itu bertindak seperti skrip bash di Linux di mana ia menjalankan program yang benar-benar terpisah dari program utama?

Jawaban:


90

Scripting adalah abstraksi pemrograman di mana Anda (secara konseptual) memiliki program (skrip) yang berjalan di dalam program lain (host). Dalam kebanyakan kasus, bahasa tempat Anda menulis skrip berbeda dari bahasa di mana host ditulis, tetapi abstraksi program-dalam-program apa pun dapat dianggap sebagai skrip.

Secara konseptual, langkah-langkah umum untuk mengaktifkan skrip adalah sebagai berikut (Saya akan menggunakan pseudo-c untuk host dan pseudo-lua untuk skrip. Ini bukan langkah yang tepat, tetapi lebih seperti alur umum di mana Anda mengaktifkan skrip)

  1. Buat mesin virtual di program host:

    VM m_vm = createVM();
  2. Buat fungsi dan paparkan ke VM:

    void scriptPrintMessage(VM vm)
    {
        const char* message = getParameter(vm, 0); // first parameter
        printf(message);
    }
    
    //...
    
    createSymbol(m_vm, "print", scriptPrintMessage);

    Perhatikan bahwa nama tempat kita mengekspos fungsi ( print) tidak harus cocok dengan nama internal fungsi itu sendiri ( scriptPrintMessage)

  3. Jalankan beberapa kode skrip yang menggunakan fungsi:

    const char* scriptCode = "print(\"Hello world!\")"; // Could also be loaded from a file though
    doText(m_vm, scriptCode);

Itu semua yang ada untuk itu. Program kemudian mengalir dengan cara berikut:

  1. Kamu menelpon doText(). Kontrol kemudian ditransfer ke mesin virtual, yang akan mengeksekusi teks di dalamnya scriptCode.

  2. Kode skrip menemukan simbol yang diekspor sebelumnya print. Ini kemudian akan mentransfer kontrol ke fungsi scriptPrintMessage().

  3. Ketika scriptPrintMessage()selesai, kontrol akan ditransfer kembali ke mesin virtual.

  4. Ketika semua teks dalam scriptCodetelah dieksekusi, doText()akan selesai, dan kontrol akan ditransfer kembali ke program Anda setelahnya doText().

Jadi secara umum, yang Anda lakukan hanyalah menjalankan program di dalam program lain. Secara teoritis, tidak ada yang dapat Anda lakukan dengan skrip yang tidak dapat Anda lakukan tanpanya, tetapi abstraksi ini memungkinkan Anda melakukan beberapa hal menarik dengan sangat mudah. Beberapa dari mereka adalah:

  • Pemisahan kekhawatiran: itu adalah pola umum untuk menulis mesin gim dalam C / C ++ dan kemudian gim yang sebenarnya dalam bahasa skrip seperti lua. Dilakukan dengan benar, kode permainan dapat dikembangkan sepenuhnya secara independen dari mesin itu sendiri.

  • Fleksibilitas: bahasa skrip umumnya ditafsirkan, dan dengan demikian, perubahan pada skrip tidak akan memerlukan pembangunan kembali seluruh proyek. Selesai dengan benar, Anda bahkan dapat mengubah skrip dan melihat hasilnya tanpa memulai ulang program!

  • Stabilitas dan keamanan: karena skrip berjalan di dalam mesin virtual, jika dilakukan dengan benar, skrip buggy tidak akan merusak program host. Ini sangat penting saat Anda mengizinkan pengguna untuk menulis skrip sendiri ke gim Anda. Ingatlah bahwa Anda dapat membuat mesin virtual independen sebanyak yang Anda suka! (Saya pernah membuat server MMO di mana setiap pertandingan berjalan pada mesin virtual lua terpisah)

  • Fitur bahasa: saat menggunakan bahasa skrip, berdasarkan pilihan Anda untuk bahasa host dan skrip, Anda dapat memanfaatkan fitur terbaik yang ditawarkan oleh setiap bahasa. Secara khusus, lua's coroutine adalah fitur yang sangat menarik yang sangat sulit diimplementasikan dalam C atau C ++

Tapi skripnya tidak sempurna. Ada beberapa kelemahan umum untuk menggunakan skrip:

  • Debugging menjadi sangat sulit: biasanya, para debugger yang termasuk dalam IDE umum tidak dirancang untuk men-debug kode di dalam skrip. Karena itu, debugging jejak konsol jauh lebih umum daripada yang saya inginkan.

    Beberapa bahasa scripting seperti lua memiliki fasilitas debug yang dapat dimanfaatkan dalam beberapa IDE seperti Eclipse. Melakukan hal ini sangat sulit, dan sejujurnya saya belum pernah melihat debugging skrip berfungsi sebaik debugging asli.

    Ngomong-ngomong, kurangnya minat pengembang Unity dalam hal ini adalah kritik utama saya terhadap mesin game mereka, dan alasan utama saya tidak menggunakannya lagi, tapi saya ngelantur.

  • Integrasi IDE: Kecil kemungkinan bahwa IDE Anda akan tahu fungsi mana yang diekspor dari program Anda, dan dengan demikian, fitur seperti IntelliSense dan sejenisnya tidak mungkin bekerja dengan skrip Anda.

  • Kinerja: Menjadi program yang umum ditafsirkan, dan dimaksudkan untuk mesin virtual abstrak yang arsitekturnya mungkin berbeda dari perangkat keras yang sebenarnya, skrip biasanya lebih lambat dijalankan daripada kode asli. Beberapa VM seperti luaJIT dan V8 melakukan pekerjaan yang sangat baik. Ini mungkin terlihat jika Anda melakukan beberapa penggunaan skrip yang sangat berat.

    Biasanya, perubahan konteks (host-to-script dan script-to-host) sangat mahal, jadi Anda mungkin ingin meminimalkannya jika Anda mengalami masalah kinerja.

Cara Anda menggunakan skrip terserah Anda. Saya telah melihat skrip digunakan untuk hal-hal yang sederhana seperti memuat pengaturan, hingga serumit membuat seluruh game dalam bahasa scripting dengan mesin gim yang sangat tipis. Saya bahkan pernah melihat mesin game yang sangat eksotis yang menggabungkan skrip lua dan JavaScript (via V8).

Scripting hanyalah alat. Bagaimana Anda menggunakannya untuk membuat game yang hebat sepenuhnya terserah Anda.


Saya benar-benar baru dalam hal ini, tetapi saya menggunakan Unity. Anda menyebutkan sesuatu tentang Persatuan, dapatkah Anda menjelaskannya? Haruskah saya menggunakan sesuatu yang lain?
Tokamocha

1
Saya tidak akan menggunakan printfdalam contoh Anda (atau penggunaan minimal printf("%s", message);)
ratchet freak

1
@ratchetfreak: Tanda titik koma di akhir pesan Anda yang diikuti oleh tanda kurung berkedip padaku ...
Panda Pyjama

1
Salah satu jawaban terbaik yang pernah saya lihat di situs ini dalam waktu yang lama; itu layak setiap upvote yang didapatnya. Dilakukan dengan sangat baik.
Steven Stadnicki

1
Poster anak untuk Lua dalam permainan adalah World of Warcraft, di mana sebagian besar UI ditulis dalam Lua, dan sebagian besar dapat digantikan oleh pemain. Saya terkejut Anda tidak menyebutkannya.
Michael Hampton

7

Secara umum, Anda mengikat atau mengekspos beberapa fungsi asli ke Lua (sering menggunakan perpustakaan utilitas untuk melakukannya, meskipun Anda dapat melakukannya dengan tangan). Itu memungkinkan kode Lua untuk membuat panggilan ke kode C ++ asli Anda ketika game Anda mengeksekusi kode Lua itu. Dalam pengertian ini, asumsi Anda bahwa kode Lua hanya memanggil kode asli adalah benar (walaupun Lua memiliki pustaka fungsionalitas standar yang tersedia; Anda tidak perlu memanggil kode asli Anda untuk semuanya).

Kode Lua itu sendiri ditafsirkan oleh runtime Lua, yang merupakan kode C yang Anda tautkan sebagai perpustakaan (biasanya) dalam program Anda sendiri. Anda dapat membaca lebih lanjut tentang cara kerja Lua di beranda Lua . Secara khusus, Lua bukan "kelas yang tidak dikompilasi" seperti yang Anda duga, terutama jika Anda berpikir tentang kelas C ++, karena C ++ hampir tidak pernah dikompilasi secara dinamis dalam praktik. Namun, runtime Lua dan objek yang dibuat oleh skrip Lua yang dijalankan game Anda menghabiskan ruang dalam kumpulan memori sistem gim Anda.


5

Bahasa scripting seperti Lua dapat digunakan dalam beberapa cara. Seperti yang Anda katakan, Anda dapat menggunakan Lua untuk memanggil fungsi-fungsi dalam program utama tetapi Anda juga dapat memiliki fungsi Lua dipanggil dari sisi C ++ jika Anda mau. Umumnya Anda membangun antarmuka untuk memungkinkan fleksibilitas dengan bahasa scripting pilihan Anda sehingga Anda dapat menggunakan bahasa scripting dalam serangkaian skenario. Hal terbaik tentang bahasa skrip seperti Lua adalah ditafsirkan alih-alih dikompilasi sehingga Anda dapat memodifikasi skrip Lua dengan cepat sehingga Anda tidak perlu duduk-duduk menunggu gim Anda dikompilasi hanya untuk Anda kompilasi ulang jika apa yang Anda inginkan Sudah selesai tidak sesuai dengan selera Anda.

Perintah Lua hanya dipanggil ketika program C ++ ingin menjalankannya. Dengan demikian, kode hanya akan ditafsirkan ketika dipanggil. Saya kira Anda dapat menganggap Lua sebagai skrip bash yang dijalankan secara terpisah dari program utama.

Umumnya Anda ingin menggunakan Bahasa scripting untuk hal-hal yang Anda mungkin ingin memperbarui atau beralih lebih jauh ke bawah. Saya telah melihat banyak perusahaan menggunakannya untuk GUI sehingga memberikan banyak kemampuan untuk antarmuka. Memberikan Anda tahu bagaimana mereka membuat antarmuka Lua Anda, Anda juga dapat memodifikasi GUI sendiri. Tetapi ada banyak jalur lain yang dapat Anda ambil untuk menggunakan Lua, seperti logika AI, informasi tentang senjata, dialog karakter, dll.


3

Sebagian besar bahasa scripting, termasuk Lua, beroperasi pada Mesin Virtual ( VM ), yang pada dasarnya adalah sistem untuk memetakan instruksi skrip ke instruksi CPU nyata atau panggilan fungsi. Lua VM biasanya berjalan dalam proses yang sama dengan aplikasi utama. Ini khusus berlaku untuk gim yang menggunakannya. Lua API memberi Anda beberapa fungsi yang Anda panggil dalam aplikasi asli untuk memuat dan mengkompilasi file skrip. Misalnya luaL_dofile()mengkompilasi skrip yang diberikan ke Lua bytecode dan kemudian menjalankannya. Bytecode ini kemudian akan dipetakan oleh VM yang berjalan di dalam API ke instruksi mesin asli dan panggilan fungsi.

Proses menghubungkan bahasa asli, seperti C ++, dengan bahasa scripting disebut mengikat . Dalam kasus Lua, API-nya menyediakan fungsi yang membantu Anda mengekspos fungsi asli ke kode skrip. Jadi, misalnya, Anda dapat mendefinisikan fungsi C ++ say_hello()dan membuat fungsi ini dapat dipanggil dari skrip Lua. Lua API juga menyediakan metode untuk membuat variabel dan tabel melalui kode C ++ yang akan terlihat untuk skrip saat dijalankan. Dengan menggabungkan fitur-fitur ini Anda dapat mengekspos seluruh kelas C ++ ke Lua. Sebaliknya juga memungkinkan, Lua API memungkinkan pengguna untuk memodifikasi variabel Lua dan memanggil fungsi Lua dari kode C ++ asli.

Kebanyakan jika tidak semua bahasa skrip menyediakan API untuk memfasilitasi pengikatan kode skrip dengan kode asli. Sebagian besar juga dikompilasi menjadi bytecode dan dijalankan dalam VM, tetapi beberapa dapat ditafsirkan baris demi baris.

Semoga ini bisa membantu memperjelas beberapa pertanyaan Anda.


3

Karena tidak ada yang menyebutkan ini, saya akan menambahkannya di sini untuk mereka yang tertarik. Ada seluruh buku tentang subjek yang disebut Game Scripting Mastery . Ini adalah teks fantastis yang ditulis beberapa waktu yang lalu, tetapi tetap sepenuhnya relevan hingga saat ini.

Buku ini tidak hanya akan menunjukkan kepada Anda bagaimana bahasa scripting cocok dengan kode asli, tetapi juga mengajarkan Anda bagaimana menerapkan bahasa scripting Anda sendiri. Meskipun hal ini akan berlebihan bagi 99% pengguna, tidak ada cara yang lebih baik untuk memahami sesuatu daripada benar-benar mengimplementasikannya (bahkan dalam bentuk yang sangat mendasar).

Jika Anda ingin menulis sendiri mesin game (atau Anda hanya bekerja dengan mesin render), teks ini sangat berharga untuk memahami bagaimana bahasa scripting dapat dimasukkan ke dalam mesin / game Anda.

Dan jika Anda ingin membuat bahasa skrip Anda sendiri, ini adalah salah satu tempat terbaik untuk memulai (sejauh yang saya tahu).


2

Pertama, bahasa scripting BIASANYA tidak dikompilasi . Itu adalah sebagian besar dari apa yang umumnya mendefinisikan mereka sebagai bahasa scripting. Sebaliknya, mereka sering "ditafsirkan." Apa dasarnya ini berarti bahwa ada bahasa lain (yang adalah disusun, lebih sering daripada tidak) yang membaca dalam teks, secara real time, dan melakukan operasi, baris demi baris.

Perbedaan antara ini dan bahasa lain adalah bahasa scripting cenderung lebih sederhana (biasanya disebut "level lebih tinggi"). Namun, mereka juga cenderung sedikit lebih lambat, karena kompiler cenderung mengoptimalkan banyak masalah yang datang dengan "elemen manusia" pengkodean, dan biner yang dihasilkan cenderung lebih kecil dan lebih cepat dibaca, untuk mesin. Selain itu, ada sedikit overhead dari program lain yang perlu dijalankan untuk membaca kode yang sedang dijalankan, dengan program yang dikompilasi.

Sekarang, Anda mungkin berpikir, "Yah, saya rasa itu sedikit lebih mudah, tetapi mengapa seseorang mau menyerahkan semua kinerja itu untuk sedikit lebih mudah digunakan?"

Anda tidak akan sendirian dalam asumsi ini, namun tingkat kemudahan yang Anda cenderung dapatkan dengan bahasa scripting, tergantung pada apa yang Anda lakukan dengan mereka, dapat sebanding dengan pengorbanan dalam kinerja.

Pada dasarnya: Untuk contoh di mana kecepatan pengembangan lebih penting daripada kecepatan program yang dijalankan - gunakan bahasa scripting. Ada banyak situasi seperti ini dalam pengembangan game. Terutama ketika berhadapan dengan hal-hal sepele seperti penanganan acara tingkat tinggi.

Sunting: Alasan lua cenderung agak populer dalam pengembangan game adalah karena ini bisa dibilang salah satu yang tercepat (jika bukan yang tercepat) yang tersedia untuk bahasa scripting di muka bumi. Namun, dengan kecepatan ekstra ini, ia telah mengorbankan beberapa kemudahannya. Yang sedang berkata, itu masih bisa dibilang lebih nyaman daripada bekerja dengan C atau C ++ lurus.

Sunting Penting: Setelah penelitian lebih lanjut, saya telah menemukan bahwa ada lebih banyak kontroversi mengenai definisi bahasa scripting (lihat dikotomi Ousterhout ). Kritik utama mendefinisikan bahasa sebagai "bahasa scripting" adalah bahwa itu tidak signifikan terhadap sintaks atau semantik bahasa yang ditafsirkan atau dikompilasi.

Sementara bahasa yang biasanya dianggap "bahasa scripting" biasanya ditafsirkan secara tradisional, bukan dikompilasi, panjang dan pendek dari definisi mereka sebagai "bahasa scripting" benar-benar sampai pada kombinasi bagaimana orang melihatnya, dan bagaimana pencipta mereka mendefinisikannya.

Secara umum, suatu bahasa dapat dengan mudah dianggap sebagai bahasa scripting (dengan asumsi Anda setuju dengan dikotomi Ousterhout) jika memenuhi kriteria berikut (menurut artikel yang ditautkan, di atas):

  • Mereka diketik secara dinamis
  • Mereka memiliki sedikit atau tidak ada ketentuan untuk struktur data yang kompleks
  • Program di dalamnya (skrip) ditafsirkan

Selain itu, sering diterima bahwa bahasa adalah bahasa scripting jika dirancang untuk berinteraksi dan berfungsi bersama bahasa pemrograman lain (biasanya bahasa yang tidak dianggap sebagai bahasa scripting).


1
Saya tidak setuju dengan Anda di baris pertama tempat Anda menulis "bahasa skrip tidak dikompilasi". Lua sebenarnya dikompilasi menjadi bytecode antara sebelum dieksekusi oleh kompiler JIT. Beberapa tentu saja diinterpretasikan baris demi baris, tetapi tidak semua.
glampert

Saya tidak setuju dengan definisi Anda tentang "bahasa scripting." Ada bahasa dengan penggunaan ganda (seperti Lua, atau C #) yang relatif umum digunakan dalam bentuk yang dikompilasi, daripada bentuk skrip (atau sebaliknya, seperti halnya dengan C #). Ketika suatu bahasa digunakan sebagai bahasa scripting, ia secara tegas didefinisikan sebagai bahasa yang ditafsirkan, dan tidak dikompilasi.
Gurgadurgen

Cukup adil, definisi Anda lebih sesuai dengan Wikipedia: "Bahasa scripting atau bahasa skrip adalah bahasa pemrograman yang mendukung skrip, program yang ditulis untuk lingkungan run-time khusus yang dapat menafsirkan (daripada mengkompilasi) ...", tidak apa-apa komentar saya kemudian.
glampert

1
Bahkan Lua reguler sepenuhnya dikompilasi ke bytecode, kompiler JIT bahkan dapat menghasilkan biner asli. Jadi tidak, Lua tidak ditafsirkan.
Oleg V. Volkov

Bagian pertama adalah rapuh, saya tergoda untuk downvote. Dia agak benar karena pada akhirnya ditafsirkan dan @ OlegV.Volkov kompilasi JIT tidak membuat sesuatu dikompilasi. Kompilasi ditentukan oleh waktu kompilasi, yang dimiliki Lua, kode byte Lua tidak (JIT atau tanpa JIT). Jangan sampai istilah kita kacau.
Alec Teal
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.