Оглавление:

Расширения Scratch 3.0: 8 шагов
Расширения Scratch 3.0: 8 шагов

Видео: Расширения Scratch 3.0: 8 шагов

Видео: Расширения Scratch 3.0: 8 шагов
Видео: Урок по Scratch - Циклы - для начинающих 2024, Ноябрь
Anonim
Расширения Scratch 3.0
Расширения Scratch 3.0

Расширения Scratch - это фрагменты кода Javascript, которые добавляют новые блоки в Scratch. Хотя Scratch связан с множеством официальных расширений, официального механизма для добавления пользовательских расширений нет.

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

Вам нужно знать, как программировать на Javascript и как разместить свой Javascript на веб-сайте. Для последнего я рекомендую GitHub Pages.

Основная хитрость заключается в использовании мода Scratch от SheepTester, который позволяет загружать расширения и плагины.

Это руководство поможет вам создать два расширения:

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

Шаг 1. Два типа расширений

Есть два типа расширений, которые я назову «изолированными» и «изолированными». Изолированные расширения работают как веб-воркеры и, как следствие, имеют значительные ограничения:

  • Веб-воркеры не могут получить доступ к глобальным объектам в объекте окна (вместо этого у них есть глобальный объект self, который гораздо более ограничен), поэтому вы не можете использовать их для таких вещей, как доступ к геймпаду.
  • Изолированные расширения не имеют доступа к объекту среды выполнения Scratch.
  • Изолированные расширения работают намного медленнее.
  • Сообщения об ошибках консоли Javascript для изолированных расширений более загадочны в Chrome.

С другой стороны:

  • Безопаснее использовать чужие расширения для песочницы.
  • Изолированные расширения с большей вероятностью будут работать с любой возможной официальной поддержкой загрузки расширений.
  • Изолированные расширения можно протестировать без загрузки на веб-сервер путем кодирования в URL-адрес data: //.

Официальные расширения (такие как Музыка, Ручка и т. Д.) Не изолированы от тестовой среды. Конструктор расширения получает объект среды выполнения из Scratch, и окно полностью доступно.

Расширение Fetch изолировано, но для Gamepad требуется объект навигатора из окна.

Шаг 2: написание изолированного расширения: часть I

Чтобы создать расширение, вы создаете класс, который кодирует информацию о нем, а затем добавляете немного кода для регистрации расширения.

Главное в классе расширения - это метод getInfo (), который возвращает объект с обязательными полями:

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

И есть дополнительное поле меню, которое не используется в Fetch, но будет использоваться в Gamepad.

Итак, вот базовый шаблон для Fetch:

class ScratchFetch {

constructor () {} getInfo () {return {"id": "Fetch", "name": "Fetch", "blocks": [/* добавить позже * /]}} / * добавить методы для блоков * /} Scratch.extensions.register (новый ScratchFetch ())

Шаг 3. Написание изолированного расширения: часть II

Теперь нам нужно создать список блоков в объекте getInfo (). Каждому блоку нужны как минимум эти четыре поля:

  • opcode: это имя метода, который вызывается для работы блока
  • blockType: это тип блока; наиболее распространенными для расширений являются:

    • «команда»: что-то делает, но не возвращает значение
    • «репортер»: возвращает строку или число
    • "Boolean": возвращает логическое значение (обратите внимание на заглавные буквы).
    • «шляпа»: блок захвата событий; если ваш код 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 ":" reporter "," text ":" extract [имя] from [data] "," arguments ": {" name ": {" type ":" string "," defaultValue ":" temperature "}," data ": {" type ":" string "," defaultValue ": '{"temperature": 12.3}'},}},]

Здесь мы определили два блока: fetchURL и jsonExtract. Оба репортеры. Первый извлекает данные из URL-адреса и возвращает их, а второй извлекает поле из данных JSON.

Наконец, вам нужно включить методы для двух блоков. Каждый метод принимает объект в качестве аргумента, причем объект включает поля для всех аргументов. Вы можете расшифровать их, используя фигурные скобки в аргументах. Например, вот один синхронный пример:

jsonExtract ({имя, данные}) {

var parsed = JSON.parse (data) if (name in parsed) {var out = parsed [name] var t = typeof (out) if (t == "string" || t == "number") возвращается, если (t == "логическое") вернуть t? 1: 0 return JSON.stringify (out)} else {return ""}}

Код извлекает поле имени из данных JSON. Если поле содержит строку, число или логическое значение, мы возвращаем это. В противном случае мы повторно JSONify поля. И мы возвращаем пустую строку, если имя отсутствует в JSON.

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

fetchURL ({url}) {

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

Вот и все. Полное расширение здесь.

Шаг 4. Использование изолированного расширения

Использование изолированного расширения
Использование изолированного расширения
Использование изолированного расширения
Использование изолированного расширения
Использование изолированного расширения
Использование изолированного расширения

Есть два способа использования изолированного расширения. Сначала вы можете загрузить его на веб-сервер, а затем загрузить в мод SheepTester Scratch. Во-вторых, вы можете закодировать его в URL-адрес данных и загрузить его в мод Scratch. На самом деле я довольно часто использую второй метод для тестирования, так как он позволяет не беспокоиться о том, что старые версии расширения будут кэшированы сервером. Обратите внимание, что, хотя вы можете разместить javascript из страниц Github, вы не можете сделать это напрямую из обычного репозитория github.

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

Зайдите в мод SheepTester Scratch. Нажмите кнопку «Добавить расширение» в нижнем левом углу. Затем нажмите «Выбрать расширение» и введите свой 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, вы, вероятно, запустите свое расширение с:

class EXTENSIONCLASS {

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

В расширении без тестовой среды можно использовать все стандартные оконные объекты. Наконец, ваше расширение без тестовой среды должно заканчиваться этим кусочком магического кода:

(функция () {

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 на класс вашего расширения.

Шаг 6. Написание расширения без тестовой среды: простой геймпад

Теперь давайте создадим простое расширение для геймпада, которое предоставляет единственный блок события («шляпу»), когда кнопка нажата или отпущена.

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

class ScratchSimpleGamepad {

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

получить информацию() {

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 ", value: 1}, {text:" release ", value: 0}],}}; } Я думаю, что значения в раскрывающемся меню по-прежнему передаются функции кода операции в виде строк, несмотря на то, что они объявлены как числа. Поэтому при необходимости явно сравните их со значениями, указанными в меню. Теперь мы пишем метод, который обновляет состояния кнопок всякий раз, когда происходит новый цикл опроса событий.

Обновить() {

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 = gamepads [0] if (gamepad.buttons.length! = This.previousButtons.length) { // другое количество кнопок, поэтому новый геймпад this.previousButtons = for (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, чем рассматривать ее как логическое if (this.currentButtons &&! this.previousButtons ) {return true}} else {if (! this.currentButtons && this.previousButtons ) {return true}}} return false} И, наконец, мы добавляем регистрационный код нашего волшебного расширения после определения класса

(функция () {

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

Вы можете получить полный код здесь.

Шаг 7. Использование расширения без тестовой среды

Использование расширения без тестовой среды
Использование расширения без тестовой среды

Еще раз разместите где-нибудь свое расширение и на этот раз загрузите его с помощью аргумента load_plugin =, а не url = мода SheepTester Scratch. Например, для моего простого мода для геймпада перейдите по ссылке:

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

(Между прочим, если вам нужен более сложный геймпад, просто удалите слово «простой» из приведенного выше URL-адреса, и у вас будет поддержка грохота и аналоговой оси.)

Опять же, расширение должно появиться в левой части редактора Scratch. Выше очень простая программа Scratch, которая говорит «привет», когда вы нажимаете кнопку 0, и «до свидания», когда вы ее отпускаете.

Шаг 8: двойная совместимость и скорость

Я заметил, что блоки расширений выполняются на порядок быстрее при использовании метода загрузки, который я использовал для расширений без тестовой среды. Поэтому, если вы не заботитесь о преимуществах безопасности работы в изолированной программной среде Web Worker, ваш код выиграет от загрузки с аргументом? Load_plugin = URL для мода SheepTester.

Вы можете сделать изолированное расширение совместимым с обоими методами загрузки, используя следующий код после определения класса расширения (измените CLASSNAME на имя вашего класса расширения):

(функция () {

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

Рекомендуемые: