Daftar Isi:

Ekstensi Scratch 3.0: 8 Langkah
Ekstensi Scratch 3.0: 8 Langkah

Video: Ekstensi Scratch 3.0: 8 Langkah

Video: Ekstensi Scratch 3.0: 8 Langkah
Video: MEMBUAT GAMES SEDERHANA DI SCRATCH 3.0 2024, Juli
Anonim
Ekstensi Scratch 3.0
Ekstensi Scratch 3.0

Ekstensi Scratch adalah potongan kode Javascript yang menambahkan blok baru ke Scratch. Sementara Scratch dibundel dengan banyak ekstensi resmi, tidak ada mekanisme resmi untuk menambahkan ekstensi buatan pengguna.

Ketika saya membuat ekstensi pengontrol Minecraft untuk Scratch 3.0, saya merasa sulit untuk memulai. Instruksi ini mengumpulkan informasi dari berbagai sumber (terutama ini), ditambah beberapa hal yang saya temukan sendiri.

Anda perlu tahu cara memprogram dalam Javascript dan cara meng-host Javascript Anda di situs web. Untuk yang terakhir, saya merekomendasikan Halaman GitHub.

Trik utamanya adalah menggunakan mod SheepTester dari Scratch yang memungkinkan Anda memuat ekstensi dan plugin.

Instruksi ini akan memandu Anda membuat dua ekstensi:

  • Ambil: memuat data dari URL dan mengekstrak tag JSON, misalnya untuk memuat data cuaca
  • SimpleGamepad: menggunakan pengontrol game di Scratch (versi yang lebih canggih ada di sini).

Langkah 1: Dua Jenis Ekstensi

Ada dua jenis ekstensi yang akan saya sebut "unsandboxed" dan "sandboxed". Ekstensi kotak pasir berjalan sebagai Pekerja Web, dan akibatnya memiliki batasan yang signifikan:

  • Pekerja Web tidak dapat mengakses objek global di jendela (sebagai gantinya, mereka memiliki objek mandiri global, yang jauh lebih terbatas), jadi Anda tidak dapat menggunakannya untuk hal-hal seperti akses gamepad.
  • Ekstensi kotak pasir tidak memiliki akses ke objek runtime Scratch.
  • Ekstensi kotak pasir jauh lebih lambat.
  • Pesan kesalahan konsol Javascript untuk ekstensi kotak pasir lebih samar di Chrome.

Di samping itu:

  • Menggunakan ekstensi kotak pasir orang lain lebih aman.
  • Ekstensi kotak pasir lebih cenderung berfungsi dengan dukungan pemuatan ekstensi resmi apa pun.
  • Ekstensi kotak pasir dapat diuji tanpa mengunggah ke server web dengan menyandikan ke dalam data:// URL.

Ekstensi resmi (seperti Musik, Pena, dll.) semuanya tidak dikotakpasirkan. Konstruktor untuk ekstensi mendapatkan objek runtime dari Scratch, dan jendela dapat diakses sepenuhnya.

Ekstensi Fetch dikotak pasir, tetapi Gamepad membutuhkan objek navigator dari jendela.

Langkah 2: Menulis Ekstensi Kotak Pasir: Bagian I

Untuk membuat ekstensi, Anda membuat kelas yang mengkodekan informasi tentangnya, lalu menambahkan sedikit kode untuk mendaftarkan ekstensi.

Hal utama di kelas ekstensi adalah metode getInfo() yang mengembalikan objek dengan bidang yang diperlukan:

  • id: nama internal ekstensi, harus unik untuk setiap ekstensi
  • name: nama ekstensi yang ramah, muncul di daftar blok Scratch
  • blok: daftar objek yang menjelaskan blok kustom baru.

Dan ada bidang menu opsional yang tidak digunakan di Fetch tetapi akan digunakan di Gamepad.

Jadi, inilah template dasar untuk Fetch:

kelas ScratchFetch {

constructor() { } getInfo() { return { "id": "Ambil", "nama": "Ambil", "blok": [/* tambahkan nanti */] } } /* tambahkan metode untuk blok */ } Scratch.extensions.register (ScratchFetch baru ())

Langkah 3: Menulis Ekstensi Kotak Pasir: Bagian II

Sekarang, kita perlu membuat daftar blok di objek getInfo(). Setiap blok membutuhkan setidaknya empat bidang ini:

  • opcode: ini adalah nama metode yang dipanggil untuk melakukan pekerjaan blok
  • blockType: ini adalah tipe blok; yang paling umum untuk ekstensi adalah:

    • "command": melakukan sesuatu tetapi tidak mengembalikan nilai
    • "reporter": mengembalikan string atau angka
    • "Boolean": mengembalikan boolean (perhatikan kapitalisasi)
    • "hat": blok penangkapan acara; jika kode Scratch Anda menggunakan blok ini, runtime Scratch secara teratur melakukan polling metode terkait yang mengembalikan boolean untuk mengatakan apakah peristiwa tersebut telah terjadi
  • teks: ini adalah deskripsi ramah dari blok, dengan argumen dalam tanda kurung, misalnya, "ambil data dari "
  • argumen: ini adalah objek yang memiliki bidang untuk setiap argumen (mis., "url" dalam contoh di atas); objek ini pada gilirannya memiliki bidang ini:

    • ketik: baik "string" atau "angka"
    • defaultValue: nilai default yang akan diisi sebelumnya.

Misalnya, berikut adalah bidang blok di ekstensi Ambil saya:

"blok": [{ "opcode": "fetchURL", "blockType": "reporter", "text": "mengambil data dari ", "argumen": { "url": { "type": "string", "defaultValue ": "https://api.weather.gov/stations/KNYC/observations" }, } }, { "opcode": "jsonExtract", "blockType": "reporter", "text": "extract [nama] dari [data]", "argumen": { "name": { "type": "string", "defaultValue": "temperature" }, "data": { "type": "string", "defaultValue": '{"suhu": 12,3}' }, } },]

Di sini, kami mendefinisikan dua blok: fetchURL dan jsonExtract. Keduanya wartawan. Yang pertama mengambil data dari URL dan mengembalikannya, dan yang kedua mengekstrak bidang dari data JSON.

Terakhir, Anda perlu menyertakan metode untuk dua blok. Setiap metode mengambil objek sebagai argumen, dengan objek termasuk bidang untuk semua argumen. Anda dapat memecahkan kode ini menggunakan kurung kurawal dalam argumen. Misalnya, berikut adalah satu contoh sinkron:

jsonExtract({nama, data}) {

var parsed = JSON.parse(data) if (nama di parsing) { var out = parsed[name] var t = typeof(out) if (t == "string" || t == "number") return out if (t == "boolean") kembali t ? 1: 0 return JSON.stringify(out) } else { return "" } }

Kode menarik bidang nama dari data JSON. Jika bidang berisi string, angka, atau boolean, kami mengembalikannya. Jika tidak, kami melakukan JSONify ulang bidang tersebut. Dan kami mengembalikan string kosong jika namanya hilang dari JSON.

Namun, terkadang Anda mungkin ingin membuat blok yang menggunakan API asinkron. Metode fetchURL() menggunakan API fetch yang asinkron. Dalam kasus seperti itu, Anda harus mengembalikan janji dari metode Anda yang berhasil. Contohnya:

fetchURL({url}) {

kembalikan fetch(url).then(respons => response.text()) }

Itu dia. Ekstensi lengkapnya ada di sini.

Langkah 4: Menggunakan Ekstensi Kotak Pasir

Menggunakan Ekstensi Kotak Pasir
Menggunakan Ekstensi Kotak Pasir
Menggunakan Ekstensi Kotak Pasir
Menggunakan Ekstensi Kotak Pasir
Menggunakan Ekstensi Kotak Pasir
Menggunakan Ekstensi Kotak Pasir

Ada dua cara menggunakan ekstensi kotak pasir. Pertama, Anda dapat mengunggahnya ke server web, lalu memuatnya ke mod Scratch SheepTester. Kedua, Anda dapat menyandikannya ke dalam URL data, dan memuatnya ke mod Scratch. Saya sebenarnya menggunakan metode kedua sedikit untuk pengujian, karena menghindari kekhawatiran tentang versi ekstensi yang lebih lama di-cache oleh server. Perhatikan bahwa meskipun Anda dapat meng-host javascript dari Halaman Github, Anda tidak dapat melakukannya langsung dari repositori github biasa.

Fetch.js saya di-host di https://arpruss.github.io/fetch.js. Atau Anda dapat mengonversi ekstensi Anda menjadi URL data dengan mengunggahnya di sini, lalu menyalinnya ke papan klip. URL data adalah URL raksasa yang menyimpan seluruh file di dalamnya.

Buka mod Scratch SheepTester. Klik tombol Tambah Ekstensi di sudut kiri bawah. Kemudian klik "Pilih ekstensi", dan masukkan URL Anda (Anda dapat menempelkan seluruh URL data raksasa jika Anda mau).

Jika semuanya berjalan dengan baik, Anda akan memiliki entri untuk ekstensi Anda di sisi kiri layar Scratch Anda. Jika semuanya tidak berjalan dengan baik, Anda harus membuka konsol Javascript Anda (shift-ctrl-J di Chrome) dan mencoba men-debug masalah tersebut.

Di atas Anda akan menemukan beberapa contoh kode yang mengambil dan mem-parsing data JSON dari stasiun KNYC (di New York) dari US National Weather Service, dan menampilkannya, sambil memutar sprite menghadap ke arah yang sama seperti angin bertiup. Cara saya membuatnya adalah dengan mengambil data ke browser web, dan kemudian mencari tahu tag. Jika Anda ingin mencoba stasiun cuaca yang berbeda, masukkan kode pos terdekat ke dalam kotak pencarian di weather.gov, dan halaman cuaca untuk lokasi Anda akan memberikan kode stasiun empat huruf, yang dapat Anda gunakan sebagai pengganti KNYC di kode.

Anda juga dapat menyertakan ekstensi kotak pasir Anda tepat di URL untuk mod SheepTester dengan menambahkan argumen "?url=". Contohnya:

sheeptester.github.io/scratch-gui/?url=https://arpruss.github.io/fetch.js

Langkah 5: Menulis Ekstensi Tanpa Kotak Pasir: Pendahuluan

Konstruktor ekstensi yang tidak dikotakpasirkan melewati objek Runtime. Anda dapat mengabaikannya atau menggunakannya. Salah satu penggunaan objek Runtime adalah dengan menggunakan properti currentMSecs untuk menyinkronkan peristiwa ("hat blok"). Sejauh yang saya tahu, semua opcode blok acara disurvei secara teratur, dan setiap putaran polling memiliki nilai currentMSecs tunggal. Jika Anda membutuhkan objek Runtime, Anda mungkin akan memulai ekstensi Anda dengan:

kelas EXTENSIONCLASS {

konstruktor(waktu proses) { this.runtime = waktu proses … } … }

Semua objek jendela standar dapat digunakan dalam ekstensi yang tidak dikotakpasirkan. Terakhir, ekstensi yang tidak dikotakpasirkan harus diakhiri dengan sedikit kode ajaib ini:

(fungsi() {

var extensionInstance = new EXTENSIONCLASS(window.vm.extensionManager.runtime) var serviceName = window.vm.extensionManager._registerInternalExtension(extensionInstance) window.vm.extensionManager._loadedExtensions.set(extensionInstance.getInfo()})(serviceName))

di mana Anda harus mengganti EXTENSIONCLASS dengan kelas ekstensi Anda.

Langkah 6: Menulis Ekstensi Tanpa Kotak Pasir: Gamepad Sederhana

Sekarang mari kita membuat ekstensi gamepad sederhana yang menyediakan satu blok peristiwa ("topi") ketika sebuah tombol ditekan atau dilepaskan.

Selama setiap siklus polling blok acara, kami akan menyimpan stempel waktu dari objek runtime, dan status gamepad sebelumnya dan saat ini. Stempel waktu digunakan untuk mengenali jika kita memiliki siklus polling baru. Jadi, kita mulai dengan:

class ScratchSimpleGamepad {

constructor(runtime) { this.runtime = runtime this.currentMSecs = -1 this.previousButtons = this.currentButtons = } … } Kami akan memiliki satu blok acara, dengan dua input--nomor tombol dan menu untuk memilih apakah kami ingin acara dipicu saat pers atau rilis. Jadi, inilah metode kami

Mendapatkan informasi() {

return { "id": "SimpleGamepad", "name": "SimpleGamepad", "blocks": [{ "opcode": "buttonPressedReleased", "blockType": "hat", "text": "button [eventType]", "argumen": { "b": { "type": "number", "defaultValue": "0" }, "eventType": { "type": "number", "defaultValue": "1 ", "menu": "pressReleaseMenu" }, }, },], "menus": { "pressReleaseMenu": [{text:"press", value:1}, {text:"release", value:0}], } }; } Saya pikir nilai-nilai di menu drop-down masih diteruskan ke fungsi opcode sebagai string, meskipun dinyatakan sebagai angka. Jadi, bandingkan secara eksplisit dengan nilai yang ditentukan dalam menu sesuai kebutuhan. Kami sekarang menulis metode yang memperbarui status tombol setiap kali siklus polling acara baru terjadi

memperbarui() {

if (this.runtime.currentMSecs == this.currentMSecs) return // bukan siklus polling baru this.currentMSecs = this.runtime.currentMSecs var gamepads = navigator.getGamepads() if (gamepads == null || gamepads.length = = 0 || gamepads[0] == null) { this.previousButtons = this.currentButtons = return } var gamepad = gamepads[0] if (gamepad.buttons.length != this.previousButtons.length) { // jumlah tombol yang berbeda, jadi gamepad baru this.previousButtons = for (var i = 0; i < gamepad.buttons.length; i++) this.previousButtons.push(false) } else { this.previousButtons = this. currentButtons } this.currentButtons = for (var i = 0; i < gamepad.buttons.length; i++) this.currentButtons.push(gamepad.buttons.pressed) } Terakhir, kita dapat mengimplementasikan blok peristiwa kita, dengan memanggil metode update() dan kemudian memeriksa apakah tombol yang diperlukan baru saja ditekan atau dilepaskan, dengan membandingkan status tombol saat ini dan sebelumnya

buttonPressedReleased({b, eventType}) {

this.update() if (b < this.currentButtons.length) { if (eventType == 1) { // catatan: ini akan menjadi string, jadi lebih baik membandingkannya dengan 1 daripada memperlakukannya sebagai Boolean if (this.currentButtons && ! this.previousButtons) { return true } } else { if (!this.currentButtons && this.previousButtons) { return true } } } return false } Dan akhirnya kami menambahkan kode pendaftaran ekstensi ajaib kami setelah mendefinisikan kelas

(fungsi() {

var extensionInstance = new ScratchSimpleGamepad(window.vm.extensionManager.runtime) var serviceName = window.vm.extensionManager._registerInternalExtension(extensionInstance) window.vm.extensionManager._loadedExtensions.set(extensionInstance.getInfo()})(namalayanan))

Anda bisa mendapatkan kode lengkapnya di sini.

Langkah 7: Menggunakan Ekstensi Tanpa Kotak Pasir

Menggunakan Ekstensi Tanpa Kotak Pasir
Menggunakan Ekstensi Tanpa Kotak Pasir

Sekali lagi, host ekstensi Anda di suatu tempat, dan kali ini muat dengan argumen load_plugin= daripada url= ke mod Scratch SheepTester. Misalnya, untuk mod Gamepad sederhana saya, buka:

sheeptester.github.io/scratch-gui/?load_plugin=https://arpruss.github.io/simplegamepad.js

(Omong-omong, jika Anda menginginkan gamepad yang lebih canggih, hapus saja "sederhana" dari URL di atas, dan Anda akan memiliki dukungan gemuruh dan sumbu analog.)

Sekali lagi, ekstensi akan muncul di sisi kiri editor Scratch Anda. Di atas adalah program Scratch yang sangat sederhana yang mengatakan "halo" ketika Anda menekan tombol 0 dan "selamat tinggal" ketika Anda melepaskannya.

Langkah 8: Kompatibilitas ganda dan Kecepatan

Saya perhatikan bahwa blok ekstensi menjalankan urutan besarnya lebih cepat menggunakan metode pemuatan yang saya gunakan untuk ekstensi yang tidak dikotakpasirkan. Jadi, kecuali Anda peduli dengan manfaat keamanan dari menjalankan di kotak pasir Web Worker, kode Anda akan mendapat manfaat dari dimuat dengan argumen ?load_plugin=URL ke mod SheepTester.

Anda dapat membuat ekstensi kotak pasir kompatibel dengan kedua metode pemuatan dengan menggunakan kode berikut setelah menentukan kelas ekstensi (ubah CLASSNAME menjadi nama kelas ekstensi Anda):

(fungsi() {

var extensionClass = CLASSNAME if (typeof window === "undefined" || !window.vm) { Scratch.extensions.register(new extensionClass()) } else { var extensionInstance = new extensionClass(window.vm.extensionManager.runtime) var serviceName = window.vm.extensionManager._registerInternalExtension(extensionInstance) window.vm.extensionManager._loadedExtensions.set(extensionInstance.getInfo().id, serviceName) } })()

Direkomendasikan: