Youtube動画
こちらより視聴できます。
説明
マインクラフトには、「スクリプトAPI」と呼ばれる拡張機能が存在します。この「スクリプトAPI」とは、プログラミング言語(Javascript)を用いてゲームシステムをカスタマイズできる機能のことであり、Javascriptの知識さえあれば、マインクラフトのシステムをある程度いじることができるんです。 本動画では、このスクリプトAPI(ScriptAPI)についての情報を体系化し、この動画を一通り見ることでその概要・書き方・実践的な使い方が学べるようにすることを目的とします。動画内で言及される各種スクリプトについては以下をご覧ください。
【ScriptAPIのテンプレート】https://lemon-slime.com/?p=1210
【ScriptAPIの環境構築】https://lemon-slime.com/?p=1254
【UIの作り方】https://lemon-slime.com/?p=1299
【コマンドの作り方1】https://lemon-slime.com/?p=1404
【コマンドの作り方2】https://lemon-slime.com/?p=1492
【クールダウン】https://lemon-slime.com/?p=1235
【ブロックを動かす】https://lemon-slime.com/?p=1638
【vscode】https://code.visualstudio.com/
【node.js】https://nodejs.org/en/download/prebuilt-installer
【uuidジェネレーター】https://www.uuidgenerator.net/
字幕一覧
茜 : コマンドを作りたい、
茜 : エンチャントを自作したい、
茜 : オリジナルのUIが作りたい!
茜 : こうしたマインクラフトの「やりたい!」を支えるのがScriptAPIである…
茜 : このチャンネルではこれまで多くのscript apiに関する情報を発信してきたが、
茜 : 少々断片的な印象があった…
茜 : そこでこの動画ではこれらについての情報を体系的に並べて、
茜 : 一通り動画を見ればscript apiについての概要が分かるようになることを目的とする
葵 : つまり総集編ってことだね
茜 : せやけど過去動画の中でもとりわけ有用な部分だけを抜き出し、
茜 : そうでない部分はできるだけ省くようにしたからだいぶ見やすくなったと思うわ
茜 : 最終的にこの動画を全て見れば、script apiそのものの概要と、
茜 : スクリプトの書き方、オリジナルのUI、エンチャント、コマンドの作り方が分かると思うわ
茜 : 葵、そもそもスクリプトAPIって何やと思う?
葵 : よく分かんないけど、プログラミングをしてるってイメージはあるよ?
茜 : せやな、具体的にはJavascriptっていう言語を使って、
茜 : マインクラフトのシステムをカスタマイズする機能のことやねん
茜 : 例えばこれは、「プレイヤーがジャンプしたらkillコマンドを発動する」っていうスクリプトやけど、
茜 : こんな感じでシステムに干渉できるわけやな
葵 : 面白そうだね
茜 : そうやと思うわ
茜 : で、早速このスクリプトAPIを触ってみるわけやけど、
茜 : これ、事前準備がそこそこ必要なんよ
葵 : …
茜 : そういう本質的じゃない部分に時間をかけるのもイヤやし、
茜 : うp主がスクリプトAPIのテンプレートを作ったみたいやから、
茜 : 今回は、これを使って楽しよっ!
葵 : テンプレートか、助かる~
茜 : まずはお馴染み、うp主のサイトにアクセスしよう
茜 : 概要欄にリンクがあるで
茜 : 概要欄にリンクがあるで
茜 : ちょっとスクロールするとダウンロードリンクがあるから、これをクリック
茜 : 適当な場所にダウンロードして…
茜 : ダウンロードしたzipファイルを右クリックして「すべて展開」を選択
茜 : 適当な場所に展開しよう
茜 : 展開が終わったフォルダを右クリックして、「切り取り」を選択。
茜 : com.mojangのdevelopment_behavior_packsフォルダ内にコピーしよう
葵 : com.mojangなんてフォルダー、どこにあったっけ?
茜 : 結構奥のほうなんよ、さっきのページに詳しいことが書いてあるから、見てみて
茜 : こんな感じに、development_behavior_packs内に
茜 : スクリプトAPIのテンプレートがあれば成功。
茜 : マインクラフトを起動してさっきのテンプレートが認識されているか確認してみるで
茜 : 「ビヘイビアパック」を選択して
茜 : さっきのテンプレートが正しく表示されていればOK。
葵 : ちゃんとあるね!
茜 : OK。ただ、これ自体はテンプレートであって
茜 : 何か特別なことをするわけではないから、有効化しても何も起こらへんで
葵 : ねー、早速スクリプトを書いてみようよ!
茜 : せやな、一旦マインクラフトは終了するで
茜 : さっきのフォルダをダブルクリックして、
茜 : その中にあるscriptsフォルダを開こう
葵 : 何かあるよ? main.js?
茜 : これはjavascriptのファイルで、ここにスクリプトを書いていくねん
茜 : 早速ダブルクリックして開こう
茜 : 初めてjsファイルを開くときはこんな画面が出ると思うけど、
茜 : メモ帳とかVSCodeとかのテキストエディタを開けばOK
葵 : なんかあるよ?
茜 : 一行目は「minecraft/serverモジュールを持ってきなさい」という意味。
茜 : これでマインクラフトの世界に干渉できるようになんねん
葵 : めっちゃ大事だ
茜 : せやで、これは消さんほうがええな
茜 : 余談やけど、ここで変数serverを宣言してるから、
茜 : 後で突拍子もなく出てくるserverは、
茜 : このマインクラフトのモジュールを表してるから注意な。
葵 : 了解!
茜 : 次にスクリプトを書いていくで
葵 : スクリプトを記述って書いてあるね
茜 : これはコメントやから消しても残してもどっちでも大丈夫やで
茜 : この下に早速、こんな感じでスクリプトを書いてみよう!
茜 : うp主のサイトにも書いてあるから、それをコピー&ペーストするのが早いかも
葵 : これはどういう意味なん?
茜 : 実際にゲーム内で見てみよう!
茜 : さっきのjsファイルを保存したらワールドを新規作成して
茜 : ビヘイビアパックを有効化。
茜 : すると・・・
葵 : え、なにこれ
葵 : メッセージ欄がHelloで埋め尽くされてるんだけど…
茜 : こんな感じで、ゲームシステムを改変できるのが、「スクリプトAPI」の効果。
葵 : どういうこと?
茜 : マインクラフトのモジュールにはsystemクラスのrunInterval関数があるんやけど、
茜 : これは何かを常時実行することができんねん
茜 : そしてworldクラスのsendMessage関数はメッセージを送信する関数。
茜 : 今回はHelloという文字を送信してるな
葵 : なるほど、それらを組み合わせて・・・
葵 : こんなことができたんだ
茜 : せやで、意外と単純やろ?
葵 : でも、もっと実用的なスクリプトがいいなあ・・・
茜 : じゃあ、葵がさっき言ってたアイテムを使ったらコマンドが発動するスクリプトでも作ろう!
茜 : とりあえずさっきのスクリプトはもう使わへんから削除して…
茜 : ここに新しいスクリプトを書いていくで
茜 : まずは、アイテムが使われたときに発火する関数を定義。
茜 : 関数の名前はeventの頭文字、evとするで。
茜 : もしも使われたアイテムのidがstickであれば、
茜 : そのイベントの発動者を基準にコマンドを発動する、という感じ。
茜 : 今回はgiveコマンドとするで
茜 : そしてマインクラフトを起動して手持ちの棒を右クリックで使ってみると・・・
葵 : あ、ダイアモンドが増えてる。
茜 : これもスクリプトAPIの効果やな。
茜 : このスクリプトもうp主のサイトに書いてあるから、コピー&ペーストするのが早いで。
茜 : アイテムのidやコマンドの内容を変えればお好みにアレンジできるので是非。
葵 : うちの作ったオリジナルアイテムもいけるのか…
茜 : もちろん!
茜 : この動画では簡単にAPIスクリプトについて触れたけど、
茜 : ここからはちょっと専門的な解説もしていくわ
葵 : 専門的な解説って何?
茜 : あれはすでにあるテンプレートを使って構築したけど、
茜 : それだとちょっと柔軟性・拡張性に欠けるんだよね
茜 : というわけでここでは一からAPIスクリプトの環境を構築する方法について話すわ
茜 : まずは必要なものがいくつかあるからこれをインストールしよう
茜 : 詳細は概要欄を見てね
葵 : なんかソフトの一覧にマインクラフトが入ってるんだけど
茜 : このチャンネルの視聴者の大多数は持ってると思うから解説は省くわ
茜 : 次のvscodeはプログラミングに特化したテキストエディタで、
茜 : 必須ではないけどあったほうが断然楽やで
茜 : 概要欄に貼ってあるリンクからダウンロードできるで
葵 : ゲーム制作で使ったやつだ
茜 : 汎用性が高いよな
茜 : 最後にnode.js。
葵 : これはあんまり聞かないなぁ
茜 : 本来javascriptっていう言語はWebページ上で動作するスクリプトなんやけど、
茜 : これをパソコン上で使えるようにするのがnode.jsの役割。
葵 : ふーん…
茜 : これも概要欄からアクセス可能。
茜 : ダウンロードボタンを押して、msiファイルをダブルクリックして実行すれば完了。
茜 : 今回解説する工程ではnode.jsは必須やで
茜 : これがダウンロードできたら次に進もう
茜 : まずはいつも通り、com.mojangフォルダを開こう
茜 : com.mojangフォルダの場所はご覧の通り。
茜 : 一時停止推奨。
葵 : 結構奥のほうだよね
茜 : せやな、迷いやすいから注意。
茜 : 開けたらdevelopment_behavior_packsを開こう
茜 : そしたら右クリックしてフォルダを新規作成。
茜 : フォルダの名前はお好みで。
茜 : 今回はtestとしたで
茜 : そしたらこれを開いてmanifest.jsonを作ろう
茜 : 右クリックして新規作成からテキストドキュメントを選択。
茜 : するとテキストファイルが作成されるから、これを改名して
茜 : manifest.jsonとしよう
葵 : 聞いたことあるな…
茜 : これはアドオンの要項を記述する、必須のファイルだから
茜 : 本チャンネルでも何回か取り上げてるで
茜 : これをvscodeで開いて、編集しよう
茜 : 内容はこのサイトに書いてある通りで、コピー&ペーストすればOK。
茜 : 何点か補足すると、
茜 : ここでアドオンの名前と説明を定義しているので、
茜 : アレンジはお好みで。
茜 : これがそのまま表示名になるで
茜 : このuuidはアドオン固有の文字列。
茜 : これはそのまま使うというよりも、
茜 : uuidジェネレーターとかを使って生成・変更するのがええかもな
葵 : そのまま使うとどうなるの?
茜 : このアドオン自体は動くんやけど、配布するときにちょっと面倒なことになるな
茜 : 最後にminecraft/serverのバージョンについて。
茜 : 動画作成時点で有効なバージョンはこんな感じで、
茜 : 基本的に最新のバージョンになればなるほどいろんな機能が使えるようになるんやけど
茜 : ベータ版は挙動が安定しないから使用には注意が必要やな
茜 : それが完了したらフォルダを新規作成して
茜 : scriptsと改名しよう
葵 : たしかここにスクリプトを書いていくんだっけ?
茜 : せやな、scriptsフォルダを開いて
茜 : 新規作成からテキストドキュメントを選んで
茜 : これをmain.jsに改名しよう
茜 : これをvscodeで開いたら、
茜 : 上のメニュー欄からTerminal、New Terminalの順に選択。
茜 : するとターミナルが開くので、
茜 : まずはこのアドオンがあるフォルダに移動しよう
茜 : 続いてnpmコマンドでマインクラフトのモジュールをインストール。
茜 : こんな感じになれば成功。
茜 : ちなみにこの一連の操作はコマンドプロンプトでもできるで
葵 : もはやマイクラの動画とは思えない…
茜 : ちょっと専門的すぎたな…
茜 : でも事前準備はこれで終わり。
茜 : あとはmain.jsに何かスクリプトを書いていこう
茜 : スクリプトの内容についてはさっきお話ししているので
茜 : ここでは割愛。
茜 : こんな感じで簡単にスクリプトが書けたら保存して、
茜 : アドオンの有効化を行ってからゲームをロードすると…
茜 : 無事、メッセージ欄がHelloで埋め尽くされるで
茜 : manifest.jsonのversionについてなんやけど、
茜 : ここをベータ版にすると最新の機能、
茜 : 例えば自作のコマンドの制作なんかができるようになるんやけど
茜 : その場合はベータAPIをオンにする必要があるから注意な
茜 : というところで基本事項の解説は終了
茜 : ここからは、これらを使って実践的な使い方、
茜 : 例えばオリジナルのコマンドやUIの制作方法について解説
茜 : さて、まずUIの制作にあたっては必要なものがいくつかあるから注意
茜 : これらのインストールを前提にこの動画の解説を進めていくんやけど、
茜 : その前に用語の定義から。
葵 : え、そこから?
茜 : 一応ね…
葵 : やりすぎると視聴者維持率が下がるよ…
茜 : おっと、それはあかん
茜 : これは各自確認してもらうとして
茜 : 早速、uiの制作に取り掛かろう!
茜 : まずはスクリプトAPIの根幹を成すアドオンを作っていくんやけど、
茜 : 葵、アドオンに欠かせないものと言えば?
葵 : え、根気とか?
茜 : そういう精神的な話とちゃうのよ
葵 : あー、manifest.jsonね
茜 : せやな、いつも通りcom.mojangフォルダを開いて
茜 : 中にあるdevelopment_behavior_packsを開くで
茜 : manifest.jsonの作り方はさっき解説しているので今回は割愛
葵 : 見た感じいつもと変わらないけど…
茜 : 一見するとそうなんやけど、
茜 : 最後でモジュールserver-uiをインストールせなあかんから注意点やな
茜 : テンプレートを使っている場合、これがない可能性が高いので、この部分を書き足せばOK
葵 : 初見殺しだ…
茜 : この辺がハードルになってるのは否めへんよな
茜 : このnameやdescriptionを変えてアレンジできるのはいつも通り。
茜 : uuidも変えておくとええで
茜 : それが終わったら次にマイクラのモジュールのインストールをしよう
茜 : まずはVSCodeのターミナルかコマンドプロンプトを起動。
葵 : コマンドプロンプト?
茜 : タスクバーの検索欄に「cmd」と入力したら起動できるで
茜 : ここにコマンドを打っていくんやけど…
茜 : まずはcdコマンドを実行してアドオンの作業フォルダに移動しよう
茜 : 例としてはこんな感じ。このパスは人によって違うから各自確認してみてね
茜 : 次に、ご覧の2つのコマンドを実行。
葵 : npmコマンドなんてあるんだぁ
茜 : これはjavascriptのモジュールをインストールできるコマンドなんやけど、
茜 : 実行には冒頭で話したnode.jsのインストールが必要やから注意。
茜 : こんな感じのメッセージが出れば成功やな
茜 : いよいよ本題。次にスクリプトを書いていこう!
葵 : やっとか…
茜 : scriptsフォルダ内のmain.jsに移動。
葵 : ここにスクリプトを書いていくんだよね
茜 : せやで
茜 : これをテキストエディタで開いたら、
茜 : 早速、これを記述してモジュールの呼び出しを行おう
葵 : あれ、今回は2つなんだ…
茜 : せやで、uiを表示するための特殊なモジュールが必要やねん
茜 : これで事前準備は完了。
茜 : 次にうp主のサイトにある、これをコピー&ペースト。
葵 : これはどういう意味?
茜 : 端的に言えば、UIを定義して表示するための関数。
茜 : ここでUIのタイトルとボタンの名前を定義しているから、アレンジはお好みで。
葵 : 下に書いてあるのは?
茜 : ここでボタンを押したときの挙動を定義してんねん
茜 : 一番上のボタンを押したときにresponseは0になって、
茜 : 二番目のボタンを押すとresponseは1になるで
茜 : 何も押さなかったときは、デフォルト。
茜 : 今回はそれぞれについてsendMessage関数でメッセージを送信してるな
葵 : これで終わり?
茜 : ここでやってるのはUIそのものの定義だから、実際に表示するスクリプトはまだなんよ
茜 : というわけで最後に、定義した関数を実行してUIを表示するスクリプトを書こう
茜 : とは言っても、常にUIが表示されてたら邪魔やし、UIが表示される条件も考えなあかんな
葵 : 何かアイテムを使ったら表示するのがシンプルで良さそう
茜 : せやな、そうするとこういうのがええかもな
茜 : 例にもれず、うp主のサイトに書いてあるからコピー&ペーストすればOK
茜 : アイテムを使ったら発火する関数を定義して、
茜 : もし使ったアイテムが棒だったら、
茜 : 使っているプレイヤーを基準にUIを表示する、という感じ。
茜 : この関数はplayerを引数にしてるから、アレンジする場合はこの点に注意やな
茜 : あと、ここがbeforeEventsだと動かへんから、これも注意。
茜 : 一度マイクラを起動して棒を使ってみるで
葵 : え、すごい! UIが表示されてる
茜 : こんな感じでUIとボタンが表示されて、どちらかを選択すると…
茜 : ちゃんとメッセージが表示されてるな
茜 : もう一個のボタンもOK。
葵 : ところでお姉ちゃんはどっち派なの?
茜 : うち? うちはマクド派やで
茜 : 逆に、何でみんなマックって言うの?
葵 : あ..やっぱり
茜 : 葵は?
茜 : え、意外…
茜 : メッセージだけじゃ物足りない場合は、コマンドを実行することもできるで
茜 : 正直、コマンドでほとんどのことはできるから、これとsendMessageで事足りると思うわ
茜 : 試しに「マック」を選択するとgiveコマンドが発動するようにしてみたで
茜 : 実際にマックを選択してみると…
葵 : あ、エメラルドが手に入った
茜 : こんな感じで自由にカスタマイズできるのがええよな
茜 : さっきはボタンをいくつか配置して、
茜 : 何かを起こす、みたいなことをやったけど、
茜 : そもそもボタンしか配置できへんかったから、
茜 : ちょっと自由度が低いのは否めへんよな
葵 : あー、確かに
茜 : 今回はボタンだけじゃなくて、テキスト入力欄とか
茜 : チェックボックスとか、色々なものを追加する方法を解説するで
葵 : 面白そう!
茜 : 解説の前に挙動の確認を。
茜 : このスライダーを動かすと数字を入力できるし、
茜 : テキストボックスにはテキストを入力できんねん
茜 : ドロップダウンでいくつかの選択肢の中から選ぶことができて、
茜 : チェックボックスでオンとオフを切り替えられるで
茜 : これで送信をすると、データが出力されんねん
葵 : へえ、すごい
茜 : というわけで早速これを作っていくんやけど、
茜 : まずはお馴染み、main.jsにアクセスして、
茜 : ここにスクリプトを書いていこう!
茜 : まずはimport文で2つのモジュールを呼び出すで
葵 : これはいつも通りだね
葵 : これはいつも通りだね
茜 : せやな、既にある場合はこれは不要。
茜 : 次に引数をplayerとして、フォームを呼び出す関数を作ろう
茜 : 変数formを宣言して、
茜 : uiモジュールのmodalformdataを呼び出すで
葵 : こんなのあったっけ?
茜 : これはformのテキストボックスとかチェックボックスを呼び出す関数。
茜 : 前回とはちょっとちゃうから注意な。
茜 : さて、これを使って各種機能をつけていくで
茜 : まずは、このスライダー。
茜 : スライダーを定義するには、slider関数を使おう
茜 : この関数には引数が必要で、この引数の文字が、
茜 : そのままここに表示するラベル名になんねん
茜 : さらにこの関数は引数が4つ必要で、
茜 : 順に最小値、最大値、刻み幅、初期値を定義すんねん
茜 : 実際にこのスライダーの初期値は50になってるな
葵 : 引数、多いね
茜 : 一つでもなかったらエラーを吐くから気つけてな
茜 : 続いて、テキストエリアを定義。
茜 : textField関数を使おう!
茜 : ここでは引数の文字列が2つ必要で、
茜 : 順にラベル名とプレースホルダー名を定義すんねん
茜 : それぞれ、こことここの文字になんねん
葵 : へえ、細かい…
茜 : 次に、いくつかの選択肢から一つを選ぶドロップダウン。
茜 : dropdown関数を使うで
茜 : ここでは引数が2つ必要で、
茜 : 一つは表示されるラベル名、もう一つは表示される選択肢。
茜 : これは配列形式で定義するから注意な
茜 : 最後に、オンオフを切り替えるチェックボックス。
茜 : toggle関数を使うで
茜 : 引数は、ラベル名。
葵 : 1個だけなんだ
茜 : これはシンプル。
茜 : 最後にこのformを表示する機構を作ろう!
茜 : 前回と同じで、show関数を使おう
茜 : 引数は、player。
茜 : これを実行したら、引数responseを受けて実行する関数を定義
茜 : ここでいきなり挙動を書いてもええんやけど、
茜 : 例外処理を先にすましておくのが筋やな
葵 : 例外処理?
茜 : 予期しない動作をした時の挙動を定義すんねん
茜 : まずはこのフォームを送信せず閉じたときの挙動を設定しよう
茜 : if文を使って条件分岐させて、
茜 : もしフォームがキャンセルされたなら、
茜 : プレイヤーに軽くメッセージを送っといて、
茜 : return文でこの関数から離脱。
茜 : 次にメインの挙動を設定するんやけど、
茜 : 今回はフォームのデータを表示するだけの
茜 : シンプルなシステムを設定するで
葵 : なるほど
茜 : sendMessage関数でメッセージを送信。
茜 : ここではスライダーの値を取得して出力するで
茜 : スライダーの値をString型にして、
茜 : response.formvalue0でスライダーの値を取得
葵 : どうして0なの?
茜 : このformvaluesはフォームのデータを配列で出力するんやけど、
茜 : 上から順に0,1,2と配列の番号が振られんねん
茜 : 今回、スライダーは一番上にあるから0になるわけやな
葵 : なるほどね
葵 : なるほどね
茜 : 同じようにテキストボックスは今回1となるし、
茜 : ドロップダウンは2になるな
茜 : チェックボックスは3やけど、基本これは
茜 : 選択に合わせてtrueかfalseを返すから、
茜 : if文と合わせて使うことが多いかもしれへん
葵 : なにこれ?
茜 : 三項演算子。if文みたいなもんや
茜 : さて、関数の宣言ができたから、後はこれを呼び出すで
茜 : 今回は、棒を使ったらこの関数を発火させよう
茜 : この辺のやり方についてはこの動画が詳しいで
茜 : 関数を使うときにplayerクラスの引数が必要なのは注意
茜 : この状態で棒を使うと・・・
葵 : なんか出てきた!
茜 : 表示もOKやな
茜 : 色々入力を変えて遊んでみよう
葵 : 出力が変わったね
葵 : ドロップダウンの選択が1になってるけど…
茜 : ドロップダウンは要素の名前じゃなくて、
茜 : 要素の番号を返すみたいやな..
茜 : こんな時はif文を使って、
茜 : それぞれについて処理を定義するのがええかもな
茜 : こうするとドロップダウンの名前が出てくるな
茜 : キャンセルしたときの処理もOK
茜 : 今回はsendmessageでメッセージ送信をやったけど、
茜 : 実際はrunCommandと合わせて
茜 : 動的に処理を実行することが多いかもな
茜 : これを使えば、入力した座標にテレポートするアドオンが作れたり
葵 : 夢が広がるね!
茜 : せやな、みんなも試してみてね!
茜 : さて、これまではUIの作り方について話してきたけど、
茜 : ここからはコマンドの作り方について解説するわ
茜 : 自作といってもな、スラッシュから始まる普通のコマンドを作るわけとちゃうんよ
茜 : こんな感じでチャット欄を監視して、
茜 : 特定のフレーズだったら何かを実行する、というのをやりたいねん
茜 : で、ここが重要やねんけど、
茜 : 今回チャット欄の監視を行うにあたって
茜 : ここがベータ版じゃないと動かへんのよ
葵 : 最新機能なんだ
茜 : で、ここのバージョンは頻繁に変わるから、
茜 : 公式のリファレンスでバージョンを確認してね
茜 : ちなみに、動画作成時点のバージョンは1.15のベータ版。
茜 : あと、ベータ版を使う場合はワールド起動時に「ベータAPI」を
茜 : オンにする必要があるのも注意。
葵 : 忘れそうだ…
茜 : 後でもう一回話すで
茜 : main.jsを開いて、スクリプトを書いていこう!
葵 : ようやくコマンドが作れるのね…
茜 : その前にモジュールのインストールを忘れずに
葵 : あとはスクリプトを書くだけだね!
茜 : せやな、
茜 : 大まかな流れはこんな感じで、
茜 : 何らかのメッセージが送信されたときにそれを検知して
茜 : 処理につなげていくという流れやな
茜 : まずは簡単な例を書いていくで
茜 : まずは、いつも通りモジュールの呼び出しから
茜 : これでマイクラの世界に干渉できるようになるで
茜 : 続いて、チャットが送信されたときに発火する関数を定義。
葵 : chatSendなんてあるんだ
茜 : これで、チャットの送信を監視できんねん
茜 : 便利やからめっちゃ使うで!
茜 : subscribeで関数の実行を予約したら、
茜 : この関数の名前をevとするで
茜 : ev.messageでメッセージの内容を取得して、
茜 : もしこのメッセージが「!killall」だったら、
茜 : このメッセージの送信者、ev.senderのもとで
茜 : runCommand関数でコマンドを実行。
茜 : これを保存して、
茜 : さっき言ったようにベータAPIをオンにしたら、
茜 : ゲーム内で「!killall」が使えるようになるはずやで
茜 : 実際に使ってみよう
葵 : あ、モブが倒された
茜 : こんな感じでちゃんと機能するな
茜 : さっき書いたスクリプトはうp主のサイトにもあるから、
茜 : コピー&ペーストするのが早いで
茜 : ここではrunCommand関数を使って処理を行ったけど、
茜 : sendMessage関数を使ってメッセージを送信することもできるで
茜 : これを実行すると、こんな感じでメッセージが返ってくるで
葵 : 山びこ山みたい
茜 : それ以外にもさっき紹介したやり方でUIを出すとか、
茜 : 色んなスクリプトが考えられるな
茜 : みんなもオリジナルのコマンドを作ってみよう!
茜 : で、普通のブログとか動画はここで終わるところやけど、
茜 : ここで終わらないのが当チャンネル。
茜 : 普通のコマンドだとこんな感じでオプションの設定、
茜 : いわゆる引数を付けられるんやけど、
茜 : オリジナルのコマンドにもこれを実装してみよう、という話。
茜 : 説明に入る前に、前提知識から。
茜 : 例えばこんな感じに文章があったとするやん?
葵 : ぶん、しょう?
茜 : オレンジのところに文字が入って、白は空白。
茜 : で、こんな文章について、
茜 : javascriptに標準搭載されてるsplit関数を使って
茜 : 空白で分割すると、
茜 : こんな感じになんねん
葵 : それぞれの文字に番号が振られるのか
茜 : せやで、そしてそれぞれを配列に格納。
茜 : これは空白に限らずいろんな文字で分割出来て、
茜 : この文章を点で分割するとこんな感じになるな
茜 : このsplit関数を使うと引数を取得出来て、
茜 : その例はこんな感じ。
葵 : これは何?
茜 : 足し算を行うaddコマンドを実装する例。
茜 : 実際に使ってみるで
茜 : 引数の数字を2つ入力すると・・・
葵 : あ、足し算ができてる!
茜 : もちろん、他の数字でも・・・
葵 : これも大丈夫そう
茜 : 小数の足し算もOK
茜 : ということでこれの解説を。
葵 : 一行目はさっきと変わらないね
茜 : せやな
茜 : だけど2行目はstartsWithが使われてるな
葵 : ほんとだ。これは何?
茜 : これは、文字列が指定の語句から始まっているかどうかを判定する、
茜 : javascriptの標準ライブラリの関数。
茜 : 今回は、メッセージが「!add」から始まっている時にtrueを返すで
茜 : 3行目ではさっき話したsplit関数を使って、
茜 : 空白でメッセージを分割して配列に格納。
茜 : 4行目で足し算を行う、という流れやな
茜 : この辺の詳しい流れはうp主のサイトにも書いてるで
茜 : そしてこれを応用すると、
茜 : 引数を無限に増やすことができんねん
葵 : え、すご!
茜 : 今回はこんな感じで5つの引数を置いてみよう
葵 : あ、合計が出た
茜 : こんな感じで引数の数に関わらず
茜 : コマンドを実行することができんねん
茜 : さっきと違うのは、引数の処理でfor文を使ってるところやな
茜 : このlengthで配列の要素の数を取得してるで
葵 : なるほど、面白いね!
茜 : みんなもカスタマイズして遊んでみてね!
茜 : で、さらに応用するとこんな感じで
茜 : コマンドの中で任意のコマンドを実行できるんよ
葵 : え、どういうこと?
茜 : executeコマンドをイメージすると分かりやすいかも
茜 : これはwaitコマンドといって、
茜 : コマンドの実行を遅らせるコマンドやねん
茜 : 試しにこれを使ってみるで
茜 : こんな感じのコマンドを実行すると・・・
茜 : ちょっと遅れてメインのコマンドが発動すんねん
茜 : これは任意のコマンドについて有効で・・・
茜 : giveコマンドを遅延させることもできるな
茜 : ここでは実行が60ティック、3秒遅れるで
葵 : すごい!
茜 : 今回の場合、空白で分割するのはあかんのよ
葵 : というと?
茜 : giveコマンドの中にも空白があるやろ?
葵 : ああ、waitコマンドの引数とgiveコマンドの引数を
葵 : 区別しなきゃいけないのか…
茜 : 今回はこういう感じで分割したいから、
茜 : for文でコマンド部分の文字列を合体してるで
葵 : なるほど…
茜 : ここまで色々コマンドの作成方法について話したけど、
茜 : チャット欄に依存する以上、
茜 : コマンドブロックを使った実行の自動化が難しいんだよね
葵 : 確かに…
茜 : 次はこの問題の解決策を示すわ
茜 : で、その方法なんやけど…
葵 : タイトルにも書いてあるし、何ならここにもあるよ?
茜 : せやな、鍵を握るのがこのscripteventコマンド。
葵 : でも、そんなのあったっけ?
茜 : それがあんねん
茜 : こんな感じでヘルプにも出てくるんやけど、
茜 : 影が薄いのか、あまり知られてへんみたいやな
葵 : だって説明見てもよく分かんないこと書いてあるし…
葵 : せめて「コマンドを自作できます」とか書かれていればいいのに…
茜 : ま、その辺分かりづらいのはあるよな
茜 : まずはこのコマンドがどういうものなのかを話すわ
葵 : お願いします!
茜 : そもそも、scripteventコマンドの構文はこんな感じ。
茜 : 最初にコマンド名であるscripteventをおいて、
茜 : その後にidを、
茜 : そして最後にメッセージを書くで
葵 : このidって何? あらかじめ決まってるの?
茜 : いや、自分で決められんねん
茜 : このidだったらこういう処理をする…
茜 : っていうのを、あらかじめ自分で決めんねん
葵 : へぇ…
茜 : 百聞は一見に如かずやな、実際にコマンドを使ってみるで
茜 : 今回はあらかじめ、killallというidを入力すると
茜 : killコマンドが発動するようにしてるで
茜 : そしてこのコマンドを実行すると…
葵 : モブが倒されたね
茜 : こんな感じで機能するわけやな
茜 : このidの部分には名前空間が必要やから注意
葵 : これ、どうやってidを定義するの?
茜 : ここで必要になるのが、ScriptAPI。
茜 : いつも通りmain.jsにアクセスして
茜 : モジュールを呼び出したら、
茜 : こんな感じでスクリプトを書いていくで
茜 : まずは、scripteventが発火したときにトリガーする関数を定義。
茜 : 関数の引数はevとするで
茜 : ev.idで送信されたscripteventのidを取得して
茜 : もしこれがkillallやったら、
茜 : scripteventの送信者、ev.sourceEntityのもとで
葵 : これで完成?
茜 : もうちょっとやりたいことがあんねんけど、
茜 : とりあえず、このスクリプトの挙動を確認してみよう!
茜 : scripteventコマンドを実行するで
葵 : ちゃんと動くね!
茜 : 次にコマンドブロックで動くか見てみるで
茜 : 同じようにscripteventコマンドを入力。
茜 : そしてコマンドを実行すると…
茜 : こんな感じでエラーが発生すんねん
葵 : え、scripteventはコマンドブロックでも使えるんじゃないの?
茜 : 無条件で使えるわけとちゃうねん
茜 : 考えてみれば当たり前のことで、
茜 : 今回、コマンドを実行したエンティティについて
茜 : runCommand関数で処理を行ってるけど、
茜 : ブロックはエンティティとちゃうからエラーが発生するわけやな
葵 : スクリプトがコマンドブロックに対応してないのか…
葵 : じゃあ、どうすればいいの?
茜 : 実行者を場合分けして、
茜 : それぞれについて処理を行っていくで
茜 : ev.sourceTypeでコマンドの実行元を取得して、
茜 : それがentityだったら普通に処理を実行。
茜 : そうでなくて、実行元がBlockやった場合、
茜 : ev.sourceBlockで実行元のブロックを取得して、
茜 : そのブロックのディメンション内でrunCommandを実行。
葵 : 回りくどいね
茜 : しゃーなしやな
茜 : 実際に動かしてみるで
茜 : コマンドブロックを起動すると…
葵 : あ、モブが倒されてる
茜 : エラーも発生してへんな
茜 : え、これを応用すると足し算コマンドが作れんねん
葵 : 足し算?
茜 : 今回は、idとしてaddを入力。
茜 : そして次のメッセージ欄に、足し算する数字を入力。
茜 : 今回は、小数の入った2つの数字を足し算するで
葵 : 結果がでてるね
茜 : さっきと違うのは、このev.messageで
茜 : メッセージ部分の文字を取得してるとこやな
葵 : ホントだ
茜 : これをsplit関数を使って空白で分割して、
茜 : それらの数字を使って足し算をしてるで
茜 : 後半部分はさっきとあまり変わらへんのやけど、
茜 : この辺の流れだけちょっとちゃうな
葵 : ホントだ、どういうこと?
茜 : sendMessage関数はクラスがプレイヤーじゃないと使えへんから、
茜 : そのブロックがあるディメンションにいるプレイヤーを取得して、
茜 : 各プレイヤーについてメッセージ送信してんねん
茜 : こんな感じで、scripteventを使えば
茜 : コマンドを「自作」できんねん
葵 : 結構、面白いね
茜 : せやろ、これを使えば自由度がかなり上がると思うわ
茜 : コマンドブロックが使えるのもええよな
茜 : さて、基本的なscript apiの概要や使い方についてお話ししたところで、
茜 : 次は実践的な使い方、特にエンチャントの自作について話すわ
茜 : 百聞は一見に如かずやし、まずは挙動を見せるわ
葵 : え、雷が落ちてる!
茜 : こんな感じで、敵を攻撃したら雷が落ちるエンチャントを作れんねん
葵 : ホントにエンチャントなの? コマンドでもできそうだけど…
茜 : でも、ただの剣で攻撃しても何も起こらへんねん
葵 : あー、それはコマンドじゃ難しいか…
茜 : このエンチャントがついてるときだけ、雷が落ちんねん
葵 : どういう仕組みなの?
茜 : 実は、雷が落ちる剣にはこんな説明文がついてんねん
葵 : ん?電撃の力?
茜 : せやで、これが付いてたら雷が落ちるし、
茜 : 逆についてへん場合は雷は落ちへん
茜 : これが実質的なエンチャントとして機能するわけやな
葵 : 疑似的といったのはそういうことだったのね…
葵 : そういえばアイテムに説明文ってつけれたっけ?
茜 : それができんねん
茜 : 詳しいことはこの動画とかこの動画で解説してるで
葵 : どうやって説明文を検知して、雷を落とすんだろう?
茜 : 流れとしては敵を攻撃したときに発火する関数を作って、
茜 : そのとき持っているアイテムを取得して、
茜 : そのアイテムについている説明文を検知。
茜 : その説明文が「電撃の力」ならば、
茜 : summonコマンドで対象に雷を落とす、という感じやな
茜 : 次章ではこれを形にしていこう!
茜 : まずはimport文でモジュールを呼び出し。
茜 : 次にスクリプトの本体を書いていこう
茜 : まずは攻撃したときに発火する関数を定義。
茜 : entityHitEntityプロパティを使うで
茜 : 関数の引数をevとして
茜 : まずは攻撃をしたplayerを取得して変数に格納。
茜 : この変数playerが、エンティティのplayerであることを確認して、
茜 : playerの持っている武器を取得して変数weaponに格納
茜 : getComponent関数を使おう
茜 : getItem関数で現在選択中のスロットのアイテムを取得して
茜 : もしこのweaponが定義されてないなら関数から離脱
茜 : この場合は素手っていうことやな
茜 : 続いてgetLore関数でこのアイテムの説明文を取得して、
茜 : それが電撃の力であるかどうかを確認
茜 : もしそうなら攻撃されたエンティティを基準に
茜 : summonコマンドで雷を召喚。
葵 : なんかここの書き方おかしくない?
茜 : 実はこの関数は説明文を配列の形式で出力すんねん
葵 : へえ、意外。
茜 : ここでは0番目の要素を取り出してる感じやな
茜 : スクリプトの本体はそうなんやけど、
茜 : 「電撃の力」のついたアイテムをどっかから調達せなあかんから、
茜 : 次はそれを定義するスクリプトを書こう!
茜 : 今回はコマンドで入手することにするで
茜 : 詳細はさっき解説してるな
茜 : idをenchantmentの頭文字、enchとして
茜 : 実行者playerを宣言。
茜 : そしてさっきと同じように手に持ってるアイテムを取得
茜 : さっきと違ってここがgetSlotなのは注意な
茜 : そしてこのweaponがきちんと存在するか確認して、
茜 : 最後にこのweaponに「電撃の力」という説明文を付与
茜 : 相変わらず配列で指定する点には注意。
茜 : 実際に確認してみるで
茜 : まずは説明文がついてない状態。
葵 : ただの剣だね
茜 : この剣を使っても特に何も起こらへんな
茜 : 次にさっき作ったコマンドで説明文をつけてみよう
葵 : ちゃんと説明文がついたね
茜 : 実際に攻撃すると…
葵 : 雷が落ちてる!
茜 : ちゃんと動くことが確認できるな
茜 : さて、このエンチャントだけど、やろうと思えば
茜 : ツルハシとか道具にもついちゃうんだよね
葵 : あ、ホントだ。剣だけに制限したいなぁ
茜 : そういう時は条件付きで説明文を付与すればええかも
茜 : weaponのidを取得して、これが「sword」で終わるなら
茜 : setLore関数を実行するようにしてみよう
茜 : そうでない場合は、
茜 : プレイヤーに軽くメッセージを送信。
茜 : 試しにこのシャベルに説明文をつけてみるで
茜 : scripteventを実行すると…
葵 : あ、なんか出てきた
茜 : こんな感じでエンチャントの付与を制限することもできるな
茜 : 今回の処理は敵に対して何かを行うものだったけど、
茜 : 当然、自分に対して何かを起こすこともできんねん
茜 : 試しにsayコマンドが発動するようにしてみよう
茜 : 攻撃をすると…
葵 : なんか出てきた
茜 : こんな感じでエンチャントの挙動を変えることもできるな
茜 : 基本的にはこれらを組み合わせてscript apiを構築していくんやけど、
茜 : ここではscript apiの実践的な使い方の例を2つ示すわ
茜 : これらを参考にしながらオリジナルのスクリプトを作ってみてね
茜 : まずはこの「クールダウン」をどう定義するかなんやけど…
葵 : え、定義も何も一度アイテムを使ったら一定時間は使えなくなることでしょ?
茜 : せやな、その線で考えていこうか
葵 : でもどうやって実装するの?
茜 : いろいろ考えたんやけど、こんなアプローチはどうやろうか?
葵 : え、難しそう…
茜 : ここではscoreboardコマンドを使うのが肝やな
茜 : もし視聴者の中でもっとスマートな方法を思いついた方は、コメントをお願いします!
葵 : 肝心のスクリプトは?
茜 : さっきのアプローチに則ると、こんな感じ。
葵 : さっきよりだいぶ長くなったね
茜 : 機能がちょっと複雑なぶん、しゃーなしやな
茜 : いつも通り、うp主のサイトにも書いてあるから、参考に。
茜 : 一つずつ解説していくな
茜 : 最初のcooldown_timeはクールダウンの長さを定義していて、単位は秒。
葵 : デフォルトは3秒なんだ
茜 : せやな、ここの数字を変えることでアレンジできるで
茜 : 次はアイテムを使用したときに発火する関数を定義。
葵 : さっきも見たな…
茜 : 1つ違うのは、コマンドの発動が使用者のスコアに依存している点。
茜 : ここのrun以下でアイテム使用時のコマンドを定義しているので、
茜 : アレンジする場合はお好みで。
葵 : sayコマンドとかいうマニアックなものを…
茜 : sayコマンドはマインクラフト界のHello Worldやで…
茜 : アイテムを使用した際にスコアを付与している点もさっきと違うところやな
茜 : 続いてゲーム内で常に実行される関数を定義。
葵 : このrunIntervalがそれか
茜 : せやで、結構よく使うから覚えておいて損はないな
茜 : 全てのプレイヤーについて、
茜 : scoreboardコマンドを使って、毎ティックスコアを1ずつ減らしていく感じ
茜 : ちなみに、何かを常に実行するにはrunInterval以外にも
茜 : tick.jsonというのがあるんやけど、
茜 : これについてはまた別の機会に。
葵 : 別の機会に解説すること多すぎ。
茜 : ネタは多ければ多いほどええんよ
茜 : で、これを保存することで棒を使ったらhello!というメッセージが表示されるけど、
茜 : 連続で使用すると表示されなくなるはずやで
葵 : 「はず」…とは?
茜 : 実機検証は済んでるから心配せんでもええで
葵 : 良かった…
茜 : 最後にアイテムの使用時に条件を課す方法について解説するで
葵 : ところでその「条件」って何?
茜 : 色々あると思うけど、ここではプレイヤーが特殊なアクションを起こしていた場合に
茜 : アイテムを使用可能にする方法について考えていくで
茜 : 例えばプレイヤーがスニークしているときにアイテムを使用可能にするスクリプトはこんな感じ。
葵 : if文が加わってるね
茜 : せやな、このisSneakingプロパティはエンティティがスニークしている時にtrueを返すから、
茜 : これを条件判定に使ってる感じやな
茜 : そしてこれを応用して、
茜 : 条件分岐を行うこともできるな
茜 : スニークしている時はHelloというメッセージを送信して、
茜 : そうでない場合は別のメッセージを送信する感じ。
葵 : これの何がすごいの?
茜 : 葵、これを侮っちゃいけない…
茜 : こうやって条件分岐をすることで、一つのアイテムに複数の機能を持たせられるんよ
葵 : あー、その発想はなかったわ
茜 : ちなみに同様のことはanimation_controllerでもできるんやけど、
茜 : いつかこれについても解説するかもしれへん
葵 : それは解説しないやつだ…
茜 : ぐぬぬ…
茜 : こういう条件判定に使うプロパティにはいくつか種類があって、
茜 : 主なものはこんな感じ。
葵 : 結構あるんだね
茜 : まあ、でも実際うp主がよく使うのはisSneakingとisJumpingくらいやけどね…
茜 : 詳しいことはうp主のサイトに書いてあるから、参考にどうぞ。
茜 : 次はscript apiでブロックを動かす方法について話すわ
茜 : さて、まずはいつも通り挙動の確認から
葵 : え、すごい!勝手にブロックが動いてるよ?
茜 : こんな感じで、自由自在に動くブロックが作れんねん
葵 : これ、どういう仕組みで動いてるの?
茜 : 今回はプレイヤーが手に持っているアイテムを判別して、
茜 : それが木の棒なら右に、そうでないなら左に動いてんねん
葵 : あ、プレイヤーの意思で動いてるんだ
茜 : せやねん、勝手に動いてるんとちゃうから、それが今回の肝やな
葵 : でも、ブロックを動かせたところで何ができるんだろう?
茜 : 実用的に使えそうなところだとエレベーターとかかな
茜 : プレイヤーの意思で上や下に行けるようにすると面白そうやし
茜 : 同じように自動ドアとかエスカレーターも作れそう
茜 : あとはこれを応用してブロックゲームを作ってみるとか
葵 : あー、なるほど。マイクラでテトリスなんかできたら楽しそうだ
葵 : これってどうやって動かすんだろう?
茜 : おそらく2つ方法があって、
茜 : 一つはコマンドを使ったやり方。
茜 : そしてもう一つはScriptAPIを使ったやり方。
茜 : それぞれの方法のメリット・デメリットについて話すわ
茜 : さて、このコマンドを使ったやり方の良いところは
茜 : とにかく簡単ってことやな
茜 : 専門知識は要らへんし、環境が整ってなくても可能。
葵 : 具体的なコマンドは?
茜 : setblockを2つ組み合わせて移動を実現するのが早いかも
茜 : ただ、コマンドの場合対象となる座標を指定せなあかんから、
茜 : 柔軟性に欠けるのは否めへんよな…
葵 : あー、それぞれの移動先に対応したコマンドが必要だもんね
茜 : この時点で、自由自在に動かせへんのと同義やな
葵 : そこで登場するのがScriptAPIってことか
茜 : せやねん、プログラミングを使って
茜 : ブロックの移動を制御できれば自由度も増すよな
茜 : というわけで、今回はscript apiを使ってブロックを動かしてみよう
茜 : まずはいつも通り、main.jsにアクセスして開こう
茜 : そしてimport文でモジュールを呼び出し。
茜 : 続いてブロックを動かすための関数を作ろう
茜 : 引数は、動かすブロックと方向
葵 : 方向?
茜 : 正確に言うと、これはベクトルやな
茜 : そして移動先のブロックの座標を計算して
茜 : x,y,zのそれぞれについてこのように記述。
茜 : それが出来たら移動先のブロックを取得しよう
茜 : まずこれが定義済みかどうか確認して
茜 : 続いてこれが空気ブロックかどうかを確認。
葵 : そっか、移動先に別のブロックがあったら困るもんね
茜 : せやな、別にブロックが埋まってもええならこのif文は不要。
茜 : 続いてブロックの移動を定義しよう
茜 : setblocktype関数で指定の座標のブロックを置き換え。
茜 : 元々ブロックがあった場所にはairを配置しておこう
茜 : さて、この関数の部分はこれで終了。
葵 : あとはこれを呼び出すだけかな?
茜 : 実はこの関数の呼び出しには一つ問題点があんねん
葵 : 問題点?
茜 : というのもこのblockの指定にあたって
茜 : block型のオブジェクトが要んねん
葵 : え、普通にここにあるブロックを指定したいんだけど
茜 : そのブロックの指定をどうやるか、という問題やな
茜 : 一々座標で指定するのは大変。
葵 : あ、そっか
茜 : ということで今回はプレイヤーの近くにあるブロックを
茜 : 柔軟に指定することにしよう
茜 : 新しく関数get blocksを定義して、
茜 : 引数はplayerとblockのid、範囲としよう
茜 : 各種変数をこのように定義して、
茜 : x,y,z座標のそれぞれについて繰り返し処理。
茜 : それぞれの座標に存在するブロックを取得して変数に格納。
茜 : そのblockが指定のidと同じなら、
茜 : 配列blocksの末尾にそのブロックを格納。
茜 : 最後にこの配列をreturnして関数部分は終了
葵 : 近くにあるブロックの組をまとめて出力するんだ
茜 : そういうこと!
茜 : そういうこと!
茜 : 最後にこれらの関数を使ってブロックの移動を定義しよう
茜 : 今回は試しにプレイヤーの近くにあるダイアモンドブロックを指定して
茜 : 次にプレイヤーが手に持っているアイテムを取得。
茜 : このアイテムが定義されてるかをまず確認して、
茜 : もし定義済みならさっき取得した各ダイアモンドブロックについて
茜 : プレイヤーの手に棒があるときに
茜 : move block関数でブロックの移動を実行。
茜 : 今回はx方向に1移動させてみよう
茜 : 逆に棒を持ってへんときはx方向に-1移動。
葵 : これでプレイヤーの操作を可能にするのか
茜 : 今回は手持ちのアイテムで場合分けしてるけど、
茜 : 他にも色々方法はあると思うわ
茜 : 実際にマイクラを起動すると、
葵 : あ、ちゃんとブロックが動いてるね
茜 : 手に持っているアイテムの判定もOK
茜 : ちなみにこのストッパーのブロックを撤去すると
葵 : あ、さらに進み続けるんだ
茜 : 行き先が空気ブロックならこんな感じで進み続けんねん
葵 : でも、なんか止まってない?
茜 : 今回はプレイヤーの周囲10ブロックにある
茜 : ダイアモンドブロックだけを指定して移動の対象にしてるから
茜 : 当然離れたブロックには効果が及ばへんわけやな
茜 : さっきの関数の部分を変えればアレンジできるで
葵 : あ、そうなんだ
茜 : でもあんまり範囲を広げすぎると重くなるから注意な
茜 : このブロックの移動、応用も効いて楽しいので
茜 : みんなもやってみてね!
茜 : 今回のスクリプトは概要欄からコピペできるので是非!
茜 : というわけでこの動画ではscript apiについて一通り解説したけど、
茜 : 当然できることはまだぎょうさんあると思うわ
葵 : まさに氷山の一角だよね
茜 : せやねん、みんなもこれを参考にオリジナルのスクリプトで遊んでみよう!
茜 : あと、ここでは紹介しなかったけどマイクラにはweb socket serverと呼ばれる分野があって、
茜 : こっちはscript apiより専門的な分、扱えるとさらに楽しいので触ってみるのもええかも
茜 : 今回の動画はこれでおしまい!
葵 : 長時間のご視聴、お疲れ様です…
茜 : 質問やご意見はコメント欄よりお願いします
茜 : それじゃー、
茜 : バイバーイ!
茜 : バイバーイ!