Оглавление:
- Шаг 1. Два типа расширений
- Шаг 2: написание изолированного расширения: часть I
- Шаг 3. Написание изолированного расширения: часть II
- Шаг 4. Использование изолированного расширения
- Шаг 5. Написание расширения без тестовой среды: введение
- Шаг 6. Написание расширения без тестовой среды: простой геймпад
- Шаг 7. Использование расширения без тестовой среды
- Шаг 8: двойная совместимость и скорость
Видео: Расширения Scratch 3.0: 8 шагов
2024 Автор: John Day | [email protected]. Последнее изменение: 2024-01-30 11:47
Расширения 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)}}) ()
Рекомендуемые:
Блок расширения джойстика для клавиатуры Arduino и устройство звукового контроллера с использованием Deej: 8 шагов
Arduino Keyboard Joystick Extender Box и Sound Controller Thing с использованием Deej: Почему Некоторое время я хотел добавить к моей клавиатуре небольшой джойстик для управления элементами интерфейса или других небольших задач в играх и симуляторах (MS Flight Sim, Elite: Dangerous, Звездные войны: эскадрильи и т. Д.). Кроме того, для Elite: Dangerous я был
Использование расширения "Сундук надежды" для поиска незавершенных храмовых таинств в вашем семейном древе в семейном поиске: 11 шагов
Использование расширения "Сундук надежды" для поиска незавершенных храмовых таинств в вашем семейном древе по семейному поиску: цель этого руководства - продемонстрировать, как искать в семейном древе в Семейном поиске предков с незавершенными храмовыми таинствами с помощью расширения "Сундук надежды". Использование Сундука Надежды может значительно ускорить поиск
Универсальная печатная плата расширения ввода / вывода для управления многими лампами Nixie с помощью I2C: 9 шагов (с изображениями)
Универсальная печатная плата расширителя ввода / вывода для управления многими лампами Nixie с помощью I2C: В настоящее время существует большой интерес к воплощению в жизнь старинных ламп Nixie. На рынке доступно множество комплектов часов с трубкой никси. Оказалось, что даже шла оживленная торговля старинными русскими никси-лампами. Также здесь, на Instructables, там
Разработка печатной платы расширения (Intel® IoT): 20 шагов
Проектирование печатной платы расширения (Intel® IoT): это руководство было написано в качестве отправной точки для тех, кто стремится максимально использовать потенциал Intel® Edison, включив его в полностью разработанный проект встраиваемых систем. Для этого вам, скорее всего, потребуется сделать - как называет Intel®
Самодельная плата расширения Raspberry Pi на заказ: 8 шагов
Самодельная плата расширения Raspberry Pi: с 2015 года я улучшаю этот замечательный проект, чтобы в моей машине был почти неограниченный настраиваемый медиацентр. Однажды я решил организовать там провода с помощью изготовленной на заказ самодельной печатной платы. Фотографии выше находятся на стадии широкого прототипа, так что