Innehållsförteckning:

Scratch 3.0 -tillägg: 8 steg
Scratch 3.0 -tillägg: 8 steg

Video: Scratch 3.0 -tillägg: 8 steg

Video: Scratch 3.0 -tillägg: 8 steg
Video: 4. Koda ett spel i Scratch! Del 1: Välj figur och rita bana 2024, Juli
Anonim
Scratch 3.0 -tillägg
Scratch 3.0 -tillägg

Skraptillägg är bitar av Javascript -kod som lägger till nya block till Scratch. Medan Scratch är samlad med ett gäng officiella tillägg, finns det ingen officiell mekanism för att lägga till användartillverkade tillägg.

När jag gjorde min Minecraft -styrningstillägg för Scratch 3.0 tyckte jag att det var svårt att komma igång. Denna instruktionsbok samlar ihop information från olika källor (särskilt detta), plus några saker jag upptäckte själv.

Du behöver veta hur du programmerar i Javascript och hur du lagrar ditt Javascript på en webbplats. För det senare rekommenderar jag GitHub Pages.

Huvudtricket är att använda SheepTesters mod av Scratch som låter dig ladda tillägg och plugins.

Denna instruktionsbok guidar dig genom att göra två tillägg:

  • Hämta: ladda data från en URL och extrahera JSON -taggar, till exempel för att ladda väderdata
  • SimpleGamepad: använder en spelkontroll i Scratch (en mer sofistikerad version finns här).

Steg 1: Två typer av tillägg

Det finns två typer av tillägg som jag kommer att kalla "osandboxad" och "sandlåda". Sandlåda -tillägg körs som webbarbetare och har därför betydande begränsningar:

  • Webbarbetare kan inte komma åt globalerna i fönsterobjektet (i stället har de ett globalt självobjekt, vilket är mycket mer begränsat), så du kan inte använda dem för saker som åtkomst till gamepad.
  • Sandlåda -tillägg har inte tillgång till Scratch runtime -objektet.
  • Sandlådeförlängningar är mycket långsammare.
  • Javascript -konsolfelmeddelanden för sandlåda -tillägg är mer kryptiska i Chrome.

Å andra sidan:

  • Att använda andra människors sandlåda -tillägg är säkrare.
  • Sandlåda -tillägg fungerar mer sannolikt med eventuellt officiellt stöd för laddning av tillägg.
  • Tillägg med sandlåda kan testas utan överföring till en webbserver genom kodning till en data: // URL.

De officiella tilläggen (som musik, penna, etc.) är alla osandade. Konstruktorn för tillägget får körtidsobjektet från Scratch och fönstret är fullt tillgängligt.

Tillägget Hämta är sandlådat, men Gamepad behöver navigationsobjektet från fönstret.

Steg 2: Skriva en Sandboxed Extension: Del I

För att göra ett tillägg skapar du en klass som kodar information om den och lägger sedan till en bit kod för att registrera tillägget.

Det viktigaste i tilläggsklassen är en getInfo () -metod som returnerar ett objekt med de obligatoriska fälten:

  • id: tilläggets interna namn måste vara unikt för varje tillägg
  • namn: tilläggets vänliga namn, som visas i Scratchs lista över block
  • block: en lista över objekt som beskriver det nya anpassade blocket.

Och det finns ett valfritt menyfält som inte används i Fetch men kommer att användas i Gamepad.

Så här är den grundläggande mallen för Hämta:

klass ScratchFetch {

constructor () {} getInfo () {return {"id": "Hämta", "namn": "Hämta", "block": [/* lägg till senare * /]}} / * lägg till metoder för block * /} Scratch.extensions.register (nytt ScratchFetch ())

Steg 3: Skriva en Sandboxed Extension: Del II

Nu måste vi skapa listan över block i getInfo () objekt. Varje block behöver minst dessa fyra fält:

  • opcode: detta är namnet på metoden som kallas för att utföra blockets arbete
  • blockType: detta är blocktypen; de vanligaste för tillägg är:

    • "kommando": gör något men returnerar inte ett värde
    • "reporter": returnerar en sträng eller ett tal
    • "Boolean": returnerar en boolean (notera versaler)
    • "hatt": blockering av händelser; om din Scratch -kod använder detta block, undersöker Scratch -körtiden regelbundet den associerade metoden som returnerar en boolean för att säga om händelsen har hänt
  • text: detta är en vänlig beskrivning av blocket, med argumenten inom parentes, t.ex. "hämta data från "
  • argument: detta är ett objekt som har ett fält för varje argument (t.ex. "url" i exemplet ovan); detta objekt har i sin tur följande fält:

    • typ: antingen "string" eller "number"
    • defaultValue: standardvärdet som ska förfyllas.

Här är till exempel blockfältet i min Hämtningstillägg:

"block": [{"opcode": "fetchURL", "blockType": "reporter", "text": "hämta data från ", "argument": {"url": {"type": "string", "defaultValue ":" https://api.weather.gov/stations/KNYC/observations "},}}, {" opcode ":" jsonExtract "," blockType ":" reporter "," text ":" extract [name] från [data] "," argument ": {" name ": {" type ":" string "," defaultValue ":" temperatur "}," data ": {" type ":" string "," defaultValue ": '{"temperatur": 12.3}'},}},]

Här definierade vi två block: fetchURL och jsonExtract. Båda är reportrar. Den första hämtar data från en URL och returnerar den, och den andra extraherar ett fält från JSON -data.

Slutligen måste du inkludera metoderna för två block. Varje metod tar ett objekt som ett argument, med objektet inklusive fält för alla argument. Du kan avkoda dessa med lockiga hängslen i argumenten. Här är till exempel ett synkront exempel:

jsonExtract ({namn, data}) {

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

Koden hämtar namnfältet från JSON -data. Om fältet innehåller en sträng, ett nummer eller en boolean, returnerar vi det. Annars JSONifierar vi fältet igen. Och vi returnerar en tom sträng om namnet saknas i JSON.

Ibland kan du dock skapa ett block som använder ett asynkront API. Metoden fetchURL () använder fetch API som är asynkron. I ett sådant fall bör du lämna tillbaka ett löfte från din metod som gör jobbet. Till exempel:

fetchURL ({url}) {

returhämtning (url). sedan (response => response.text ())}

Det är allt. Hela förlängningen är här.

Steg 4: Använda en Sandboxed Extension

Använda en Sandboxed Extension
Använda en Sandboxed Extension
Använda en Sandboxed Extension
Använda en Sandboxed Extension
Använda en Sandboxed Extension
Använda en Sandboxed Extension

Det finns två sätt att använda förlängning med sandlåda. Först kan du ladda upp den till en webbserver och sedan ladda den till SheepTesters Scratch -mod. För det andra kan du koda det till en data -URL och ladda det till Scratch -modet. Jag använder faktiskt den andra metoden ganska mycket för att testa, eftersom den undviker oro för att äldre versioner av tillägget ska cachas av servern. Observera att även om du kan vara värd för javascript från Github Pages, kan du inte göra det direkt från ett vanligt github -arkiv.

Min fetch.js finns på https://arpruss.github.io/fetch.js. Eller så kan du konvertera ditt tillägg till en data -URL genom att ladda upp det här och sedan kopiera det till Urklipp. En data -URL är en gigantisk URL som innehåller en hel fil i den.

Gå till SheepTesters Scratch -mod. Klicka på knappen Lägg till tillägg i nedre vänstra hörnet. Klicka sedan på "Välj ett tillägg" och ange din webbadress (du kan klistra in hela den gigantiska webbadressen om du vill).

Om allt gick bra har du en post för ditt tillägg till vänster på din Scratch-skärm. Om det inte gick bra bör du öppna din Javascript-konsol (shift-ctrl-J i Chrome) och försöka felsöka problemet.

Ovan hittar du en exempelkod som hämtar och analyserar JSON -data från KNYC (i New York) -stationen i US National Weather Service och visar den medan den vänder på spriten mot samma sätt som vinden blåser. Så här gjorde jag det genom att hämta data till en webbläsare och sedan räkna ut taggarna. Om du vill prova en annan väderstation, ange ett närliggande postnummer i sökrutan på weather.gov, och vädersidan för din plats ska ge dig en fyrbokstavsstationskod som du kan använda i stället för KNYC i koda.

Du kan också inkludera ditt sandlåda -tillägg direkt i webbadressen för SheepTesters mod genom att lägga till ett "? Url =" -argument. Till exempel:

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

Steg 5: Skriva en osandboxad tillägg: Introduktion

Konstruktorn för en osandboxad tillägg får ett Runtime -objekt passerat. Du kan ignorera det eller använda det. En användning av Runtime -objektet är att använda dess currentMSecs -egenskap för att synkronisera händelser ("hattblock"). Såvitt jag kan se är alla händelseblockets opkoder pollade regelbundet, och varje omgång av omröstningen har ett enda currentMSecs -värde. Om du behöver Runtime -objektet kommer du förmodligen att starta ditt tillägg med:

klass EXTENSIONCLASS {

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

Alla standardfönsterobjekt -saker kan användas i tillägget utan sandbox. Slutligen bör din osandboxade tillägg sluta med denna bit av magisk kod:

(funktion () {

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

där du ska ersätta EXTENSIONCLASS med din tilläggsklass.

Steg 6: Skriva en osandboxad tillägg: Enkel spelkontroll

Låt oss nu göra en enkel gamepad -förlängning som ger en enda händelse ("hatt") block för när en knapp trycks eller släpps.

Under varje händelseblocks pollingcykel kommer vi att spara en tidsstämpel från körtidsobjektet och tidigare och nuvarande spelkontrollstater. Tidsstämpeln används för att känna igen om vi har en ny omröstningscykel. Så, vi börjar med:

klass ScratchSimpleGamepad {

constructor (runtime) {this.runtime = runtime this.currentMSecs = -1 this.previousButtons = this.currentButtons = }…} Vi kommer att ha ett händelseblock, med två ingångar-ett knappnummer och en meny för att välja om vi vill att händelsen ska aktiveras vid tryck eller släpp. Så här är vår metod

få information() {

return {"id": "SimpleGamepad", "name": "SimpleGamepad", "blocks": [{"opcode": "buttonPressedReleased", "blockType": "hat", "text": "knapp [eventType] "," argument ": {" b ": {" type ":" number "," defaultValue ":" 0 "}," eventType ": {" type ":" number "," defaultValue ":" 1 "," menu ":" pressReleaseMenu "},},},]," menyer ": {" pressReleaseMenu ": [{text:" press ", värde: 1}, {text:" release ", värde: 0}],}}; } Jag tror att värdena i rullgardinsmenyn fortfarande överförs till opcode-funktionen som strängar, trots att de deklareras som siffror. Så jämför dem uttryckligen med de värden som anges i menyn efter behov. Vi skriver nu en metod som uppdaterar knapptillstånden när en ny händelsepollningscykel inträffar

uppdatering() {

if (this.runtime.currentMSecs == this.currentMSecs) returnerar // inte en ny pollingcykel 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) { // olika antal knappar, så ny gamepad this.previousButtons = för (var i = 0; i <gamepad.buttons.length; i ++) this.previousButtons.push (false)} else {this.previousButtons = this. currentButtons} this.currentButtons = för (var i = 0; i <gamepad.buttons.length; i ++) this.currentButtons.push (gamepad.buttons .pressed)} Slutligen kan vi implementera vårt händelseblock genom att ringa metoden update () och sedan kontrollera om den nödvändiga knappen just har tryckts eller släppts genom att jämföra nuvarande och tidigare knapptillstånd

buttonPressedReleased ({b, eventType}) {

this.update () if (b <this.currentButtons.length) {if (eventType == 1) {// note: detta kommer att vara en sträng, så det är bättre att jämföra det med 1 än att behandla det som en booleskt om (this.currentButtons &&! this.previousButtons ) {return true}} else {if (! this.currentButtons && this.previousButtons ) {return true}}} return false Och slutligen lägger vi till vår magiska tilläggsregistreringskod efter att ha definierat klassen

(funktion () {

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

Du kan få hela koden här.

Steg 7: Använda en osandboxad förlängning

Använda en osandboxad tillägg
Använda en osandboxad tillägg

Återigen, värd din tillägg någonstans, och den här gången ladda den med load_plugin = istället för url = argument till SheepTesters Scratch -mod. Till exempel, för min enkla Gamepad -mod, gå till:

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

(Förresten, om du vill ha en mer sofistikerad gamepad, tar du bara bort "enkel" från ovanstående URL, så får du mullrande och analog axelstöd.)

Återigen ska tillägget visas på vänstra sidan av din Scratch -redigerare. Ovan är ett mycket enkelt Scratch -program som säger "hej" när du trycker på knapp 0 och "hejdå" när du släpper det.

Steg 8: Dubbla kompatibilitet och hastighet

Jag har märkt att förlängningsblock kör en storleksordning snabbare med den laddningsmetod jag använde för osandade tillägg. Så om du inte bryr dig om säkerhetsfördelarna med att köra i en Web Worker -sandlåda, kommer din kod att gynnas av att laddas med argumentet? Load_plugin = URL till SheepTesters mod.

Du kan göra en sandlåda -förlängning kompatibel med båda laddningsmetoderna genom att använda följande kod efter att du har definierat tilläggsklassen (ändra CLASSNAME till namnet på din tilläggsklass):

(funktion () {

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

Rekommenderad: