Engine, bahasa scripting, dan novel visual - dalam 45 jam

novel visual, skrip mesin dan bahasa selama 45 jam


Salam Kebetulan selama tiga tahun berturut-turut saya menjadikan permainan sebagai hadiah untuk Tahun Baru bagi orang-orang tertentu. Pada tahun 2018, itu adalah platformer dengan elemen teka-teki, yang saya tulis di hub Pada 2019 - RTS jaringan untuk dua pemain, tentang yang saya tidak menulis apa pun. Dan akhirnya, pada tahun 2020 - sebuah cerita pendek visual, yang akan dibahas nanti, dibuat dalam waktu yang sangat terbatas.


Dalam artikel ini:


  • desain dan implementasi mesin untuk cerita pendek visual,
  • permainan dengan plot non-linear dalam 8 jam,
  • penghapusan logika game dalam skrip dalam bahasa mereka sendiri.

Menarik? Kemudian selamat datang di kucing.


Perhatian: ada banyak teks dan ~ 3.5mb gambar


Konten:


0. Justifikasi untuk pengembangan mesin.


  1. Pilihan platform.
  2. Arsitektur mesin dan implementasinya:
    2.1. Pernyataan masalah.
    2.2. Arsitektur dan implementasi.
  3. Bahasa skrip:
    3.1. Bahasa
    3.2. Penerjemah
  4. Pengembangan game:
    4.1. Sejarah dan perkembangan logika game.
    4.2. Grafik
  5. Statistik dan hasil.

Catatan: Jika karena alasan tertentu Anda tidak tertarik pada detail teknis, Anda dapat langsung beralih ke langkah 4 β€œPengembangan Game”, namun Anda akan melewatkan sebagian besar konten


0. Justifikasi untuk pengembangan mesin


Tentu saja, ada sejumlah besar mesin siap pakai untuk cerita pendek visual, yang, tidak diragukan lagi, lebih baik daripada solusi yang dijelaskan di bawah ini. Namun, tidak peduli apa pun programmer saya, jika saya tidak menulis yang lain. Karena itu, mari kita berpura-pura bahwa perkembangannya dibenarkan.


1. Pemilihan platform


Faktanya, pilihannya kecil: Java atau C ++. Tanpa berpikir dua kali, saya memutuskan untuk mengimplementasikan rencana saya di Jawa, karena untuk pengembangan cepat, ia menyediakan semua kemungkinan (yaitu, manajemen memori otomatis dan kesederhanaan yang lebih besar dibandingkan dengan C ++, yang menyembunyikan banyak detail tingkat rendah dan, sebagai hasilnya, memungkinkan penekanan yang kurang pada bahasa itu sendiri dan hanya memikirkan logika bisnis), dan juga menyediakan dukungan untuk windows, grafik, dan audio di luar kotak.


Swing dipilih untuk mengimplementasikan antarmuka grafis, karena saya menggunakan Java 13, di mana JavaFX tidak lagi menjadi bagian dari perpustakaan, dan menambahkan puluhan megabita OpenJFX tergantung pada itu terlalu malas. Mungkin ini bukan solusi terbaik, tapi tetap saja.


Mungkin muncul pertanyaan: mesin gim macam apa itu, tetapi tanpa akselerasi perangkat keras? Jawabannya terletak pada kurangnya waktu untuk berurusan dengan OpenGL, dan juga tanpa makna mutlaknya: FPS tidak penting untuk novel visual (dalam hal apa pun, dengan animasi dan grafik sebanyak dalam hal ini).


2. Arsitektur mesin dan implementasinya


2.1 pernyataan masalah


Untuk memutuskan bagaimana melakukan sesuatu, Anda perlu memutuskan mengapa. Ini saya tentang pernyataan masalah, arsitekturnya tidak universal, tetapi mesin "khusus domain", menurut definisi, secara langsung tergantung pada permainan yang dimaksud.


Dengan mesin universal, saya memahami mesin yang mendukung konsep tingkat rendah, seperti "Obyek Game", "Adegan", "Komponen". Diputuskan untuk membuatnya bukan mesin universal, karena ini akan secara signifikan mengurangi waktu pengembangan.


Seperti yang direncanakan, permainan harus terdiri dari bagian-bagian berikut:


struktur permainan novel visual


Artinya, ada latar belakang untuk setiap adegan, teks utama, serta bidang teks untuk input pengguna (novel visual dikandung dengan input pengguna sewenang-wenang, dan tidak dengan pilihan dari opsi yang diusulkan, seperti yang sering terjadi. Nanti saya akan menjelaskan mengapa ini buruk keputusan). Diagram juga menunjukkan bahwa ada beberapa adegan dalam permainan dan, sebagai akibatnya, transisi dapat dibuat di antara mereka.


Catatan: Menurut adegan saya maksud bagian logis dari permainan. Kriteria adegan bisa menjadi latar belakang yang sama di seluruh bagian ini.


Juga di antara persyaratan untuk mesin adalah kemampuan untuk memutar audio dan menampilkan pesan (dengan fungsi input pengguna opsional).


Mungkin keinginan yang paling penting adalah keinginan untuk menulis logika permainan bukan di Jawa, tetapi dalam beberapa bahasa deklaratif sederhana.


Ada juga keinginan untuk menyadari kemungkinan animasi prosedural, yaitu, gerakan gambar dasar, sehingga pada tingkat Jawa akan dimungkinkan untuk menentukan fungsi dimana kecepatan gerakan saat ini dipertimbangkan (misalnya, sehingga grafik kecepatannya langsung, atau sinusoid, atau yang lainnya).


Seperti yang direncanakan, semua interaksi pengguna harus dilakukan melalui sistem dialog. Dalam hal ini, dialog dianggap tidak harus berupa dialog dengan NPC atau sesuatu yang serupa, tetapi umumnya merupakan reaksi terhadap input pengguna mana pun yang telah didaftarkan pawang yang bersangkutan. Tidak jelas Ini akan menjadi lebih jelas segera.


2.2. Arsitektur dan implementasi


Dengan semua hal di atas, Anda dapat membagi mesin menjadi tiga bagian yang relatif besar yang sesuai dengan paket java yang sama:


  1. display - berisi segala sesuatu yang menyangkut output kepada pengguna informasi apa pun (grafik, teks dan suara), serta menerima input darinya. Semacam (Lihat), jika kita berbicara tentang MVC / MVP / dll.
  2. initializer - berisi kelas di mana mesin diinisialisasi dan diluncurkan.
  3. sl - berisi alat untuk bekerja dengan bahasa skrip (selanjutnya - SL).

Dalam paragraf ini saya akan mempertimbangkan dua bagian pertama. Saya akan mulai dengan yang kedua.


Kelas initializer memiliki dua metode utama: initialize() dan run() . Awalnya, kontrol datang ke kelas peluncur, dari tempat initialize() dipanggil. Setelah panggilan, penginisialisasi menganalisis parameter yang diteruskan ke program (jalur ke direktori dengan pencarian dan nama pencarian untuk dijalankan), memuat manifes pencarian yang dipilih (tentangnya beberapa saat kemudian), menginisialisasi tampilan, memeriksa apakah versi bahasa (SL) yang diperlukan oleh pencarian didukung oleh data penerjemah, dan akhirnya, meluncurkan utas terpisah untuk konsol pengembang.


Segera setelah itu, jika semuanya berjalan lancar, peluncur memanggil metode run() , yang menggerakkan pemuatan pencarian yang sebenarnya. Pertama, ada semua skrip yang terkait dengan pencarian yang diunduh (tentang struktur file pencarian - di bawah), mereka diumpankan ke penganalisa, yang hasilnya diberikan kepada penerjemah. Kemudian, inisialisasi semua adegan dimulai dan inisialisasi menyelesaikan pelaksanaan alirannya, akhirnya menggantung tombol Enter handler pada tampilan. Jadi, ketika pengguna menekan Enter, adegan pertama dimuat, tetapi lebih banyak tentang itu nanti.


Struktur file pencarian adalah sebagai berikut:


struktur file pencarian


Ada folder terpisah untuk pencarian, di mana root adalah manifes, serta tiga folder tambahan: audio - untuk suara, graphics - untuk bagian visual dan scenes - untuk skrip yang menggambarkan adegan.


Saya ingin menjelaskan secara singkat manifestonya. Ini berisi bidang-bidang berikut:


  • sl_version_req - Versi SL diperlukan untuk memulai pencarian,
  • init_scene - nama adegan dari mana pencarian dimulai,
  • quest_name - nama pencarian indah yang muncul di judul jendela,
  • resolution - resolusi layar yang tujuan pencariannya (beberapa kata tentang ini nanti),
  • font_size - ukuran font untuk semua teks,
  • font_name adalah nama font untuk semua teks.

Perlu dicatat bahwa selama inisialisasi tampilan, antara lain, perhitungan resolusi rendering dilakukan: yaitu, resolusi yang diperlukan diambil dari manifes dan diperas ke ruang yang tersedia untuk jendela sehingga:


  • rasio aspek tetap sama seperti dalam resolusi dari manifes,
  • semua ruang yang tersedia ditempati dengan lebar atau tinggi.
    Berkat ini, pengembang pencarian dapat yakin bahwa gambarnya, misalnya 16: 9, akan ditampilkan pada layar apa pun dalam rasio ini.

Juga, ketika tampilan diinisialisasi, kursor disembunyikan, karena tidak terlibat dalam gameplay.


Singkatnya tentang konsol pengembang. Ini dikembangkan karena alasan berikut:


  1. Untuk debug
  2. Jika terjadi kesalahan selama pertandingan, itu dapat diperbaiki melalui konsol pengembang.
    Ini hanya menerapkan beberapa perintah, yaitu: keluaran deskriptor dari jenis tertentu dan statusnya, keluaran utas bekerja, restart layar, dan perintah - exec yang paling penting, yang memungkinkan mengeksekusi kode SL dalam adegan saat ini.

Ini mengakhiri deskripsi penginisialisasi dan hal-hal terkait, dan kita dapat melanjutkan ke deskripsi tampilan.


Struktur akhirnya adalah sebagai berikut:


struktur tampilan


Dari pernyataan masalah, kita dapat menyimpulkan bahwa semua yang harus dilakukan adalah menggambar, menggambar teks, memutar audio.


Bagaimana teks / gambar biasanya digambar dalam mesin universal dan seterusnya? Ada metode tipe update() , yang disebut setiap centang / langkah / bingkai / render / bingkai / dll dan di mana ada panggilan ke metode tipe drawText() / drawImage() - ini memastikan penampilan teks / gambar dalam bingkai ini. Namun, begitu panggilan ke metode tersebut berhenti, rendering dari hal-hal yang terkait berhenti.


Dalam kasus saya, diputuskan untuk melakukan sedikit berbeda. Karena untuk novel visual, teks dan gambar relatif permanen, dan juga hampir semua yang dilihat pengguna (yaitu, mereka cukup penting), mereka dibuat sebagai objek game - yaitu, hal-hal yang hanya perlu Anda spawn dan tidak akan hilang sampai Anda bertanya kepada mereka. Selain itu, solusi ini menyederhanakan implementasi.


Objek (dari sudut pandang OOP) yang menggambarkan teks / gambar disebut descriptor. Artinya, untuk pengguna mesin API hanya ada deskriptor yang dapat ditambahkan ke kondisi tampilan dan dihapus dari itu. Dengan demikian, dalam versi final tampilan ada deskriptor berikut (mereka sesuai dengan kelas dengan nama yang sama):


Nama Deskriptor:Deskripsi Penjelasan:
ImageDescriptorDeskriptor gambar: berisi gambar ( BufferedImage ), posisi di layar dan lebar dengan tinggi. Namun, saat membuat, hanya lebar yang ditentukan - tinggi dihitung sebanding dengan tinggi aslinya (dan tidak ada cara untuk meregangkan / mengompres gambar secara tidak proporsional, jadi ini adalah keputusan yang buruk).
TextDescriptorDeskriptor teks: berisi teks, posisi dan ukurannya. Selain itu, teks tidak memiliki skala lebar dan tinggi, tetapi terpotong saat melewati batas. Teks dapat ditransfer oleh suku kata dan hanya dengan karakter spasi, dan juga gulir dengan garis.
AudioDescriptorDeskriptor audio yang dapat memutar audio (baik sekali atau loop), jeda dan hapus.
AnimationDescriptorPegangan animasi yang, seperti audio, dapat dilingkarkan. Berisi objek yang mengimplementasikan animasi, dan deskriptor gambar tempat animasi dimainkan. Metode utama adalah update(long) , yang mengambil jumlah milidetik yang telah berlalu sejak panggilan update(long) terakhir update(long) . Jumlah mereka digunakan untuk menghitung keadaan animasi saat ini.
InputDescriptorDeskripsi deskriptor: adalah bidang teks di mana kursor selalu berada di akhir teks. Perlu juga dicatat bahwa penyimpanan dan rendering teks dari deskriptor input dilakukan melalui deskriptor teks yang dibuat secara implisit, agar tidak menggandakan logika. Lucunya, saya memperhitungkan kemungkinan menekan Backspace, tetapi tidak memperhitungkan Hapus; dan ketika Hapus masih ditekan selama permainan, β–―β–―β–― muncul di lapangan, karena tidak ada pemrosesan yang dilakukan untuk Hapus dan karakter mencoba ditampilkan sebagai teks.
KeyAwaitDescriptorPegangan yang dilewati tombol yang diperlukan (dari KeyEvent ) dan panggilan balik dengan beberapa logika yang akan diluncurkan ketika tombol yang sesuai ditekan.
PostWorkDescriptorPegangan yang menerima panggilan balik yang akan dipanggil setelah memproses setiap centang.

Layar juga berisi bidang untuk penerima input saat ini (deskriptor input) dan bidang yang menunjukkan deskriptor teks mana yang sekarang memiliki fokus dan yang teksnya akan bergulir di bawah tindakan yang sesuai pada bagian pengguna.


Siklus permainan terlihat seperti ini:


  1. Pemrosesan audio - memanggil metode update() pada deskriptor audio, yang memeriksa keadaan audio saat ini, membebaskan memori (jika perlu), dan melakukan pekerjaan teknis lainnya.
  2. Memproses penekanan tombol - mentransfer karakter yang dimasukkan ke deskriptor untuk menerima input, memproses penekanan tombol untuk tombol gulir (panah atas dan bawah) dan Backspace.
  3. Pemrosesan Animasi.
  4. Menghapus latar belakang di buffer rendering ( BufferedImage berfungsi sebagai buffer).
  5. Menggambar gambar.
  6. Rendering teks.
  7. Bidang gambar untuk input.
  8. Output dari buffer ke layar.
  9. Menangani PostWorkDescriptor .
  10. Beberapa bekerja untuk mengganti status tampilan, yang akan saya bahas nanti (di bagian interpreter SL).
  11. Hentikan aliran untuk waktu yang dihitung secara dinamis sehingga FPS sama dengan nilai yang ditentukan (30 secara default).

Catatan: Mungkin muncul pertanyaan, "Mengapa memberikan bidang input jika deskriptor teks yang sesuai telah dibuat untuk mereka yang akan diberikan langkah lebih awal?" Bahkan, rendering dalam paragraf 7 tidak terjadi - hanya parameter InputDescriptor yang disinkronkan dengan parameter InputDescriptor - seperti visibilitas layar, posisi, ukuran, dan lainnya. Ini dilakukan, seperti ditunjukkan di atas, untuk alasan bahwa pengguna tidak secara langsung mengontrol deskriptor input yang sesuai dengan deskriptor teks dan umumnya tidak tahu apa-apa tentang itu.


Perlu dicatat bahwa ukuran dan posisi elemen pada layar tidak diatur dalam piksel, tetapi dalam ukuran relatif - angka dari 0 hingga 1 (diagram di bawah). Yaitu, seluruh lebar untuk rendering adalah 1, dan keseluruhan tinggi adalah 1 (dan mereka tidak sama, yang saya lupa beberapa kali dan kemudian menyesal). Itu juga akan bermanfaat untuk menjadikan (0,0) menjadi pusat, dan lebar / tinggi harus sama dengan dua, tetapi untuk beberapa alasan saya lupa / tidak memikirkannya. Namun, bahkan opsi dengan lebar / tinggi sama dengan 1 menyederhanakan kehidupan pengembang pencarian.


sistem koordinat relatif


Beberapa kata tentang sistem untuk membebaskan memori.


Setiap deskriptor memiliki metode setDoFree(boolean) , yang harus dihubungi pengguna jika ia ingin menghancurkan deskriptor yang diberikan. Pengumpulan sampah untuk deskriptor dari beberapa tipe terjadi segera setelah memproses semua deskriptor dari tipe ini. Juga, audio yang diputar sekali dihapus secara otomatis setelah pemutaran berakhir. Sama persis dengan animasi non-looping.


Jadi, saat ini Anda dapat menggambar apa pun yang Anda inginkan, tetapi ini bukan gambar di atas, yang hanya memiliki latar belakang, teks utama dan bidang input. Dan inilah pembungkus di atas layar, yang sesuai dengan kelas DefaultDisplayToolkit .


Ketika diinisialisasi, itu hanya menambahkan deskriptor untuk latar belakang, teks, dll ke layar.Ia juga tahu bagaimana menampilkan pesan dengan ikon opsional, bidang input dan panggilan balik.


Kemudian bug kecil muncul, sebuah koreksi penuh yang membutuhkan mengulangi setengah dari sistem rendering: jika Anda melihat urutan rendering dalam loop game, Anda dapat melihat bahwa gambar diambil terlebih dahulu dan hanya kemudian teks. Pada saat yang sama, ketika toolkit menunjukkan gambar, itu menempatkannya di tengah layar lebar dan tinggi . Dan jika ada banyak teks dalam pesan, maka sebagian teks harus tumpang tindih dengan adegan. Namun, karena latar belakang pesan adalah gambar (benar-benar hitam, tapi tetap saja), dan gambar digambar sebelum teks, satu teks dilapiskan pada teks lainnya (tangkapan layar di bawah). Masalahnya sebagian diselesaikan dengan pemusatan vertikal bukan pada layar, tetapi di area di atas teks utama. Solusi lengkap akan mencakup memperkenalkan parameter kedalaman dan mengulang renderers dari kata "sepenuhnya".


Demonstrasi hamparan

teks tumpang tindih


Mungkin ini tentang tampilan, akhirnya, semuanya. Anda dapat beralih ke bahasa, seluruh API untuk bekerja dengan yang terkandung dalam paket sl .


3. Bahasa scripting


Catatan: Jika% USERNAME% yang dihormati membacanya di sini, dia melakukannya dengan baik, dan saya akan memintanya untuk tidak berhenti melakukannya: sekarang ini akan jauh lebih menarik daripada sebelumnya.


3.1. Bahasa


Awalnya, saya ingin membuat bahasa deklaratif di mana hanya perlu menunjukkan semua parameter yang diperlukan untuk adegan itu, dan itu saja. Mesin akan mengambil semua logika. Namun, pada akhirnya, saya sampai pada bahasa prosedural, bahkan dengan elemen OOP (hampir tidak dapat dibedakan), dan ini adalah solusi yang baik, karena, dibandingkan dengan versi deklaratif, itu memberi peluang lebih banyak fleksibilitas dalam logika game.


Sintaks bahasa dipikirkan agar sesederhana mungkin untuk parsing, yang logis, mengingat jumlah waktu yang tersedia.


Jadi, kode tersebut disimpan dalam file teks dengan ekstensi SSF; setiap file berisi deskripsi satu atau lebih adegan; setiap adegan berisi nol atau lebih aksi; setiap tindakan berisi nol atau lebih operator.


Sedikit penjelasan tentang persyaratan. Suatu tindakan hanyalah sebuah prosedur tanpa kemungkinan lewat argumen (sama sekali tidak mencegah perkembangan game). Operator tampaknya tidak cukup apa arti kata ini dalam bahasa biasa (+, -, /, *), tetapi bentuknya sama: operator adalah totalitas nama dan semua argumennya.


Mungkin Anda ingin akhirnya melihat kode sumber untuk SL, ini dia:


 scene dungeon { action init { load_image "background" "dungeon/background.png" load_image "key" "dungeon/key.png" load_audio "background" "dungeon/background.wav" load_audio "got_key" "dungeon/got_key.wav" } action first_come { play "background" loop set_background "background" set_text "some text" add_dialog "(||(|) (||-))" "dial_look_around" dial_look_around on } //some comment action dial_look_around { play "got_key" once show "some text 2" "key" none tag "key" switch_dialog "dial_look_around" off } } 

Sekarang menjadi jelas apa operatornya. Terlihat juga bahwa setiap tindakan adalah blok pernyataan (pernyataan dapat berupa blok pernyataan), dan juga fakta bahwa komentar satu baris didukung (tidak masuk akal untuk memasukkan yang multi-baris, selain itu, saya tidak menggunakan yang satu-baris).


Demi penyederhanaan, konsep seperti "variabel" tidak diperkenalkan ke dalam bahasa; sebagai hasilnya, semua nilai yang digunakan dalam kode adalah literal. Tergantung pada jenisnya, literal berikut dibedakan:


Nama Literal:Catatan:
String literalKemampuan untuk menghindari tanda kutip dan garis miring (\\) disertakan, juga dimungkinkan untuk memasukkan tanda hubung dalam teks
Integer literalMendukung angka negatif
Floating point literalMendukung angka negatif
Tidak ada yang harfiahKode diwakili sebagai none
Boolean literalDalam kode - on / off untuk benar / salah, masing-masing
Literal umumJika literal tidak termasuk salah satu dari tipe di atas dan terdiri dari huruf-huruf alfabet, angka, dan garis bawah bahasa Inggris, itu adalah literal umum.

Beberapa kata tentang penguraian bahasa. Ada beberapa level "memuat" kode (diagram di bawah):


  1. Tokenizer adalah kelas modular untuk memecah kode sumber menjadi token (unit semantik minimum bahasa). Setiap jenis token dikaitkan dengan nomor - jenisnya. Mengapa modular? Karena bagian-bagian dari tokenizer yang memeriksa apakah ada bagian dari kode sumber yang merupakan token dari jenis tertentu diisolasi dari tokenizer dan diunduh dari luar (dari paragraf kedua).
  2. Add-on tokenizer adalah kelas yang mendefinisikan penampilan setiap jenis token di SL; di tingkat bawah menggunakan tokenizer. Juga di sini adalah penyaringan token ruang angkasa dan penghilangan komentar single-line. Outputnya memberikan aliran token yang bersih, yang digunakan ...
  3. ... parser (itu juga modular), yang menghasilkan pohon sintaksis abstrak pada output. Modular - karena dengan sendirinya ia hanya dapat mengurai adegan dan tindakan, tetapi tidak tahu bagaimana menganalisis operator. Oleh karena itu, modul dimuat ke dalamnya (pada kenyataannya, dia sendiri memuatnya dalam konstruktor, yang tidak terlalu baik), yang dapat mengurai masing-masing operatornya.

skrip pipa bahasa penguraian


Sekarang, secara singkat tentang operator, sehingga gagasan fungsionalitas bahasa muncul. Awalnya, ada 11 operator, kemudian dalam proses berpikir melalui permainan, beberapa dari mereka bergabung menjadi satu, beberapa berubah, dan 9 lainnya ditambahkan. Ini tabel ringkasannya:


Nama Operator:Catatan:
load_imageMemuat gambar ke dalam memori
load_audioMemuat audio ke dalam memori
set_textMengatur teks utama adegan.
set_backgroundAtur latar belakang untuk adegan itu.
playMemutar audio (dapat memutar satu kali atau mengulang pemutaran)
showMemperlihatkan pesan dengan callback (dalam SL) dan gambar opsional (dipanggil setelah pengguna menutup pesan)
tagMenetapkan tag (label). Itu dapat dianggap sebagai variabel global dengan nama yang ditentukan, yang tidak menyimpan nilai apa pun. Ini berguna dalam kasus yang berbeda: misalnya, Anda dapat menandai sedemikian rupa apakah pemain menemukan kunci pintu, apakah ia sudah berada di lokasi ini, dll.
if_tag / if_tag_nOperator percabangan yang memungkinkan Anda untuk melakukan sesuatu tergantung pada apakah tag yang sesuai dipasang. cabang else didukung. if_tag dieksekusi jika tag diatur, if_tag_n - dan sebaliknya
add_dialogMemungkinkan Anda menambahkan dialog ke adegan. Tentang mereka beberapa saat kemudian
gotoTransisi ke adegan lain
callPanggilan tindakan khusus
call_externMeminta tindakan dari adegan lain.
stop_allBerhenti memainkan semua suara / musik
show_motionMemperlihatkan dan memindahkan gambar dari satu titik ke titik lainnya dalam waktu (durasi) yang ditentukan (dengan panggilan balik opsional yang dipanggil saat gerakan berakhir)
animateAnimasi gambar yang sebelumnya ditampilkan melalui show_motion . Dari opsi: Anda dapat menentukan jenis animasi - v_motion / h_motion (gerakan vertikal / horizontal berdasarkan fungsi 2t3βˆ’3t2), kemungkinan pengulangan, indikasi waktu (durasi) dimana animasi harus dimainkan. Dimungkinkan untuk mengirimkan satu nilai numerik (satu, karena tidak ada cara untuk meneruskan sejumlah variabel argumen, jadi ini sebagian adalah penopang) ke animasi (untuk setiap animasi itu berarti hal yang berbeda) dan panggilan balik opsional (dipanggil saat animasi dimainkan).

Operator untuk bekerja dengan penghitung - variabel integer khusus adegan.


Nama Operator:Catatan:
counter_setMembuat penghitung dan menginisialisasi dengan beberapa nilai
counter_addMenambahkan nilai ke penghitung
if_counter <modifier>Mampu membandingkan nilai dua penghitung / angka / angka dan penghitung. <modifier> adalah literal umum dan memiliki bentuk eq/gr/ls[_n] , di mana eq sama, gr lebih besar dari, ls lebih kecil dari, ls lebih kecil, _n adalah negasi (misalnya, gr_n tidak lebih besar). Seperti yang Anda lihat, semuanya disederhanakan sebanyak mungkin.

Ada juga pemikiran memperkenalkan return (fungsionalitas yang sesuai bahkan ditambahkan pada tingkat inti penerjemah), tetapi saya lupa, dan itu tidak berguna.


, , : show_motion (, , 0.01) duration .


, (lookup) ( ): ///, load_audio / load_image / counter_set / add_dialog . , , , , β€” . . , . , : " scene_coast.dialog_1 " β€” dialog_1 scene_coast .


SL-, . , , , β€” . : (-, ), , lookup ', , , . , goto lookup ', .


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


. :


 add_dialog "regexp" "dialog_name" callback on/off 

, . , : , , , ( ).


, , ( ) ( ) , ( ). : , , , "" "".


kerja sistem dialog


, ( , )


, "":


 (||((|||) ( )?(||)?)|(||)( )?|  ) 

***


, : β€” , , β€” .


, :


3.2.


: , β€” "" ( ). .


SL , - . :


  1. init β€” , ( , , , ).
  2. first_come β€” , . , , .
  3. , : come β€” , ( ).

: init first_come β€” , .


. : , , init -. , ( ) .


, n , first_come - ( - - ). . , : , , first_come come , come ( ). : , , , .


(, "", " ", " " . .). , , - - . , ( ), .


(, , ). : ? , , . provideState , ; , .


, , , , ( , ), (, , , ).


4.


. 2019- 2018-, , , .


4.1.


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


, : , , , . , , .


, 25% (5) , : , ; ( animate ), ( call_extern ).


, - ( ), (, , β€” , "You won").


novel visual demo


4.2. Grafik


, :


Rintisan untuk grafis


, , - - " ". :


  • (4x2.23''), .
  • : , , β€” .
  • ////etc.

penggunaan kuas seni


  • , , .

karakter dari novel visual


  • , . , , , . .

5.


( 11 ) 30 40 . 9 4 55 . ( ) 7 41 . β€” ~4-6 ( 45 ).


: "Darkyen's Time Tracker" JetBrains ( ).
: 2 , β€” . 45 8 .


: 4777, ( ) β€” 637.


: cloc .


30 . ( ) : β€” ~8 , β€” ~24 , ( ) β€” ~8 . .


β€” 232 ( - , WAV).


WAV?

javax.sound.sampled.AudioSystem , WAV AU , WAV.


28 ( 3 ). β€” 17 /.


- : , . , , " ", " ". (, ), ( ""/"" - ).


?

β€” , . : . . , , "" : NPC, , (, β€” ..).


, : , .


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


. ( ), :


  • . . .
  • . , .
  • , .

, , .


GitHub .


(assets) "Releases" "v1.0" .

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


All Articles