Cuprins:

Extensii Scratch 3.0: 8 pași
Extensii Scratch 3.0: 8 pași

Video: Extensii Scratch 3.0: 8 pași

Video: Extensii Scratch 3.0: 8 pași
Video: DECORURI ȘII SUNETE ÎN SCRATCH 2024, Noiembrie
Anonim
Extensii Scratch 3.0
Extensii Scratch 3.0

Extensiile Scratch sunt bucăți de cod Javascript care adaugă blocuri noi la Scratch. În timp ce Scratch este inclus în pachet cu o grămadă de extensii oficiale, nu există un mecanism oficial pentru adăugarea de extensii create de utilizator.

Când îmi făceam extensia de control Minecraft pentru Scratch 3.0, mi-a fost greu să încep. Acest Instructable colectează împreună informații din diverse surse (în special din asta), plus câteva lucruri pe care le-am descoperit eu.

Trebuie să știți cum să programați în Javascript și cum să vă găzduiți Javascript pe un site web. Pentru acesta din urmă, recomand GitHub Pages.

Principalul truc este să folosiți modul Scratch al SheepTester, care vă permite să încărcați extensii și pluginuri.

Acest instructable vă va ghida prin realizarea a două extensii:

  • Preluare: încărcarea datelor dintr-o adresă URL și extragerea etichetelor JSON, de exemplu pentru încărcarea datelor meteo
  • SimpleGamepad: folosind un controler de joc în Scratch (o versiune mai sofisticată este aici).

Pasul 1: două tipuri de extensii

Există două tipuri de extensii pe care le voi numi „unsandboxed” și „sandboxed”. Extensiile cu sandbox rulează ca Web Workers și, ca urmare, au limitări semnificative:

  • Web Workers nu poate accesa globurile din obiectul ferestrei (în schimb, au un obiect global global, care este mult mai limitat), deci nu le puteți folosi pentru lucruri precum accesul la gamepad.
  • Extensiile cu sandbox nu au acces la obiectul de execuție Scratch.
  • Extensiile cu nisip sunt mult mai lente.
  • Mesajele de eroare ale consolei Javascript pentru extensiile cu sandbox sunt mai criptice în Chrome.

Pe de altă parte:

  • Folosirea extensiilor cu sandbox altor persoane este mai sigură.
  • Este mai probabil ca extensiile cu nisip să funcționeze cu orice eventual suport oficial de încărcare a extensiilor.
  • Extensiile cu sandbox pot fi testate fără a fi încărcate pe un server web prin codificarea într-un URL de date: //.

Extensiile oficiale (cum ar fi Muzica, stiloul etc.) sunt toate fără sandbox. Constructorul pentru extensie primește obiectul de execuție de la Scratch, iar fereastra este complet accesibilă.

Extensia Fetch este sandboxed, dar Gamepad are nevoie de obiectul navigator din fereastră.

Pasul 2: Scrierea unei extensii cu sandbox: partea I

Pentru a crea o extensie, creați o clasă care codifică informații despre aceasta și apoi adăugați un pic de cod pentru a înregistra extensia.

Principalul lucru din clasa de extensie este o metodă getInfo () care returnează un obiect cu câmpurile obligatorii:

  • id: numele intern al extensiei trebuie să fie unic pentru fiecare extensie
  • nume: numele prietenos al extensiei, care apare în lista de blocuri Scratch
  • blocuri: o listă de obiecte care descriu noul bloc personalizat.

Și există un câmp de meniuri opțional care nu se obișnuiește în Fetch, dar va fi folosit în Gamepad.

Deci, iată șablonul de bază pentru Fetch:

clasa ScratchFetch {

constructor () {} getInfo () {return {"id": "Preluare", "nume": "Preluare", "blocuri": [/* adăugați mai târziu * /]}} / * adăugați metode pentru blocuri * /} Scratch.extensions.register (nou ScratchFetch ())

Pasul 3: Scrierea unei extensii cu nisip: Partea II

Acum, trebuie să creăm lista blocurilor din obiectul getInfo (). Fiecare bloc are nevoie de cel puțin aceste patru câmpuri:

  • opcode: acesta este numele metodei care este chemată pentru a face treaba blocului
  • blockType: acesta este tipul de bloc; cele mai frecvente pentru extensii sunt:

    • „comanda”: face ceva dar nu returnează o valoare
    • „reporter”: returnează un șir sau un număr
    • „Boolean”: returnează un boolean (rețineți majusculele)
    • „pălărie”: bloc de captare a evenimentului; dacă codul dvs. Scratch folosește acest bloc, runtime-ul Scratch sondează în mod regulat metoda asociată care returnează un boolean pentru a spune dacă evenimentul s-a întâmplat
  • text: aceasta este o descriere prietenoasă a blocului, cu argumentele între paranteze, de exemplu, „preluați date de la ”
  • argumente: acesta este un obiect care are un câmp pentru fiecare argument (de exemplu, „url” în exemplul de mai sus); la rândul său, acest obiect are următoarele câmpuri:

    • tastați: „șir” sau „număr”
    • defaultValue: valoarea implicită care trebuie preumplută.

De exemplu, aici este câmpul blocuri din extensia mea de preluare:

„blocuri”: [{"opcode": "fetchURL", "blockType": "reporter", "text": "preluați date din ", "arguments": {"url": {"type": "string", "defaultValue ":" https://api.weather.gov/stations/KNYC/observations "},}}, {" opcode ":" jsonExtract "," blockType ":" reporter "," text ":" extrage [numele] din [date] "," arguments ": {" name ": {" type ":" string "," defaultValue ":" temperature "}," data ": {" type ":" string "," defaultValue ": „{" temperature ": 12.3} '},}},]

Aici, am definit două blocuri: fetchURL și jsonExtract. Ambii sunt reporteri. Primul extrage date dintr-un URL și le returnează, iar al doilea extrage un câmp din datele JSON.

În cele din urmă, trebuie să includeți metodele pentru două blocuri. Fiecare metodă ia un obiect ca argument, cu obiectul incluzând câmpuri pentru toate argumentele. Puteți decoda acestea folosind acolade în argumente. De exemplu, iată un exemplu sincron:

jsonExtract ({nume, date}) {

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

Codul extrage câmpul de nume din datele JSON. Dacă câmpul conține un șir, un număr sau boolean, îl returnăm. În caz contrar, re-JSONificăm câmpul. Și returnăm un șir gol dacă numele JSON lipsește.

Uneori, totuși, poate doriți să creați un bloc care utilizează un API asincron. Metoda fetchURL () utilizează API-ul fetch care este asincron. Într-un astfel de caz, ar trebui să returnați o promisiune din metoda dvs. care face treaba. De exemplu:

fetchURL ({url}) {

return fetch (url).then (response => response.text ())}

Asta e. Extensia completă este aici.

Pasul 4: Utilizarea unei extensii Sandboxed

Utilizarea unei extensii Sandboxed
Utilizarea unei extensii Sandboxed
Utilizarea unei extensii Sandboxed
Utilizarea unei extensii Sandboxed
Utilizarea unei extensii Sandboxed
Utilizarea unei extensii Sandboxed

Există două moduri de utilizare a extensiei cu nisip. Mai întâi, îl puteți încărca pe un server web și apoi îl puteți încărca în modul Scratch al SheepTester. În al doilea rând, îl puteți codifica într-un URL de date și îl puteți încărca în modul Scratch. De fapt, folosesc a doua metodă destul de puțin pentru testare, deoarece evită îngrijorările cu privire la versiunile mai vechi ale extensiei de a fi stocate în cache de server. Rețineți că, deși puteți găzdui javascript din paginile Github, nu puteți face acest lucru direct dintr-un depozit github obișnuit.

My fetch.js este găzduit la https://arpruss.github.io/fetch.js. Sau vă puteți converti extensia într-o adresă URL de date încărcând-o aici și apoi copiind-o în clipboard. O adresă URL de date este o adresă URL gigantă care conține un întreg fișier în ea.

Accesați modul Scratch al SheepTester. Faceți clic pe butonul Adăugare extensie din colțul din stânga jos. Apoi faceți clic pe „Alegeți o extensie” și introduceți adresa URL (puteți insera în întreaga adresă URL gigant dacă doriți).

Dacă totul a mers bine, veți avea o intrare pentru extensia dvs. în partea stângă a ecranului Scratch. Dacă lucrurile nu au mers bine, ar trebui să deschideți consola Javascript (shift-ctrl-J în Chrome) și să încercați să depanați problema.

Mai sus veți găsi un exemplu de cod care preia și analizează date JSON de la stația KNYC (din New York) a Serviciului Național Meteorologic din SUA și îl afișează, în timp ce întoarceți sprite-ul pentru a se confrunta în același mod în care bate vântul. Modul în care am realizat-o a fost prin preluarea datelor într-un browser web și apoi aflarea etichetelor. Dacă doriți să încercați o altă stație meteo, introduceți un cod poștal din apropiere în caseta de căutare de la weather.gov, iar pagina meteo pentru locația dvs. ar trebui să vă ofere un cod de stație din patru litere, pe care să îl puteți utiliza în locul KNYC în cod.

De asemenea, puteți include extensia dvs. sandbox chiar în adresa URL a modului SheepTester adăugând un argument „? Url =”. De exemplu:

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

Pasul 5: Scrierea unei extensii fără sandbox: Introducere

Constructorul unei extensii nesandboxate trece un obiect Runtime. Puteți să o ignorați sau să o utilizați. O utilizare a obiectului Runtime este de a utiliza proprietatea sa curentă MSecs pentru a sincroniza evenimente („blocuri de pălării”). Din câte îmi dau seama, toate codurile de opțiuni ale blocurilor de evenimente sunt sondate în mod regulat și fiecare rundă de votare are o singură valoare curentă MSecs. Dacă aveți nevoie de obiectul Runtime, probabil că veți începe extensia cu:

clasa EXTENSIONCLASS {

constructor (runtime) {this.runtime = runtime …} …}

Toate obiectele standard ale ferestrei pot fi folosite în extensia nesandboxată. În cele din urmă, extensia dvs. fără sandbox ar trebui să se încheie cu acest bit de cod magic:

(function () {

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

unde ar trebui să înlocuiți EXTENSIONCLASS cu clasa extensiei dvs.

Pasul 6: Scrierea unei extensii fără sandbox: Gamepad simplu

Să facem acum o extensie de gamepad simplă care oferă un singur bloc de evenimente („pălărie”) pentru apăsarea sau eliberarea unui buton.

În timpul fiecărui ciclu de sondare a blocului de evenimente, vom salva un timestamp din obiectul runtime și stările de gamepad anterioare și actuale. Marcajul de timp este folosit pentru a recunoaște dacă avem un nou ciclu de votare. Deci, începem cu:

clasa ScratchSimpleGamepad {

constructor (runtime) {this.runtime = runtime this.currentMSecs = -1 this.previousButtons = this.currentButtons = } …} Vom avea un bloc de evenimente, cu două intrări - un număr de buton și un meniu pentru a selecta dacă dorim ca evenimentul să se declanșeze la apăsare sau eliberare. Deci, iată metoda noastră

obtine informatii() {

return {"id": "SimpleGamepad", "name": "SimpleGamepad", "blocks": [{"opcode": "buttonPressedReleased", "blockType": "hat", "text": "butonul [eventType] "," arguments ": {" b ": {" type ":" number "," defaultValue ":" 0 "}," eventType ": {" type ":" number "," defaultValue ":" 1 "," menu ":" pressReleaseMenu "},},},]," menus ": {" pressReleaseMenu ": [{text:" press ", valoare: 1}, {text:" release ", valoare: 0}],}}; } Cred că valorile din meniul derulant sunt transmise în continuare funcției opcode ca șiruri, în ciuda faptului că sunt declarate ca numere. Deci, comparați-le în mod explicit cu valorile specificate în meniu, după cum este necesar. Acum scriem o metodă care actualizează stările butonului ori de câte ori se întâmplă un nou ciclu de sondare a evenimentelor

Actualizați() {

if (this.runtime.currentMSecs == this.currentMSecs) return // not a new polling cycle 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) { // număr diferit de butoane, deci nou gamepad this.previousButtons = pentru (var i = 0; i <gamepad.buttons.length; i ++) this.previousButtons.push (false)} else {this.previousButtons = this. currentButtons} this.currentButtons = pentru (var i = 0; i <gamepad.buttons.length; i ++) this.currentButtons.push (gamepad.buttons .pressed)} În cele din urmă, putem implementa blocul nostru de evenimente, apelând metoda update () și apoi verificând dacă butonul necesar tocmai a fost apăsat sau eliberat, comparând stările curente și anterioare ale butoanelor

buttonPressedReleased ({b, eventType}) {

this.update () if (b <this.currentButtons.length) {if (eventType == 1) {// notă: acesta va fi un șir, deci mai bine să-l comparați cu 1 decât să-l tratați ca pe un boolean dacă (this.currentButtons &&! this.previousButtons ) {return true}} else {if (! this.currentButtons && this.previousButtons ) {return true}}} return false} Și, în cele din urmă, adăugăm codul nostru de înregistrare a extensiei magice după definirea clasei

(funcție () {

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

Puteți obține codul complet aici.

Pasul 7: Utilizarea unei extensii fără sandbox

Utilizarea unei extensii fără sandbox
Utilizarea unei extensii fără sandbox

Încă o dată, găzduiți extensia dvs. undeva și de data aceasta încărcați-o cu argumentul load_plugin = mai degrabă decât url = în modul Scratch al SheepTester. De exemplu, pentru modul meu simplu Gamepad, accesați:

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

(Apropo, dacă doriți un gamepad mai sofisticat, pur și simplu eliminați „simplu” din URL-ul de mai sus și veți avea suport pentru ronță și axă analogică.)

Din nou, extensia ar trebui să apară în partea stângă a editorului Scratch. Mai sus este un program Scratch foarte simplu care spune „salut” când apăsați butonul 0 și „la revedere” când îl eliberați.

Pasul 8: compatibilitate dublă și viteză

Am observat că blocurile de extensii rulează o ordine de mărime mai repede folosind metoda de încărcare pe care am folosit-o pentru extensiile fără sandbox. Deci, dacă nu vă pasă de avantajele de securitate ale rularii într-un sandbox Web Worker, codul dvs. va beneficia de a fi încărcat cu argumentul? Load_plugin = URL în modul SheepTester.

Puteți face o extensie cu sandbox compatibilă cu ambele metode de încărcare utilizând următorul cod după definirea clasei de extensie (schimbați CLASSNAME la numele clasei de extensie):

(function () {

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

Recomandat: