Съдържание:

Разширения за Scratch 3.0: 8 стъпки
Разширения за Scratch 3.0: 8 стъпки

Видео: Разширения за Scratch 3.0: 8 стъпки

Видео: Разширения за Scratch 3.0: 8 стъпки
Видео: Scratch уроки, как стать программистом | Урок 2 | TutorOnline 2024, Юли
Anonim
Разширения за Scratch 3.0
Разширения за Scratch 3.0

Разширенията за скреч са части от код на Javascript, които добавят нови блокове към Scratch. Докато Scratch е в комплект с куп официални разширения, няма официален механизъм за добавяне на потребителски разширения.

Когато правех моето Minecraft контролно разширение за Scratch 3.0, ми беше трудно да започна. Тази инструкция събира заедно информация от различни източници (особено тази), плюс няколко неща, които открих сам.

Трябва да знаете как да програмирате в Javascript и как да хоствате вашия Javascript на уебсайт. За последното препоръчвам GitHub Pages.

Основният трик е да използвате модела на SheepTester на Scratch, който ви позволява да зареждате разширения и приставки.

Тази инструкция ще ви води през две разширения:

  • Извличане: зареждане на данни от URL адрес и извличане на JSON тагове, например за зареждане на метеорологични данни
  • SimpleGamepad: използване на контролер за игри в Scratch (тук е по -сложна версия).

Стъпка 1: Два вида разширения

Има два вида разширения, които ще нарека „несанкционирани“и „изолирани“. Разширенията в изолирана среда се изпълняват като уеб работници и в резултат имат значителни ограничения:

  • Уеб работниците нямат достъп до глобалите в прозоречния обект (вместо това те имат глобален самостоятелен обект, който е много по -ограничен), така че не можете да ги използвате за неща като достъп до геймпад.
  • Разширенията в пясъчна кутия нямат достъп до обекта по време на изпълнение на Scratch.
  • Разширенията в пясъчна кутия са много по -бавни.
  • Съобщенията за грешки в конзолата на Javascript за разширения в изолирана среда са по -загадъчни в Chrome.

От друга страна:

  • Използването на разширения в пясъчна кутия на други хора е по -безопасно.
  • Разширенията в пясъчна кутия са по -склонни да работят с евентуална официална поддръжка за зареждане на разширения.
  • Разширенията в пясъчна кутия могат да бъдат тествани без качване на уеб сървър чрез кодиране в URL адрес за данни: //.

Официалните разширения (като музика, писалка и т.н.) са без печат. Конструкторът за разширението получава обекта по време на работа от Scratch и прозорецът е напълно достъпен.

Разширението Fetch е в пясъчна кутия, но геймпадът се нуждае от навигационния обект от прозореца.

Стъпка 2: Писане на разширение в пясъчна кутия: Част I

За да направите разширение, създавате клас, който кодира информация за него, и след това добавяте малко код, за да регистрирате разширението.

Основното в класа на разширението е метод getInfo (), който връща обект с задължителните полета:

  • id: вътрешното име на разширението трябва да е уникално за всяко разширение
  • name: приятелското име на разширението, показващо се в списъка с блокове на Scratch
  • блокове: списък на обекти, описващи новия персонализиран блок.

Има и допълнително поле от менюта, което не се използва във Fetch, но ще се използва в Gamepad.

И така, ето основният шаблон за Fetch:

клас ScratchFetch {

constructor () {} getInfo () {return {"id": "Извличане", "name": "Извличане", "блокове": [/* добавяне по -късно * /]}} / * добавяне на методи за блокове * /} Scratch.extensions.register (нов ScratchFetch ())

Стъпка 3: Писане на разширение в пясъчна кутия: Част II

Сега трябва да създадем списъка с блокове в обекта на getInfo (). Всеки блок се нуждае от поне тези четири полета:

  • opcode: това е името на метода, който се извиква да свърши работата на блока
  • blockType: това е типът блок; най -често срещаните за разширения са:

    • "команда": прави нещо, но не връща стойност
    • "репортер": връща низ или число
    • "Boolean": връща булева стойност (обърнете внимание на главни букви)
    • "hat": блок за улавяне на събития; ако вашият Scratch код използва този блок, средата на Scratch редовно анкетира свързания метод, който връща булева стойност, за да каже дали събитието се е случило
  • текст: това е приятелско описание на блока, с аргументите в скоби, например "извличане на данни от "
  • аргументи: това е обект, който има поле за всеки аргумент (например "url" в горния пример); този обект от своя страна има следните полета:

    • тип: "низ" или "номер"
    • defaultValue: стойността по подразбиране за предварително попълване.

Например, тук е полето за блокове в моето разширение Fetch:

"блокове": [{"opcode": "fetchURL", "blockType": "reporter", "text": "извличане на данни от ", "arguments": {"url": {"type": "string", "defaultValue ":" https://api.weather.gov/stations/KNYC/observations "},}}, {" opcode ":" jsonExtract "," blockType ":" репортер "," text ":" извличане [име] от [данни] "," аргументи ": {" име ": {" тип ":" низ "," defaultValue ":" температура "}," data ": {" type ":" string "," defaultValue ": '{"температура": 12.3}'},}},]

Тук дефинирахме два блока: fetchURL и jsonExtract. И двамата са репортери. Първият извлича данни от URL адрес и ги връща, а вторият извлича поле от данните на JSON.

И накрая, трябва да включите методите за два блока. Всеки метод приема обект като аргумент, като обектът включва полета за всички аргументи. Можете да ги декодирате с помощта на фигурни скоби в аргументите. Например, ето един синхронен пример:

jsonExtract ({name, data}) {

var parsed = JSON.parse (data) if (name in parsed) {var out = parsed [name] var t = typeof (out) if (t == "string" || t == "number") връщане, ако (t == "boolean") връщане t? 1: 0 връщане JSON.stringify (out)} else {return ""}}

Кодът извлича полето за име от данните в JSON. Ако полето съдържа низ, число или булева стойност, ние го връщаме. В противен случай ние отново JSONify полето. И връщаме празен низ, ако името липсва в JSON.

Понякога обаче може да искате да направите блок, който използва асинхронен API. Методът fetchURL () използва API за извличане, който е асинхронен. В такъв случай трябва да върнете обещание от вашия метод, който върши работата. Например:

fetchURL ({url}) {

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

Това е. Пълното разширение е тук.

Стъпка 4: Използване на разширение в пясъчна кутия

Използване на разширение в изолирана среда
Използване на разширение в изолирана среда
Използване на разширение в изолирана среда
Използване на разширение в изолирана среда
Използване на разширение в изолирана среда
Използване на разширение в изолирана среда

Има два начина за използване на разширение в пясъчна кутия. Първо можете да го качите на уеб сървър и след това да го заредите в модела на SheepTester's Scratch. Второ, можете да го кодирате в URL адрес на данни и да го заредите в модела Scratch. Всъщност използвам втория метод доста за тестване, тъй като избягва притесненията, че по -старите версии на разширението се кешират от сървъра. Обърнете внимание, че макар да можете да хоствате javascript от Github Pages, не можете да го направите директно от обикновено хранилище на github.

Моят fetch.js се хоства на https://arpruss.github.io/fetch.js. Или можете да конвертирате разширението си в URL адрес на данни, като го качите тук и след това го копирате в клипборда. URL адресът за данни е гигантски URL адрес, който съдържа цял файл в него.

Отидете на мода Scratch на SheepTester. Кликнете върху бутона Добавяне на разширение в долния ляв ъгъл. След това кликнете върху „Изберете разширение“и въведете своя URL адрес (можете да поставите целия гигантски URL адрес на данни, ако желаете).

Ако всичко е наред, ще имате запис за разширението си в лявата част на екрана Scratch. Ако нещата не вървят добре, трябва да отворите конзолата си Javascript (shift-ctrl-J в Chrome) и да опитате да отстраните грешките.

По -горе ще намерите примерен код, който извлича и анализира JSON данни от станцията KNYC (в Ню Йорк) на Националната метеорологична служба на САЩ и го показва, като същевременно завърта спрайта с лице по същия начин, по който духа вятърът. Начинът, по който го направих, беше като изтеглих данните в уеб браузър и след това разбрах маркерите. Ако искате да опитате различна метеорологична станция, въведете близкия пощенски код в полето за търсене на weather.gov и страницата за времето за вашето местоположение трябва да ви даде четирибуквен код на станция, който можете да използвате вместо KNYC в код.

Можете също така да включите вашето разширение в пясъчна кутия направо в URL адреса на модела на SheepTester, като добавите аргумент „? Url =“. Например:

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

Стъпка 5: Писане на разширение без кутия: Въведение

Конструкторът на разширение без скрийншот преминава през обект по време на изпълнение. Можете да го игнорирате или да го използвате. Едно използване на обекта Runtime е да се използва неговото свойство currentMSecs за синхронизиране на събития ("блокове на шапки"). Доколкото мога да разбера, всички опкодове на блока на събитията се анкетират редовно и всеки кръг от анкетата има една стойност currentMSecs. Ако имате нужда от обект Runtime, вероятно ще започнете разширението си с:

клас EXTENSIONCLASS {

конструктор (време на изпълнение) {this.runtime = време на изпълнение …}…}

Всички стандартни неща за прозоречни обекти могат да се използват в разширението без печат. И накрая, вашето незапечатано разширение трябва да завърши с този малко вълшебен код:

(function () {

var extensionInstance = нов EXTENSIONCLASS (window.vm.extensionManager.runtime) var serviceName = window.vm.extensionManager._registerInternalExtension (extensionInstance) window.vm.extensionManager._loadedExtensions.set (extensionInstance.getInfo ().), id на услугата)

където трябва да замените EXTENSIONCLASS с класа на вашето разширение.

Стъпка 6: Писане на разширение без кутия: Прост геймпад

Нека сега направим просто разширение за геймпад, което осигурява единично събитие ("шапка"), когато бутонът е натиснат или освободен.

По време на всеки цикъл на опросване на блок от събития, ние ще запазваме времева отметка от обекта по време на изпълнение, както и предишното и текущото състояние на геймпада. Времевата марка се използва за разпознаване дали имаме нов цикъл на изпитване. И така, започваме с:

клас ScratchSimpleGamepad {

конструктор (време на изпълнение) {this.runtime = време на изпълнение this.currentMSecs = -1 this.previousButtons = this.currentButtons = }…} Ще имаме един блок със събития, с два входа-номер на бутон и меню, за да изберем дали искаме събитието да се задейства при натискане или освобождаване. И така, ето нашия метод

getInfo () {

return {"id": "SimpleGamepad", "name": "SimpleGamepad", "Blocks": [{"opcode": "buttonPressedReleased", "blockType": "hat", "text": "button [eventType] "," аргументи ": {" b ": {" type ":" number "," defaultValue ":" 0 "}," eventType ": {" type ":" number "," defaultValue ":" 1 "," menu ":" pressReleaseMenu "},},},]," menus ": {" pressReleaseMenu ": [{text:" press ", стойност: 1}, {text:" release ", стойност: 0}],}}; } Мисля, че стойностите в падащото меню все още се предават на функцията opcode като низове, въпреки че са декларирани като числа. Затова изрично ги сравнете със стойностите, посочени в менюто, ако е необходимо. Сега пишем метод, който актуализира състоянията на бутоните, когато се случи нов цикъл на опросване на събития

update () {

if (this.runtime.currentMSecs == this.currentMSecs) return // не е нов цикъл на опросване this.currentMSecs = this.runtime.currentMSecs var gamepads = navigator.getGamepads () if (gamepads == null || gamepads.length = = 0 || геймпади [0] == null) {this.previousButtons = this.currentButtons = return} var gamepad = геймпади [0] if (gamepad.buttons.length! = This.previousButtons.length) { // различен брой бутони, така че новият геймпад this.previousButtons = за (var i = 0; i <gamepad.buttons.length; i ++) this.previousButtons.push (false)} else {this.previousButtons = this. currentButtons} this.currentButtons = за (var i = 0; i <gamepad.buttons.length; i ++) this.currentButtons.push (gamepad.buttons .pressed)} И накрая, можем да реализираме нашия блок със събития, като извикаме метода update () и след това проверим дали необходимия бутон е току -що натиснат или освободен, като сравним текущото и предишното състояние на бутона

buttonPressedReleased ({b, eventType}) {

this.update () if (b <this.currentButtons.length) {if (eventType == 1) {// бележка: това ще бъде низ, така че е по -добре да го сравните с 1, отколкото да го третирате като булев ако ((this.currentButtons &&! this.previousButtons ) {return true}} else {if (! this.currentButtons && this.previousButtons ) {return true}}} return false} И накрая добавяме нашия магически регистрационен код за разширение след дефиниране на класа

(function () {

var extensionInstance = нов ScratchSimpleGamepad (window.vm.extensionManager.runtime) var serviceName = window.vm.extensionManager._registerInternalExtension (extensionInstance) window.vm.extensionManager._loadedExtensions.set (extensionInstance.getInmea)))

Можете да получите пълния код тук.

Стъпка 7: Използване на разширение без кутия

Използване на разширение без кутия
Използване на разширение без кутия

За пореден път хоствайте разширението си някъде и този път го заредите с load_plugin = вместо с url = аргумент към мода Scratch на SheepTester. Например, за моя прост режим на Gamepad, отидете на:

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

(Между другото, ако искате по -усъвършенстван геймпад, просто премахнете „simple“от горния URL адрес и ще имате поддръжка на грохот и аналогова ос.)

Отново разширението трябва да се появи от лявата страна на редактора на Scratch. По -горе е много проста Scratch програма, която казва „здравей“, когато натиснете бутон 0 и „сбогом“, когато го пуснете.

Стъпка 8: Двойна съвместимост и скорост

Забелязал съм, че блоковете за разширения се изпълняват с порядък по -бързо, като се използва методът на зареждане, който използвах за разширения без печат. Така че, освен ако не ви е грижа за ползите за сигурността при работа в пясъчник на Web Worker, вашият код ще се възползва от зареждането с аргумента? Load_plugin = URL към модела на SheepTester.

Можете да направите разширение в пясъчна кутия, съвместимо с двата метода на зареждане, като използвате следния код след дефиниране на класа на разширението (променете CLASSNAME на името на вашия клас на разширение):

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

Препоръчано: