İçindekiler:

Scratch 3.0 Uzantıları: 8 Adım
Scratch 3.0 Uzantıları: 8 Adım

Video: Scratch 3.0 Uzantıları: 8 Adım

Video: Scratch 3.0 Uzantıları: 8 Adım
Video: Kodlama Eğitimi -8 | Scratch Dersleri | Kodlama Nasıl Yapılır? 2024, Temmuz
Anonim
Scratch 3.0 Uzantıları
Scratch 3.0 Uzantıları

Scratch uzantıları, Scratch'e yeni bloklar ekleyen Javascript kodu parçalarıdır. Scratch bir dizi resmi uzantıyla birlikte gelirken, kullanıcı yapımı uzantıları eklemek için resmi bir mekanizma yoktur.

Scratch 3.0 için Minecraft kontrol uzantımı yaparken, başlamayı zor buldum. Bu Eğitilebilir Kitap, çeşitli kaynaklardan (özellikle bu) bilgileri ve ayrıca kendim keşfettiğim birkaç şeyi bir araya toplar.

Javascript'te nasıl programlanacağını ve Javascript'inizi bir web sitesinde nasıl barındıracağınızı bilmeniz gerekir. İkincisi için GitHub Pages'i öneririm.

İşin püf noktası, SheepTester'ın uzantıları ve eklentileri yüklemenize izin veren Scratch modunu kullanmaktır.

Bu Eğitilebilir Tablo, iki uzantı oluşturma konusunda size rehberlik edecektir:

  • Getirme: bir URL'den veri yükleme ve örneğin hava durumu verilerini yüklemek için JSON etiketlerini çıkarma
  • SimpleGamepad: Scratch'te bir oyun kumandası kullanma (daha gelişmiş bir sürüm burada).

Adım 1: İki Uzantı Türü

"Korumalı olmayan" ve "korumalı alan" olarak adlandıracağım iki tür uzantı vardır. Korumalı alan uzantıları Web Çalışanları olarak çalışır ve sonuç olarak önemli sınırlamalara sahiptir:

  • Web Çalışanları, pencere nesnesindeki küresellere erişemezler (bunun yerine, çok daha sınırlı bir küresel öz nesneye sahiptirler), bu nedenle bunları oyun kumandası erişimi gibi şeyler için kullanamazsınız.
  • Korumalı alan uzantılarının, Scratch çalışma zamanı nesnesine erişimi yoktur.
  • Korumalı alan uzantıları çok daha yavaştır.
  • Korumalı alan uzantıları için Javascript konsolu hata mesajları, Chrome'da daha şifrelidir.

Diğer yandan:

  • Diğer kişilerin korumalı alan uzantılarını kullanmak daha güvenlidir.
  • Korumalı alan uzantılarının, herhangi bir nihai resmi uzantı yükleme desteğiyle çalışması daha olasıdır.
  • Korumalı alan uzantıları, bir data:// URL'sine kodlanarak bir web sunucusuna yüklenmeden test edilebilir.

Resmi uzantıların (Müzik, Kalem vb.) tümü korumalı alanda değildir. Uzantının yapıcısı, çalışma zamanı nesnesini Scratch'ten alır ve pencereye tamamen erişilebilir.

Getirme uzantısı korumalı alana sahiptir, ancak Gamepad'in pencereden gezgin nesnesine ihtiyacı vardır.

2. Adım: Korumalı Alana Alınmış Uzantı Yazma: Bölüm I

Bir uzantı oluşturmak için, onunla ilgili bilgileri kodlayan bir sınıf oluşturursunuz ve ardından uzantıyı kaydetmek için biraz kod eklersiniz.

Uzantı sınıfındaki ana şey, gerekli alanları içeren bir nesneyi döndüren bir getInfo() yöntemidir:

  • id: uzantının dahili adı, her uzantı için benzersiz olmalıdır
  • name: Scratch'in blok listesinde görünen uzantının kolay adı
  • bloklar: yeni özel bloğu açıklayan nesnelerin listesi.

Ayrıca, Getir'de kullanılmayan ancak Gamepad'de kullanılacak isteğe bağlı bir menü alanı var.

İşte Fetch için temel şablon:

sınıf ScratchFetch {

yapıcı() { } getInfo() { return { "id": "Getir", "ad": "Getir", "bloklar": [/* daha sonra ekle */] } } /* bloklar için yöntemler ekle */ } Scratch.extensions.register(yeni ScratchFetch())

3. Adım: Korumalı Alana Alınmış Uzantı Yazma: Bölüm II

Şimdi getInfo()'nun nesnesindeki blokların listesini oluşturmamız gerekiyor. Her bloğun en az şu dört alana ihtiyacı vardır:

  • opcode: Bu, bloğun işini yapmak için çağrılan yöntemin adıdır.
  • blockType: bu blok tipidir; uzantılar için en yaygın olanları şunlardır:

    • "komut": bir şey yapar ama bir değer döndürmez
    • "raportör": bir dize veya sayı döndürür
    • "Boolean": bir boole döndürür (büyük harf kullanımına dikkat edin)
    • "hat": olay yakalama bloğu; Scratch kodunuz bu bloğu kullanıyorsa, Scratch çalışma zamanı, olayın olup olmadığını söylemek için bir boole döndüren ilişkili yöntemi düzenli olarak yoklar.
  • metin: bu, parantez içindeki argümanlarla birlikte bloğun kolay bir açıklamasıdır, ör. "'den veri al"
  • argümanlar: bu, her argüman için bir alanı olan bir nesnedir (örneğin, yukarıdaki örnekte "url"); bu nesne sırayla şu alanlara sahiptir:

    • tür: "dize" veya "sayı"
    • defaultValue: önceden doldurulacak varsayılan değer.

Örneğin, Fetch uzantımdaki bloklar alanı:

"bloklar": [{ "opcode": "fetchURL", "blockType": "raporcu", "metin": "'den veri al", "arguments": { "url": { "type": "string", "defaultValue ": "https://api.weather.gov/stations/KNYC/observations" }, } }, { "opcode": "jsonExtract", "blockType": "reporter", "text": "extract [name] from [data]", "arguments": { "name": { "type": "string", "defaultValue": "sıcaklık" }, "data": { "type": "string", "defaultValue": '{"sıcaklık": 12.3}' }, } },]

Burada iki blok tanımladık: fetchURL ve jsonExtract. İkisi de muhabir. Birincisi, bir URL'den veri çeker ve onu döndürür ve ikincisi, JSON verilerinden bir alan çıkarır.

Son olarak, iki blok için yöntemleri eklemeniz gerekir. Her yöntem, tüm argümanlar için alanları içeren nesneyle birlikte bir nesneyi argüman olarak alır. Bunları bağımsız değişkenlerde küme parantezleri kullanarak çözebilirsiniz. Örneğin, burada bir senkronize örnek:

jsonExtract({ad, veri}) {

var parsed = JSON.parse(data) if (ad in parsed) { var out = parsed[name] var t = typeof(out) if (t == "string" || t == "number") return out if (t == "boolean") dönüş t ? 1: 0 dönüş JSON.stringify(out) } else { return "" } }

Kod, ad alanını JSON verilerinden alır. Alan bir dize, sayı veya boole içeriyorsa, onu döndürürüz. Aksi takdirde, alanı yeniden JSONifiye ederiz. JSON'da isim eksikse boş bir dize döndürürüz.

Ancak bazen, zaman uyumsuz bir API kullanan bir blok oluşturmak isteyebilirsiniz. fetchURL() yöntemi, eşzamansız olan getirme API'sini kullanır. Böyle bir durumda, işi yapan yönteminizden bir söz vermelisiniz. Örneğin:

fetchURL({url}) {

döndürme fetch(url).then(yanıt => yanıt.text()) }

Bu kadar. Tam uzantı burada.

4. Adım: Korumalı Alanda Uzantı Kullanma

Korumalı Alan Uzantısı Kullanma
Korumalı Alan Uzantısı Kullanma
Korumalı Alan Uzantısı Kullanma
Korumalı Alan Uzantısı Kullanma
Korumalı Alan Uzantısı Kullanma
Korumalı Alan Uzantısı Kullanma

Korumalı alan uzantısını kullanmanın iki yolu vardır. İlk önce onu bir web sunucusuna yükleyebilir ve ardından SheepTester'ın Scratch moduna yükleyebilirsiniz. İkinci olarak, onu bir veri URL'sine kodlayabilir ve bunu Scratch moduna yükleyebilirsiniz. Aslında, uzantının eski sürümlerinin sunucu tarafından önbelleğe alınmasıyla ilgili endişeleri ortadan kaldırdığı için, test için ikinci yöntemi biraz kullanıyorum. Javascript'i Github Pages'den barındırabilirken, bunu doğrudan sıradan bir github deposundan yapamayacağınızı unutmayın.

fetch.js'im https://arpruss.github.io/fetch.js adresinde barındırılıyor. Veya uzantınızı buraya yükleyerek bir veri URL'sine dönüştürebilir ve ardından panoya kopyalayabilirsiniz. Veri URL'si, içinde bir dosyanın tamamını tutan dev bir URL'dir.

SheepTester'ın Scratch moduna gidin. Sol alt köşedeki Uzantı Ekle düğmesini tıklayın. Ardından "Bir uzantı seçin"i tıklayın ve URL'nizi girin (isterseniz dev veri URL'sinin tamamını yapıştırabilirsiniz).

Her şey yolunda giderse, Scratch ekranınızın sol tarafında uzantınız için bir girişiniz olacaktır. İşler yolunda gitmediyse, Javascript konsolunuzu (Chrome'da shift-ctrl-J) açmalı ve sorunu gidermeye çalışmalısınız.

Yukarıda, ABD Ulusal Hava Durumu Servisi'nin KNYC (New York'taki) istasyonundan JSON verilerini alıp ayrıştıran ve hareketli grafiği rüzgarın estiği yöne bakacak şekilde döndürürken görüntüleyen bazı örnek kodlar bulacaksınız. Bunu yapmamın yolu, verileri bir web tarayıcısına getirmek ve ardından etiketleri bulmaktı. Farklı bir hava durumu istasyonunu denemek istiyorsanız, hava durumu.gov adresindeki arama kutusuna yakındaki bir posta kodunu girin ve bulunduğunuz yerin hava durumu sayfası size KNYC yerine kullanabileceğiniz dört harfli bir istasyon kodu vermelidir. kod.

Ayrıca, bir "?url=" bağımsız değişkeni ekleyerek, korumalı alan uzantınızı SheepTester modunun URL'sine ekleyebilirsiniz. Örneğin:

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

5. Adım: Korumalı Alana Alınmamış Uzantı Yazma: Giriş

Korumalı alanda olmayan bir uzantının yapıcısı, bir Runtime nesnesinden geçer. Bunu görmezden gelebilir veya kullanabilirsiniz. Runtime nesnesinin bir kullanımı, olayları ("şapka blokları") eşitlemek için currentMSecs özelliğini kullanmaktır. Anlayabildiğim kadarıyla, tüm olay bloğu işlem kodları düzenli olarak yoklanır ve yoklamanın her turunda tek bir currentMSecs değeri vardır. Runtime nesnesine ihtiyacınız varsa, muhtemelen uzantınıza şu şekilde başlayacaksınız:

sınıf EXTENSIONCLASS {

yapıcı(çalışma zamanı) { this.runtime = runtime … } … }

Korumalı olmayan uzantıda tüm standart pencere nesnesi öğeleri kullanılabilir. Son olarak, korumalı alanda olmayan uzantınız şu sihirli kod parçasıyla bitmelidir:

(işlev() {

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

EXTENSIONCLASS'ı uzantınızın sınıfıyla değiştirmelisiniz.

6. Adım: Korumalı Alana Alınmamış Uzantı Yazma: Basit Oyun Kumandası

Şimdi bir düğmeye basıldığında veya bırakıldığında tek bir olay ("şapka") bloğu sağlayan basit bir gamepad uzantısı yapalım.

Her olay bloğu yoklama döngüsü sırasında, çalışma zamanı nesnesinden ve önceki ve mevcut oyun kumandası durumlarından bir zaman damgası kaydedeceğiz. Zaman damgası, yeni bir yoklama döngümüz olup olmadığını anlamak için kullanılır. Öyleyse başlıyoruz:

sınıf ScratchSimpleGamepad {

yapıcı(çalışma zamanı) { this.runtime = çalışma zamanı this.currentMSecs = -1 this.previousButtons = this.currentButtons = } … } İki girişli bir olay bloğumuz olacak - bir düğme numarası ve olayın basıldığında mı yoksa bırakıldığında mı tetiklenmesini istediğimizi seçmek için bir menü. Yani, işte yöntemimiz

bilgi almak() {

return { "id": "SimpleGamepad", "name": "SimpleGamepad", "blocks": [{ "opcode": "buttonPressedReleased", "blockType": "hat", "text": "button [eventType]", "arguments": { "b": { "type": "number", "defaultValue": "0" }, "eventType": { "type": "number", "defaultValue": "1 ", "menu": "pressReleaseMenu" }, }, },], "menüler": { "pressReleaseMenu": [{text:"press", value:1}, {text:"release", value:0}], } }; } Açılır menüdeki değerlerin sayı olarak bildirilmelerine rağmen hala opcode işlevine dizeler olarak aktarıldığını düşünüyorum. Bu nedenle, gerektiğinde bunları menüde belirtilen değerlerle açıkça karşılaştırın. Şimdi yeni bir olay yoklama döngüsü gerçekleştiğinde düğme durumlarını güncelleyen bir yöntem yazıyoruz.

Güncelleme() {

if (this.runtime.currentMSecs == this.currentMSecs) dönüşü // yeni bir yoklama döngüsü değil this.currentMSecs = this.runtime.currentMSecs var gamepads = navigator.getGamepads() if (gamepads == null || gamepads.length = = 0 || gamepads[0] == null) { this.previousButtons = this.currentButtons = dönüş } var gamepad = gamepads[0] if (gamepad.buttons.length != this.previousButtons.length) { // farklı sayıda düğme, yani yeni oyun kumandası 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) } Son olarak, update() yöntemini çağırarak ve ardından mevcut ve önceki düğme durumlarını karşılaştırarak gerekli düğmeye yeni basılıp basılmadığını kontrol ederek olay bloğumuzu uygulayabiliriz.

buttonPressedReleased({b, eventType}) {

this.update() if (b < this.currentButtons.length) { if (eventType == 1) { // not: bu bir dize olacaktır, bu nedenle onu Boolean olarak ele almaktansa 1 ile karşılaştırmak daha iyidir (this.currentButtons && ! this.previousButtons) { return true } } else { if (!this.currentButtons && this.previousButtons) { return true } } } false döndür } Ve son olarak sınıfı tanımladıktan sonra sihirli uzantı kayıt kodumuzu ekliyoruz.

(işlev() {

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

Kodun tamamını buradan alabilirsiniz.

7. Adım: Korumalı Alana Sahip Olmayan Uzantıyı Kullanma

Korumalı Alana Sahip Olmayan Uzantıyı Kullanma
Korumalı Alana Sahip Olmayan Uzantıyı Kullanma

Bir kez daha, uzantınızı bir yerde barındırın ve bu sefer SheepTester'ın Scratch moduna url= argümanı yerine load_plugin= ile yükleyin. Örneğin, basit Gamepad modum için şuraya gidin:

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

(Bu arada, daha gelişmiş bir gamepad istiyorsanız, yukarıdaki URL'den "basit" ifadesini kaldırmanız yeterlidir; rumble ve analog eksen desteğine sahip olacaksınız.)

Yine, uzantı, Scratch düzenleyicinizin sol tarafında görünmelidir. Yukarıda 0 düğmesine bastığınızda "merhaba", bıraktığınızda "hoşçakal" yazan çok basit bir Scratch programı var.

Adım 8: Çift Uyumluluk ve Hız

Korumalı alanda olmayan uzantılar için kullandığım yükleme yöntemini kullanarak uzantı bloklarının çok daha hızlı çalıştığını fark ettim. Bu nedenle, bir Web Worker sanal alanında çalışmanın güvenlik yararlarını önemsemiyorsanız, kodunuz SheepTester'ın moduna ?load_plugin=URL bağımsız değişkeni ile yüklenmekten fayda sağlayacaktır.

Uzantı sınıfını tanımladıktan sonra aşağıdaki kodu kullanarak korumalı alanlı bir uzantıyı her iki yükleme yöntemiyle uyumlu hale getirebilirsiniz (CLASSNAME öğesini uzantı sınıfınızın adıyla değiştirin):

(işlev() {

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) } })()

Önerilen: