Taula de continguts:

Extensions Scratch 3.0: 8 passos
Extensions Scratch 3.0: 8 passos

Vídeo: Extensions Scratch 3.0: 8 passos

Vídeo: Extensions Scratch 3.0: 8 passos
Vídeo: It'll Change your life - ENDLESS background in Scratch tutorial 2024, De novembre
Anonim
Extensions Scratch 3.0
Extensions Scratch 3.0

Les extensions Scratch són trossos de codi Javascript que afegeixen blocs nous a Scratch. Tot i que Scratch s’inclou amb un munt d’extensions oficials, no hi ha un mecanisme oficial per afegir extensions fetes per l’usuari.

Quan feia la meva extensió de control de Minecraft per a Scratch 3.0, em va costar començar. Aquest instructable recopila informació de diverses fonts (especialment aquesta), a més d'algunes coses que jo mateix vaig descobrir.

Heu de saber com programar a Javascript i com allotjar el vostre Javascript en un lloc web. Per a aquest últim, recomano GitHub Pages.

El truc principal és utilitzar el mod de Scratch de SheepTester que us permet carregar extensions i connectors.

Aquest instructiu us guiarà en la realització de dues extensions:

  • Recupera: carrega dades d'un URL i extreu etiquetes JSON, per exemple per carregar dades meteorològiques
  • SimpleGamepad: utilitzant un controlador de joc a Scratch (aquí hi ha una versió més sofisticada).

Pas 1: dos tipus d'extensions

Hi ha dos tipus d'extensions que anomenaré "sense caixa" i "caixa de sorra". Les extensions de caixa de proves s’executen com a treballadors web i, per tant, tenen limitacions importants:

  • Els treballadors web no poden accedir als globals de l’objecte finestra (en lloc d’això, tenen un objecte global global, que és molt més limitat), de manera que no els podeu utilitzar per a accions com ara el gamepad.
  • Les extensions de caixa de sorra no tenen accés a l'objecte d'execució de Scratch.
  • Les extensions amb caixa de sorra són molt més lentes.
  • Els missatges d’error de la consola Javascript per a les extensions de sandbox són més críptics a Chrome.

Per altra banda:

  • L’ús d’extensions de sandbox d’altres persones és més segur.
  • És més probable que les extensions amb caixa de sorra funcionin amb qualsevol suport oficial de càrrega d’extensions oficials.
  • Les extensions amb caixa de sorra es poden provar sense carregar-les a un servidor web codificant-les en un URL data: //.

Totes les extensions oficials (com ara Music, Pen, etc.) no s’enregistren. El constructor de l'extensió obté l'objecte d'execució des de Scratch i la finestra és totalment accessible.

L'extensió Fetch es troba a la zona de sorra, però el Gamepad necessita el navegador d'objectes des de la finestra.

Pas 2: escriure una extensió de caixa de sorra: primera part

Per crear una extensió, creeu una classe que codifiqui informació sobre ella i, a continuació, afegiu una mica de codi per registrar l'extensió.

El principal de la classe d'extensió és un mètode getInfo () que retorna un objecte amb els camps obligatoris:

  • id: el nom intern de l'extensió ha de ser únic per a cada extensió
  • name: el nom amable de l'extensió, que apareix a la llista de blocs de Scratch
  • blocs: llista d'objectes que descriuen el nou bloc personalitzat.

I hi ha un camp de menús opcional que no s’utilitza a Fetch, sinó que s’utilitzarà a Gamepad.

Per tant, aquí teniu la plantilla bàsica de Fetch:

classe ScratchFetch {

constructor () {} getInfo () {return {"id": "Fetch", "name": "Fetch", "blocks": [/* afegir més tard * /]}} / * afegir mètodes per a blocs * /} Scratch.extensions.register (nou ScratchFetch ())

Pas 3: escriure una extensió de caixa de sorra: part II

Ara, hem de crear la llista de blocs a l'objecte getInfo (). Cada bloc necessita almenys aquests quatre camps:

  • opcode: és el nom del mètode que es diu per fer la feina del bloc
  • blockType: aquest és el tipus de bloc; les més habituals per a extensions són:

    • "ordre": fa alguna cosa però no retorna cap valor
    • "reporter": retorna una cadena o un número
    • "Booleà": retorna un booleà (tingueu en compte les majúscules)
    • "barret": bloc de captura d'esdeveniments; si el vostre codi Scratch utilitza aquest bloc, el temps d'execució de Scratch enquesta regularment el mètode associat que retorna un booleà per dir si s'ha produït l'esdeveniment
  • text: es tracta d'una descripció amigable del bloc, amb els arguments entre claudàtors, per exemple, "obtenir dades de "
  • arguments: és un objecte que té un camp per a cada argument (per exemple, "url" a l'exemple anterior); aquest objecte al seu torn té aquests camps:

    • escriviu: "cadena" o "número"
    • defaultValue: el valor per defecte que cal omplir prèviament.

Per exemple, aquí teniu el camp de blocs de la meva extensió Fetch:

"blocs": [{"opcode": "fetchURL", "blockType": "reporter", "text": "obtenir dades de ", "arguments": {"url": {"type": "string", "defaultValue ":" https://api.weather.gov/stations/KNYC/observations "},}}, {" opcode ":" jsonExtract "," blockType ":" reporter "," text ":" extracte [nom] de [dades] "," arguments ": {" nom ": {" tipus ":" cadena "," valor default ":" temperatura "}," dades ": {" tipus ":" cadena "," valor per defecte ": '{"temperature": 12.3}'},}},]

Aquí hem definit dos blocs: fetchURL i jsonExtract. Tots dos són reporters. El primer treu dades d'un URL i les retorna, i el segon extreu un camp de dades JSON.

Finalment, cal incloure els mètodes per a dos blocs. Cada mètode pren un objecte com a argument, amb l'objecte que inclou camps per a tots els arguments. Podeu descodificar-los fent servir claus als arguments. Per exemple, aquí teniu un exemple síncron:

jsonExtract ({nom, dades}) {

var analitzat = JSON.parse (dades) si (nom analitzat) {var out = analitzat [nom] var t = typeof (out) si (t == "cadena" || t == "número") torna si (t == "booleà") torna t? 1: 0 return JSON.stringify (out)} else {return ""}}

El codi treu el camp del nom de les dades JSON. Si el camp conté una cadena, número o booleà, el retornem. En cas contrari, tornem a JSONificar el camp. I retornem una cadena buida si falta el nom al JSON.

De vegades, però, és possible que vulgueu crear un bloc que utilitzi una API asíncrona. El mètode fetchURL () utilitza l'API fetch que és asíncrona. En aquest cas, haureu de tornar una promesa del vostre mètode que faci la feina. Per exemple:

fetchURL ({url}) {

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

Això és. L’extensió completa és aquí.

Pas 4: utilitzar una extensió Sandboxed

Ús d’una extensió Sandboxed
Ús d’una extensió Sandboxed
Ús d’una extensió Sandboxed
Ús d’una extensió Sandboxed
Ús d’una extensió Sandboxed
Ús d’una extensió Sandboxed

Hi ha dues maneres d'utilitzar les extensions de sandbox. En primer lloc, podeu penjar-lo a un servidor web i després carregar-lo al mod Scratch de SheepTester. En segon lloc, podeu codificar-lo en un URL de dades i carregar-lo al mod Scratch. En realitat, faig servir el segon mètode bastant per fer proves, ja que evita preocupacions pel fet que el servidor emmagatzemi a la memòria cau versions anteriors de l’extensió. Tingueu en compte que, tot i que podeu allotjar javascript des de Github Pages, no ho podeu fer directament des d’un dipòsit normal de github.

El meu fetch.js està allotjat a https://arpruss.github.io/fetch.js. O bé, podeu convertir l'extensió a un URL de dades carregant-la aquí i després copiant-la al porta-retalls. Un URL de dades és un URL gegant que conté tot un fitxer.

Aneu al mod Scratch de SheepTester. Feu clic al botó Afegeix extensió a l'extrem inferior esquerre. A continuació, feu clic a "Tria una extensió" i introduïu el vostre URL (si voleu, podeu enganxar a l'URL de dades gegants sencer).

Si tot va bé, tindreu una entrada per a la vostra extensió a la part esquerra de la pantalla Scratch. Si les coses no van anar bé, hauríeu d'obrir la consola Javascript (shift-ctrl-J a Chrome) i provar de depurar el problema.

A la part superior trobareu algun exemple de codi que recupera i analitza les dades JSON de l’estació KNYC (a Nova York) del Servei Meteorològic Nacional dels Estats Units i les mostra, mentre gira el sprite per enfrontar-se de la mateixa manera que bufa el vent. La forma en què ho vaig fer va ser recollint les dades en un navegador web i després descobrint les etiquetes. Si voleu provar una estació meteorològica diferent, introduïu un codi postal proper al quadre de cerca de weather.gov i la pàgina meteorològica de la vostra ubicació us proporcionarà un codi d’estació de quatre lletres, que podeu utilitzar en lloc de KNYC al codi.

També podeu incloure l'extensió de la zona de proves directament a l'URL del mod de SheepTester afegint un argument "? Url =". Per exemple:

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

Pas 5: escriure una extensió sense arquetes: introducció

El constructor d'una extensió sense caixa de sorra passa un objecte d'execució. Podeu ignorar-lo o utilitzar-lo. Un ús de l'objecte Runtime és utilitzar la seva propietat MSMS actual per sincronitzar esdeveniments ("blocs de barret"). Pel que sé, tots els codis d’opció del bloc d’esdeveniments s’interroguen periòdicament i cada ronda de votació té un valor de MSecs actual. Si necessiteu l'objecte Runtime, probablement inicieu l'extensió amb:

classe EXTENSIONCLASS {

constructor (temps d'execució) {this.runtime = temps d'execució …} …}

Totes les coses estàndard d'objecte de finestra es poden utilitzar a l'extensió sense caixa. Finalment, l'extensió sense caixa d'arena hauria d'acabar amb aquest bit de codi màgic:

(funció () {

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

on hauríeu de substituir EXTENSIONCLASS per la classe de la vostra extensió.

Pas 6: escriure una extensió sense arxivar: simple Gamepad

Fem ara una simple extensió de gamepad que proporciona un bloc d'esdeveniments ("barret") únic per a quan es prem o es deixa anar un botó.

Durant cada cicle de sondeig de blocs d’esdeveniments, desarem una marca de temps de l’objecte de temps d’execució i els estats de joc anterior i actual. La marca de temps s’utilitza per reconèixer si tenim un nou cicle de votació. Comencem, doncs, amb:

classe ScratchSimpleGamepad {

constructor (temps d'execució) {this.runtime = temps d'execució this.currentMSecs = -1 this.previousButtons = this.currentButtons = } …} Tindrem un bloc d'esdeveniments, amb dues entrades: un número de botó i un menú per seleccionar si volem que l'esdeveniment s'activi en prémer o en deixar anar. Per tant, aquí teniu el nostre mètode

obtenir informació() {

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 "},},},]," menus ": {" pressReleaseMenu ": [{text:" press ", valor: 1}, {text:" release ", valor: 0}],}}; } Crec que els valors del menú desplegable encara es passen a la funció opcode com a cadenes, tot i ser declarats com a números. Per tant, compareu-los explícitament amb els valors especificats al menú segons sigui necessari. Ara escrivim un mètode que actualitza els estats del botó cada vegada que es produeix un nou cicle de votació d'esdeveniments

update () {

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) { // nombre diferent de botons, així que el nou gamepad this.previousButtons = per a (var i = 0; i <gamepad.buttons.length; i ++) this.previousButtons.push (false)} else {this.previousButtons = this. currentButtons} this.currentButtons = per a (var i = 0; i <gamepad.buttons.length; i ++) this.currentButtons.push (gamepad.buttons .pressed)} Finalment, podem implementar el nostre bloc d’esdeveniments, trucant al mètode update () i després comprovant si s’acaba de prémer o alliberar el botó requerit, comparant els estats de botó actual i anterior.

buttonPressedReleased ({b, eventType}) {

this.update () if (b <this.currentButtons.length) {if (eventType == 1) {// nota: aquesta serà una cadena, així que és millor comparar-la amb 1 que tractar-la com a booleana si (this.currentButtons &&! this.previousButtons ) {return true}} else {if (! this.currentButtons && this.previousButtons ) {return true}}} return false} I, finalment, afegim el nostre codi de registre d’extensió màgica després de definir la classe

(funció () {

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

Podeu obtenir el codi complet aquí.

Pas 7: fer servir una extensió sense proves

Utilitzant una extensió sense arquetes
Utilitzant una extensió sense arquetes

Una vegada més, allotgeu la vostra extensió en algun lloc i, aquesta vegada, carregueu-la amb l’argument load_plugin = en lloc d’argument url = al mod Scratch de SheepTester. Per exemple, per al meu senzill mod de Gamepad, aneu a:

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

(Per cert, si voleu un gamepad més sofisticat, només cal que elimineu "simple" de l'URL anterior i tindreu suport per a l'eix analògic i el rumble.)

De nou, l'extensió hauria d'aparèixer a la part esquerra de l'editor Scratch. A dalt hi ha un programa Scratch molt senzill que diu "hola" quan premeu el botó 0 i "adéu" quan el deixeu anar.

Pas 8: compatibilitat dual i velocitat

He observat que els blocs d’extensions s’executen un ordre de magnitud més ràpidament mitjançant el mètode de càrrega que he utilitzat per a extensions sense caixa. Per tant, tret que us interessi els avantatges de seguretat d’executar-vos en un sandbox de Web Worker, el vostre codi es beneficiarà de carregar-se amb l’argument? Load_plugin = URL al mod de SheepTester.

Podeu fer compatible una extensió de sandbox amb els dos mètodes de càrrega mitjançant el codi següent després de definir la classe d’extensió (canvieu CLASSNAME pel nom de la vostra classe d’extensió):

(funció () {

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

Recomanat: