-----------------------------------------------------------------------------------------

Scratchの保存ファイル(*.sb3)の中にあるproject.jsonファイルにおけるJSONデータ構造解析メモ

-----------------------------------------------------------------------------------------

 

 

注:下記で第1レベル、第2レベルという表現はここで説明しやすいように便宜上設けたものにすぎず、実際のデータ構造の階層とはかならずしも一致していない。

 

<概略>

 

・エディタで「ファイル」→「コンピューターに保存する」で保存したファイルは、所定の保存先(ブラウザの設定による)に、「(プロジェクト名).sb3」の形で保存される。

 

・このファイルの中身を見るには、拡張子.sb3.zipに書き換える。すると、フォルダのアイコンに変わる。このフォルダを開くと、中にproject.jsonファイルがある。このファイルにプログラム等が格納されている。

 

project.jsonの中身はJSON形式のデータである。

 

・下記に示すように、第1レベルでは"targets""monitors""extensions""meta"があるが、最も中心となるのは"targets"である。ここに、ステージおよび各スプライト毎にまとまって、プログラムデータ等がある。

 

・エディタにある「コード(スクリプト)」「背景」「音」タグの情報は、それぞれ第2レベルの"blocks""costumes""sounds"に格納されている。

 

"blocks"には、連想配列の形でプログラム情報が格納されている。

 

"blocks"における連想配列の各要素が、エディタで見られるブロックに相当する。ただし、連想配列の要素の並び順は必ずしも、エディタで見られるブロックの並び順とは一致していない。また、1つのブロックが複数のブロックの組み合わせでできていた場合(演算子ブロックの組み合わせなど)、その内容は複数の要素に展開される。

 

・これらの要素間の順序および組み合わせの関係は、第4レベルで説明している"next":"parent":でつながっている。

 

・この解析結果を利用し、Scratchでバックツールとして、「sb3ファイル解析」のページを作成した。

    http://www.fujiu.gr.jp/scratch/sb3analyzer.html

 

 

(参考)JSONデータ整形ツール

https://tools.m-bsys.com/development_tooles/json-beautifier.php

(お世話になりました^_^

 

 

 

-------------------------------------------------------------

 

<解説>

 

 

XXXXとあるのは半角英数記号で、内部処理で使われる識別子。○○○は名前などで、日本語文字の場合もある。YYYYはオペコード(命令語)等で、英単語のイメージ。ここでは単に見やすくするために、XXXXや○○○やYYYYに書き換えて表記している。

なお、オペコード等はVer.3.0での基本機能を対象としており、オプション機能は今回対象としていない(調査中)。

 

 

 

(第1レベル)project.jsonファイルの最初のデータ構造

{

 "targets": [{・・・},{・・・},・・・],          ←配列の中に、ひとつはステージ、その他は各スプライトが並んでいる

 "monitors": [{・・・},{・・・},・・・],  ←画面表示する変数やリスト

 "extensions": [・・・],                  ←拡張機能?

 "meta": {・・・}                         ←バージョン、ブラウザ情報など

}

 

 

(第2レベル)"targets"の中の要素(ステージ/スプライト)のデータ構造

 "targets": [

       {                                  ←ステージの情報

              "isStage": true,            ←ステージはtrueになっている

              "name":"Stage"                     ←エディタで、ステージの名前は書き換えられない

              "variables": {・・・},             ←広域変数

              "lists": {・・・},          ←広域リスト

              "broadcasts": {・・・},            ←メッセージ

              "blocks": {・・・}          ←コード(スクリプト)。いわゆるプログラム。

              "comments": {},                    ←コメント

              "currentCostume": ・・・,   ←現在のコスチューム?

              "costumes": [・・・],              ←コスチューム

              "sounds": [・・・],         ←音

              "volume": ・・・,           ←音量

              "layerOrder": ・・・,              ←層

              "tempo": ・・・,            ←?(プログラム構造に直接関係ないので省略。以下同様)

              "videoTransparency": ・・・,       ←?

              "videoState": ・・・,              ←?

              "textToSpeechLanguage": ・・・     ←言語

       },

       {                                  ←スプライトの情報

              "isStage": false,           ←スプライト(ステージ以外)はfalseになっている

              "name": "○○○",           ←スプライト名

              "variables": {・・・},             ←ローカル変数

              "lists": {・・・},          ←ローカルリスト

              "broadcasts": {},           ←メッセージ。スプライト(ローカル)では定義しない??

              "blocks": {・・・},         ←コード(スクリプト)。いわゆるプログラム。

              "costumes": [・・・],              ←スプライトには「現在のコスチューム"currentCostume"」が無いが、それでいいのか??

              "sounds": [・・・],         ←音

              "volume": ・・・,           ←音量

              "layerOrder": ・・・,              ←層

              "visible": ・・・,          ←表示有無

              "x": ・・・,                x座標

              "y": ・・・,                y座標

              "size": ・・・,                    ←大きさ

              "direction": ・・・,        ←角度

              "draggable": ・・・,        ←マウスでの移動可否

              "rotationStyle": ・・・            ←回転方法

       }

]

 

 

(第3レベル) "targets"のところの各要素(ステージ/スプライト)の中の主な項目のデータ構造

 

       "variables": {"XXXX": ["○○○○",0]},    "XXXX"をキーとして、値部分は配列。最初に変数名("○○○○")、次にその内容。

 

       "lists": {"XXXX": ["○○○○",[]]},       "XXXX"をキーとして、値部分は配列。最初にリスト名("○○○○")、次にその内容(配列表記)。

 

       (ステージの場合)

       "broadcasts": {"XXXX": "○○○○"},       "XXXX"をキーとして、値部分にはメッセージ名がある。

       (スプライトの場合)

       "broadcasts": {},                  ←値は空。スプライトにはメッセージはない(ローカルのメッセージは無い)模様。

 

       "blocks": {"XXXX": {・・・},"XXXX": {・・・},・・・},   ←各要素が、ブロックに相当するイメージ

 

 

(第4レベル)"blocks"の中の、各要素(ブロックに相当)の構造

       "blocks": {

              "XXXX": {                   (トップのブロックの場合)

                     "opcode": "YYYY",    ←命令語。「旗が押されたら」などの英名

                     "next": "XXXX",             ←次のブロック

                     "parent": null,             ←前のブロック(トップなので、無し)

                     "inputs": {・・・},  ←命令語(opcode)により、構造が異なる。第5レベルの説明参照。

                     "fields": {・・・}   ←命令語(opcode)により、構造が異なる。第5レベルの説明参照。

                     "shadow": false,     ←?

                     "topLevel": true,    ←トップのブロックなので、true

                     "x": 175,            ←エディタで表示する際のx座標

                     "y": 115             ←エディタで表示する際のy座標

              },           

              "XXXX": {                   (途中のブロックの場合)

                     "opcode": "YYYY",    ←命令語。「メッセージを送る」などの英名

                     "next": "XXXX",             ←次のブロック

                     "parent": "XXXX",    ←前のブロック

                     "inputs": {・・・},  ←命令語(opcode)により、構造が異なる。第5レベルの説明参照

                     "fields": {・・・},  ←命令語(opcode)により、構造が異なる。第5レベルの説明参照

                     "shadow": false,     ←?

                     "topLevel": false    ←トップのブロックではないので、false。トップではない場合、x座標、y座標は無い模様。

                     },

       },

 

 

(第5レベル)命令語(opcode)毎の構造

 

"blocks""opcode"で、

 

@起動ブロック(ハットブロック)は、以下の7つ

 event_whenflagclicked            旗が押されたとき

 event_whenkeypressed             キーが押されたとき

 event_whenbroadcastreceived      メッセージを受け取ったとき

 event_whenthisspriteclicked      このスプライトが押されたとき

 event_whenbackdropswitchesto     背景が〜になったとき

 event_whengreaterthan            〜が( )のとき

 control_create_clone_of   クローンされたとき

 

 旗が押されたとき("opcode": "event_whenflagclicked")であるブロックの中の"inputs""fields"の構造

                            "inputs": {},

                            "fields": {},

 

 キーが押されたとき("opcode": "event_whenkeypressed")であるブロックの中の"inputs""fields"の構造(キーが”スペースキー”の時)

                            "inputs": {},

                            "fields": {"KEY_OPTION": ["space",null]},

 

 メッセージ△を受け取ったとき("opcode": "event_whenbroadcastreceived")であるブロックの中の"inputs""fields"の構造

                            "inputs": {},

                            "fields": {"BROADCAST_OPTION": ["","XXXX"]},

  (他は調査中)

 

 

Aメッセージを送信しているブロックは、 以下の2

 event_broadcast            メッセージ送る

 event_broadcastandwait            メッセージ送って待つ

 

 メッセージ送る("opcode": "event_broadcast")であるブロックの中の"inputs""fields"の構造

                            "inputs": {"BROADCAST_INPUT": [1,[11,"メッセージ1","XXXX"]]},

                            "fields": {},

 

 メッセージ送って待つ("opcode": "event_broadcastandwait")であるブロックの中の"inputs""fields"の構造

                            "inputs": {"BROADCAST_INPUT": [1,[11,"メッセージ1","XXXX"]]},

                            "fields": {},

 

 

B変数やリストに値を設定・追加しているブロックは、以下の7つ

(変数)

 data_setvariableto         □を○にする

 data_changevariableby             □を○ずつ変える

(リスト)

 data_deletealloflist              □のすべてを削除する

 data_deleteoflist          □の○番目を削除する

 data_addtolist                    ○を□に追加する

 data_insertatlist          □の▽番目に○を挿入する

 data_replaceitemoflist            □の▽番目を○で置き換える

 

(変数)

 □を○にする("opcode": "data_setvariableto")であるブロックの中の"inputs""fields"の構造(「□を10にする」の例)

                            "inputs": {"VALUE": [1,[10,"10"]]},       ←最内側の配列の0番目の項目が10だと、次に内容、らしい。

                            "fields": {"VARIABLE": ["","XXXX"]},    "XXXX"は変数□の番号。以下同様

 

 □を○ずつ変える("opcode": "data_changevariableby")であるブロックの中の"inputs""fields"の構造(「□を5ずつ変える」)

                            "inputs": {"VALUE": [1,[4,"5"]]},  4だと、???? 10とどう違う?

                            "fields": {"VARIABLE": ["","XXXX"]},

 

(リスト)

 □のすべてを削除する("opcode": "data_deletealloflist")であるブロックの中の"inputs""fields"の構造

                     "inputs": {},

                     "fields": {"LIST": ["","XXXX"]},

 

 □の○番目を削除する("opcode": "data_deleteoflist")であるブロックの中の"inputs""fields"の構造

                     "inputs": {"INDEX": [1,[7,"1"]]},

                     "fields": {"LIST": ["","XXXX"]},

 

 ○を□に追加する("opcode": "data_addtolist")であるブロックの中の"inputs""fields"の構造

                     "inputs": {"ITEM": [1,[10,""]]},

                     "fields": {"LIST": ["","XXXX"]},

 

 □の▽番目に○を挿入する("opcode": "data_insertatlist")であるブロックの中の"inputs""fields"の構造

                     "inputs": {"INDEX": [1,[7,""]],"ITEM": [1,[10,""]]},

                     "fields": {"LIST": ["","XXXX"]},

 

 □の▽番目を○で置き換える("opcode": "data_replaceitemoflist")であるブロックの中の"inputs""fields"の構造

                     "inputs": {"INDEX": [1,[7,""]],"ITEM": [1,[10,""]]},

                     "fields": {"LIST": ["","XXXX"]},

 

 

C変数やリストを参照している場合のブロックでの表現は、

                     (調査中)

 

 

D"blocks""opcode"で、言う・考えるをしているブロックは、 以下の4つ

 looks_say           〜と言う

 looks_sayforsecs    〜と○秒言う

 looks_think         〜と考える

 looks_thinkforsecs  〜と○秒考える

 

 〜と言う("opcode": "looks_say")であるブロックの中の"inputs""fields"の構造

                            "inputs": {"MESSAGE": [1,[10,""]]},

                            "fields": {},

 

 〜と○秒言う("opcode": "looks_sayforsecs")であるブロックの中の"inputs""fields"の構造

                            "inputs": {"MESSAGE": [1,[10,""]],"SECS": [1,[4,"2"]]},

                            "fields": {},

 

 〜と考える("opcode": "looks_think")であるブロックの中の"inputs""fields"の構造

                            "inputs": {"MESSAGE": [1,[10,""]]},

                            "fields": {},

 

 〜と○秒考える("opcode": "looks_thinkforsecs")であるブロックの中の"inputs""fields"の構造

                            "inputs": {"MESSAGE": [1,[10,""]],"SECS": [1,[4,"2"]]},

                            "fields": {},

 

 

   (その他、調査中)

 

 

 

--------------------------------------------

<例>

project.jsonファイルの内容例

 

{

       "targets": [

              {

                     "isStage": true,

                     "name":"Stage"

                     "variables": {"`jEk@4|i[#Fk?(8x)AV.-my variable": ["広域変数",0]},

                     "lists": {"c[AtS4*SonbCvD8N;a7!": ["広域リスト",[]]     },

                     "broadcasts": {      "Dcy)M/`.3;zy$~e,hO%m": "メッセージ1"     },

                     "blocks": {},

                     "comments": {},

                     "currentCostume": 0,

                     "costumes": [・・・],

                     "sounds": [・・・],

                     "volume": 100,

                     "layerOrder": 0,

                     "tempo": 60,

                     "videoTransparency": 50,

                     "videoState": "on",

                     "textToSpeechLanguage": null

              },

              {

                     "isStage": false,

                     "name": "スプライト1",

                     "variables": {"VJNI_(P?9#yCjNuP_)H]": ["ローカル変数",0]},

                     "lists": {"n*]1=A+;C2,igGNwX^Fi": ["ローカルリスト",[]]},

                     "+broadcasts": {},

                     "blocks": {

                            "]j,.]S){(Y@[G?r:0+AL": {

                                   "opcode": "event_whenflagclicked",

                                   "next": "*P8,f@{RgkC3:_t%puQl",

                                   "parent": null,

                                   "inputs": {},

                                   "fields": {},

                                   "shadow": false,

                                   "topLevel": true,

                                   "x": 175,

                                   "y": 115

                            },

                            "-oz5z3D2)e-vpGf3aQ/C": {

                                   "opcode": "event_broadcast",

                                   "next": "W|N(@W}VY}qa4;B4]yVu",

                                   "parent": "*P8,f@{RgkC3:_t%puQl",

                                   "inputs": {"BROADCAST_INPUT": [1,[11,"メッセージ1","Dcy)M/`.3;zy$~e,hO%m"]]},

                                   "fields": {},

                                   "shadow": false,

                                   "topLevel": false

                            },

                            "W|N(@W}VY}qa4;B4]yVu": {

                                   "opcode": "event_broadcastandwait",

                                   "next": null,

                                   "parent": "-oz5z3D2)e-vpGf3aQ/C",

                                   "inputs": {"BROADCAST_INPUT": [1,[11,"メッセージ1","Dcy)M/`.3;zy$~e,hO%m"]]},

                                   "fields": {},

                                   "shadow": false,

                                   "topLevel": false

                            },

                            "aPe)~4UyG]Ar]qt*oqp(": {

                                   "opcode": "event_whenkeypressed",

                                   "next": "o#d0#1o~8dOJIh.5|R6w",

                                   "parent": null,

                                   "inputs": {},

                                   "fields": {"KEY_OPTION": ["space",null]},

                                   "shadow": false,

                                   "topLevel": true,

                                   "x": 182,

                                   "y": 408

                            },

                            "*P8,f@{RgkC3:_t%puQl": {

                                   "opcode": "looks_say",

                                   "next": "-oz5z3D2)e-vpGf3aQ/C",

                                   "parent": "]j,.]S){(Y@[G?r:0+AL",

                                   "inputs": {"MESSAGE": [1,[10,"メッセージ1を・・・"]]},

                                   "fields": {},

                                   "shadow": false,

                                   "topLevel": false

                            },

                            "o#d0#1o~8dOJIh.5|R6w": {

                                   "opcode": "looks_sayforsecs",

                                   "next": "wHyHEn:;TqxEr%m=7v!+",

                                   "parent": "aPe)~4UyG]Ar]qt*oqp(",

                                   "inputs": {"MESSAGE": [1,[10,"スペースキーが・・・"]],"SECS": [1,[4,"2"]]},

                                   "fields": {},

                                   "shadow": false,

                                   "topLevel": false

                            },

                            "N/Jk(=2T|FtBS$vx|Zaa": {

                                   "opcode": "looks_thinkforsecs",

                                   "next": "#yy!PG9Xu2y%iG;o*U;|",

                                   "parent": "wHyHEn:;TqxEr%m=7v!+",

                                   "inputs": {"MESSAGE": [1,[10,"うーん..."]],"SECS": [1,[4,"2"]]},

                                   "fields": {},

                                   "shadow": false,

                                   "topLevel": false

                            },

                            "wHyHEn:;TqxEr%m=7v!+": {

                                   "opcode": "looks_think",

                                   "next": "N/Jk(=2T|FtBS$vx|Zaa",

                                   "parent": "o#d0#1o~8dOJIh.5|R6w",

                                   "inputs": {"MESSAGE": [1,[10,"うーん..."]]},

                                   "fields": {},

                                   "shadow": false,

                                   "topLevel": false

                            },

                            "#yy!PG9Xu2y%iG;o*U;|": {

                                   "opcode": "data_setvariableto",

                                   "next": "?O7wcI^UTM4_dneyr7MC",

                                   "parent": "N/Jk(=2T|FtBS$vx|Zaa",

                                   "inputs": {"VALUE": [1,[10,"0"]]},

                                   "fields": {"VARIABLE": ["広域変数","`jEk@4|i[#Fk?(8x)AV.-my variable"]},

                                   "shadow": false,

                                   "topLevel": false

                            },

                            "?O7wcI^UTM4_dneyr7MC": {

                                   "opcode": "data_changevariableby",

                                   "next": ".-(S[)B(/5yby`D#(Caa",

                                   "parent": "#yy!PG9Xu2y%iG;o*U;|",

                                   "inputs": {"VALUE": [1,[4,"1"]]},

                                   "fields": {"VARIABLE": ["ローカル変数","VJNI_(P?9#yCjNuP_)H]"]},

                                   "shadow": false,

                                   "topLevel": false

                            },

                            ".-(S[)B(/5yby`D#(Caa": {

                                   "opcode": "data_addtolist",

                                   "next": "V:0x~mZ]oVPU/pza|z9{",

                                   "parent": "?O7wcI^UTM4_dneyr7MC",

                                   "inputs": {"ITEM": [1,[10,"なにか"]]},

                                   "fields": {"LIST": ["広域リスト","c[AtS4*SonbCvD8N;a7!"]},

                                   "shadow": false,

                                   "topLevel": false

                            },

                            "V:0x~mZ]oVPU/pza|z9{": {

                                   "opcode": "data_addtolist",

                                   "next": null,

                                   "parent": ".-(S[)B(/5yby`D#(Caa",

                                   "inputs": {"ITEM": [1,[10,"なにか"]]},

                                   "fields": {"LIST": ["ローカルリスト","n*]1=A+;C2,igGNwX^Fi"]},

                                   "shadow": false,

                                   "topLevel": false

                            }

                     },

                     "costumes": [・・・],

                     "sounds": [・・・],

                     "volume": 100,

                     "layerOrder": 1,

                     "visible": true,

                     "x": 0,

                     "y": 0,

                     "size": 100,

                     "direction": 90,

                     "draggable": false,

                     "rotationStyle": "all around"

              }

       ],

      

 

       monitors

              ・・・

       extensions

              ・・・

       meta

              ・・・

 

}

-----------------------------------------------------------------

2019.4.23

藤生崇則

takanori@fujiu.gr.jp