Mục lục:

Tiện ích mở rộng Scratch 3.0: 8 bước
Tiện ích mở rộng Scratch 3.0: 8 bước

Video: Tiện ích mở rộng Scratch 3.0: 8 bước

Video: Tiện ích mở rộng Scratch 3.0: 8 bước
Video: [TIN HỌC 8] BÀI THỰC HÀNH 5 | lập trình Scratch 3.0 2024, Tháng bảy
Anonim
Tiện ích mở rộng Scratch 3.0
Tiện ích mở rộng Scratch 3.0

Phần mở rộng Scratch là các đoạn mã Javascript thêm các khối mới vào Scratch. Mặc dù Scratch được đóng gói với một loạt các tiện ích mở rộng chính thức, nhưng không có cơ chế chính thức để thêm các tiện ích mở rộng do người dùng tạo.

Khi tôi tạo tiện ích mở rộng kiểm soát Minecraft cho Scratch 3.0, tôi cảm thấy rất khó để bắt đầu. Có thể hướng dẫn này thu thập thông tin cùng nhau từ nhiều nguồn khác nhau (đặc biệt là nguồn này), cộng với một số điều tôi tự khám phá ra.

Bạn cần biết cách lập trình bằng Javascript và cách lưu trữ Javascript của bạn trên một trang web. Đối với phần sau, tôi đề xuất Trang GitHub.

Bí quyết chính là sử dụng mod Scratch của SheepTester, cho phép bạn tải các phần mở rộng và plugin.

Có thể hướng dẫn này sẽ hướng dẫn bạn thực hiện hai phần mở rộng:

  • Tìm nạp: tải dữ liệu từ một URL và trích xuất các thẻ JSON, chẳng hạn như để tải dữ liệu thời tiết
  • SimpleGamepad: sử dụng bộ điều khiển trò chơi trong Scratch (phiên bản phức tạp hơn có ở đây).

Bước 1: Hai loại tiện ích mở rộng

Có hai loại tiện ích mở rộng mà tôi sẽ gọi là "không có hộp cát" và "có hộp cát". Các tiện ích mở rộng hộp cát chạy dưới dạng Nhân viên web và do đó có những hạn chế đáng kể:

  • Web worker không thể truy cập các hình cầu trong đối tượng window (thay vào đó, chúng có một đối tượng global self, bị hạn chế hơn nhiều), vì vậy bạn không thể sử dụng chúng cho những thứ như truy cập gamepad.
  • Tiện ích mở rộng hộp cát không có quyền truy cập vào đối tượng thời gian chạy Scratch.
  • Các tiện ích mở rộng hộp cát chậm hơn nhiều.
  • Thông báo lỗi bảng điều khiển Javascript cho các tiện ích mở rộng hộp cát khó hiểu hơn trong Chrome.

Mặt khác:

  • Sử dụng tiện ích mở rộng hộp cát của người khác an toàn hơn.
  • Các tiện ích mở rộng hộp cát có nhiều khả năng hoạt động hơn với bất kỳ hỗ trợ tải tiện ích mở rộng chính thức cuối cùng nào.
  • Các tiện ích mở rộng hộp cát có thể được kiểm tra mà không cần tải lên máy chủ web bằng cách mã hóa thành dữ liệu: // URL.

Các tiện ích mở rộng chính thức (chẳng hạn như Âm nhạc, Bút, v.v.) đều không có hộp cát. Hàm tạo cho tiện ích mở rộng lấy đối tượng thời gian chạy từ Scratch và cửa sổ hoàn toàn có thể truy cập được.

Tiện ích mở rộng Tìm nạp được đóng hộp cát, nhưng Gamepad cần có đối tượng điều hướng từ cửa sổ.

Bước 2: Viết tiện ích mở rộng hộp cát: Phần I

Để tạo tiện ích mở rộng, bạn tạo một lớp mã hóa thông tin về nó, sau đó thêm một chút mã để đăng ký tiện ích mở rộng.

Điều chính trong lớp mở rộng là một phương thức getInfo () trả về một đối tượng với các trường bắt buộc:

  • id: tên nội bộ của tiện ích mở rộng, phải là duy nhất cho mỗi tiện ích mở rộng
  • name: tên thân thiện của tiện ích mở rộng, hiển thị trong danh sách các khối của Scratch
  • khối: danh sách các đối tượng mô tả khối tùy chỉnh mới.

Và có một trường menu tùy chọn không được sử dụng trong Tìm nạp nhưng sẽ được sử dụng trong Gamepad.

Vì vậy, đây là mẫu cơ bản cho Tìm nạp:

lớp ScratchFetch {

constructor () {} getInfo () {return {"id": "Fetch", "name": "Fetch", "blocks": [/* thêm sau * /]}} / * thêm phương thức cho các khối * /} Scratch.extensions.register (ScratchFetch mới ())

Bước 3: Viết tiện ích mở rộng hộp cát: Phần II

Bây giờ, chúng ta cần tạo danh sách các khối trong đối tượng của getInfo (). Mỗi khối cần ít nhất bốn trường sau:

  • opcode: đây là tên của phương thức được gọi để thực hiện công việc của khối
  • blockType: đây là kiểu khối; những cái phổ biến nhất cho tiện ích mở rộng là:

    • "command": thực hiện điều gì đó nhưng không trả về giá trị
    • "report": trả về một chuỗi hoặc số
    • "Boolean": trả về boolean (lưu ý viết hoa)
    • "hat": khối bắt sự kiện; nếu mã Scratch của bạn sử dụng khối này, thời gian chạy Scratch thường xuyên thăm dò phương thức được liên kết trả về một boolean để cho biết liệu sự kiện đã xảy ra hay chưa
  • text: đây là một mô tả thân thiện của khối, với các đối số trong dấu ngoặc vuông, ví dụ: "tìm nạp dữ liệu từ "
  • đối số: đây là một đối tượng có một trường cho mọi đối số (ví dụ: "url" trong ví dụ trên); đối tượng này lần lượt có các trường sau:

    • loại: "chuỗi" hoặc "số"
    • defaultValue: giá trị mặc định được điền trước.

Ví dụ: đây là trường khối trong tiện ích Tìm nạp của tôi:

"khối": [{"opcode": "fetchURL", "blockType": "launcher", "text": "tìm nạp dữ liệu từ ", "đối số": {"url": {"type": "string", "defaultValue ":" https://api.weather.gov/stations/KNYC/observations "},}}, {" opcode ":" jsonExtract "," blockType ":" report "," text ":" extract [name] từ [data] "," đối số ": {" name ": {" type ":" string "," defaultValue ":" nhiệt độ "}," data ": {" type ":" string "," defaultValue ": '{"nhiệt độ": 12.3}'},}},]

Ở đây, chúng tôi đã xác định hai khối: fetchURL và jsonExtract. Cả hai đều là phóng viên. Đầu tiên lấy dữ liệu từ một URL và trả về nó, và thứ hai trích xuất một trường từ dữ liệu JSON.

Cuối cùng, bạn cần bao gồm các phương thức cho hai khối. Mỗi phương thức lấy một đối tượng làm đối số, với đối tượng bao gồm các trường cho tất cả các đối số. Bạn có thể giải mã chúng bằng cách sử dụng dấu ngoặc nhọn trong các đối số. Ví dụ, đây là một ví dụ đồng bộ:

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") trả về nếu (t == "boolean") trả về t? 1: 0 return JSON.stringify (out)} else {return ""}}

Mã kéo trường tên từ dữ liệu JSON. Nếu trường chứa chuỗi, số hoặc boolean, chúng tôi trả về giá trị đó. Nếu không, chúng tôi sẽ JSONify lại trường. Và chúng tôi trả về một chuỗi trống nếu thiếu tên trong JSON.

Tuy nhiên, đôi khi bạn có thể muốn tạo một khối sử dụng API không đồng bộ. Phương thức fetchURL () sử dụng API tìm nạp không đồng bộ. Trong trường hợp như vậy, bạn nên trả về một lời hứa từ phương thức của bạn. Ví dụ:

fetchURL ({url}) {

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

Đó là nó. Phần mở rộng đầy đủ là ở đây.

Bước 4: Sử dụng Tiện ích mở rộng hộp cát

Sử dụng Tiện ích mở rộng Hộp cát
Sử dụng Tiện ích mở rộng Hộp cát
Sử dụng Tiện ích mở rộng Hộp cát
Sử dụng Tiện ích mở rộng Hộp cát
Sử dụng Tiện ích mở rộng Hộp cát
Sử dụng Tiện ích mở rộng Hộp cát

Có hai cách sử dụng tiện ích mở rộng hộp cát. Đầu tiên, bạn có thể tải nó lên máy chủ web, sau đó tải nó vào mod SheepTester's Scratch. Thứ hai, bạn có thể mã hóa nó thành một URL dữ liệu và tải nó vào trong mod Scratch. Tôi thực sự sử dụng phương pháp thứ hai khá nhiều để thử nghiệm, vì nó tránh được những lo lắng về việc các phiên bản cũ của tiện ích mở rộng được máy chủ lưu vào bộ nhớ cache. Lưu ý rằng mặc dù bạn có thể lưu trữ javascript từ Trang Github, nhưng bạn không thể làm như vậy trực tiếp từ kho lưu trữ github thông thường.

Fetch.js của tôi được lưu trữ tại https://arpruss.github.io/fetch.js. Hoặc bạn có thể chuyển đổi tiện ích mở rộng của mình thành URL dữ liệu bằng cách tải nó lên đây và sau đó sao chép nó vào khay nhớ tạm. URL dữ liệu là một URL khổng lồ chứa toàn bộ tệp trong đó.

Chuyển đến bản mod SheepTester's Scratch. Nhấp vào nút Thêm tiện ích mở rộng ở góc dưới bên trái. Sau đó nhấp vào "Chọn tiện ích mở rộng", và nhập URL của bạn (bạn có thể dán toàn bộ URL dữ liệu khổng lồ nếu muốn).

Nếu mọi việc suôn sẻ, bạn sẽ có một mục nhập cho tiện ích mở rộng của mình ở bên trái màn hình Scratch. Nếu mọi thứ không suôn sẻ, bạn nên mở bảng điều khiển Javascript của mình (shift-ctrl-J trong Chrome) và cố gắng gỡ lỗi sự cố.

Ở trên, bạn sẽ tìm thấy một số mã ví dụ tìm nạp và phân tích cú pháp dữ liệu JSON từ trạm KNYC (ở New York) của Cơ quan Thời tiết Quốc gia Hoa Kỳ và hiển thị nó, đồng thời xoay sprite đối mặt với cùng một cách mà gió đang thổi. Cách tôi thực hiện là tìm nạp dữ liệu vào trình duyệt web, sau đó tìm ra các thẻ. Nếu bạn muốn thử một trạm thời tiết khác, hãy nhập mã zip gần đó vào hộp tìm kiếm tại weather.gov và trang thời tiết cho vị trí của bạn sẽ cung cấp cho bạn mã trạm bốn chữ cái, bạn có thể sử dụng mã này thay cho KNYC trong mã số.

Bạn cũng có thể bao gồm tiện ích mở rộng hộp cát của mình ngay trong URL cho mod của SheepTester bằng cách thêm đối số "? Url =". Ví dụ:

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

Bước 5: Viết tiện ích mở rộng không có hộp cát: Giới thiệu

Hàm tạo của một tiện ích mở rộng không có hộp cát được truyền vào một đối tượng Thời gian chạy. Bạn có thể bỏ qua nó hoặc sử dụng nó. Một công dụng của đối tượng Runtime là sử dụng thuộc tính currentMSecs của nó để đồng bộ hóa các sự kiện ("khối mũ"). Theo như tôi có thể nói, tất cả các opcode của khối sự kiện đều được thăm dò thường xuyên và mỗi vòng thăm dò có một giá trị currentMSecs duy nhất. Nếu bạn cần đối tượng Runtime, bạn có thể sẽ bắt đầu tiện ích mở rộng của mình bằng:

lớp học EXTENSIONCLASS {

hàm tạo (thời gian chạy) {this.runtime = thời gian chạy…}…}

Tất cả những thứ đối tượng cửa sổ tiêu chuẩn có thể được sử dụng trong phần mở rộng không có hộp cát. Cuối cùng, tiện ích mở rộng không có hộp cát của bạn sẽ kết thúc bằng đoạn mã ma thuật này:

(hàm số() {

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

nơi bạn nên thay thế EXTENSIONCLASS bằng lớp của tiện ích mở rộng của bạn.

Bước 6: Viết tiện ích mở rộng không có hộp cát: Gamepad đơn giản

Bây giờ chúng ta hãy tạo một phần mở rộng gamepad đơn giản cung cấp một khối sự kiện duy nhất ("mũ") khi một nút được nhấn hoặc thả.

Trong mỗi chu kỳ bỏ phiếu của khối sự kiện, chúng tôi sẽ lưu dấu thời gian từ đối tượng thời gian chạy và trạng thái gamepad trước đó và hiện tại. Dấu thời gian được sử dụng để nhận biết liệu chúng tôi có một chu kỳ bỏ phiếu mới hay không. Vì vậy, chúng ta bắt đầu với:

lớp ScratchSimpleGamepad {

constructor (thời gian chạy) {this.runtime = thời gian chạy this.currentMSecs = -1 this.previousButtons = this.currentButtons = }…} Chúng tôi sẽ có một khối sự kiện, với hai đầu vào - một số nút và một menu để chọn xem chúng tôi muốn sự kiện kích hoạt khi nhấn hay thả. Vì vậy, đây là phương pháp của chúng tôi

nhận thông tin() {

return {"id": "SimpleGamepad", "name": "SimpleGamepad", "blocks": [{"opcode": "buttonPressedReleased", "blockType": "hat", "text": "button [eventType] "," đối số ": {" b ": {" type ":" number "," defaultValue ":" 0 "}," eventType ": {" type ":" number "," defaultValue ":" 1 "," menu ":" pressReleaseMenu "},},},]," menu ": {" pressReleaseMenu ": [{text:" press ", value: 1}, {text:" release ", value: 0}],}}; } Tôi nghĩ rằng các giá trị trong menu thả xuống vẫn được chuyển đến hàm opcode dưới dạng chuỗi, mặc dù được khai báo dưới dạng số. Vì vậy, hãy so sánh rõ ràng chúng với các giá trị được chỉ định trong menu nếu cần. Bây giờ chúng tôi viết một phương pháp cập nhật trạng thái nút bất cứ khi nào một chu kỳ bỏ phiếu sự kiện mới xảy ra

cập nhật() {

if (this.runtime.currentMSecs == this.currentMSecs) trả về // không phải là chu kỳ bỏ phiếu mới 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) { // số lượng nút khác nhau nên gamepad mới this.previousButtons = for (var i = 0; i <gamepad.buttons.length; i ++) this.previousButtons.push (false)} else {this.previousButtons = this. currentButtons} this.currentButtons = for (var i = 0; i <gamepad.buttons.length; i ++) this.currentButtons.push (gamepad.buttons .pressed)} Cuối cùng, chúng ta có thể triển khai khối sự kiện của mình, bằng cách gọi phương thức update () và sau đó kiểm tra xem nút cần thiết vừa được nhấn hoặc thả hay chưa, bằng cách so sánh trạng thái nút hiện tại và trước đó.

buttonPressedReleased ({b, eventType}) {

this.update () if (b <this.currentButtons.length) {if (eventType == 1) {// lưu ý: đây sẽ là một chuỗi, vì vậy tốt hơn nên so sánh nó với 1 hơn là coi nó như một Boolean if (this.currentButtons &&! this.previousButtons ) {return true}} else {if (! this.currentButtons && this.previousButtons ) {return true}}} return false} Và cuối cùng, chúng tôi thêm mã đăng ký tiện ích mở rộng kỳ diệu của mình sau khi xác định lớp

(hàm số() {

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

Bạn có thể lấy mã đầy đủ ở đây.

Bước 7: Sử dụng Tiện ích mở rộng không có hộp cát

Sử dụng Tiện ích mở rộng không có hộp cát
Sử dụng Tiện ích mở rộng không có hộp cát

Một lần nữa, hãy lưu trữ tiện ích mở rộng của bạn ở đâu đó và lần này tải nó bằng đối số load_plugin = chứ không phải url = cho mod Scratch của SheepTester. Ví dụ, đối với bản mod Gamepad đơn giản của tôi, hãy truy cập:

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

(Nhân tiện, nếu bạn muốn có một gamepad phức tạp hơn, chỉ cần xóa "đơn giản" khỏi URL ở trên, và bạn sẽ có hỗ trợ trục tương tự và ầm ầm.)

Một lần nữa, tiện ích mở rộng sẽ xuất hiện ở bên trái trình chỉnh sửa Scratch của bạn. Trên đây là một chương trình Scratch rất đơn giản nói "xin chào" khi bạn nhấn nút 0 và "tạm biệt" khi bạn thả nó ra.

Bước 8: Khả năng tương thích kép và tốc độ

Tôi nhận thấy rằng các khối tiện ích mở rộng chạy theo thứ tự cường độ nhanh hơn bằng cách sử dụng phương pháp tải mà tôi đã sử dụng cho các tiện ích mở rộng không có hộp cát. Vì vậy, trừ khi bạn quan tâm đến lợi ích bảo mật của việc chạy trong hộp cát Web Worker, mã của bạn sẽ được hưởng lợi khi được tải với đối số? Load_plugin = URL vào mod của SheepTester.

Bạn có thể tạo một tiện ích mở rộng hộp cát tương thích với cả hai phương pháp tải bằng cách sử dụng mã sau sau khi xác định lớp tiện ích mở rộng (thay đổi CLASSNAME thành tên của lớp tiện ích mở rộng của bạn):

(hàm số() {

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

Đề xuất: