【この有料アセットがすごい！】UnityでScratch - Likeなブロックプログラミングを実現「Play Mode Blocks Engine」

#初めに
先輩に面白いアセットを教えていただいたので実際に買って遊んでみた。
そのレビューとして、ざっくり理解した使い方を書いていこうと思う

# 基本的な機能の説明
UnityでScratch-Likeな機能が実装できる有料アセット
値段は25ドルなので2600円くらい？（2021/02/20現在）
[Play Mode Blocks Engine](https://assetstore.unity.com/packages/templates/systems/play-mode-blocks-engine-158224?locale=ja-JP)

大部分はScratchと同じ。
Blockを組んで何らかの実行イベントを起こすと組んだブロックに応じて処理が走る。

# 全体
外観はこんな感じ。（いくつか自分で適当にボタンやテキスト追加してます）
![スクリーンショット 2021-02-20 17.30.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/bac8fd18-5522-6014-ce16-72a1721a88d1.png)

ブロックは２種類ある

1. Function Block：targetObjectやBlockのカタマリに作用。上下に凹凸のあるもの<br>![スクリーンショット 2021-02-19 22.58.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/577095bf-9d05-774d-4408-00209b03396c.png)

1. Operation Block：Function Blockの入力値として用いる。上下に凹凸がない。<br>![スクリーンショット 2021-02-19 22.59.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/00ec1d69-4e48-6d62-4244-c325f645e62c.png)


ブロックの分類は７種類ある

1. Event：イベントリスナーとして動く<br> ![スクリーンショット 2021-02-19 22.32.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/1a9b671f-8726-78c7-820e-001efacfd783.png)

1. Motion：targetObjectの動きや位置、方向に関連するブロック<br>![スクリーンショット 2021-02-19 22.34.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/6a6e9d42-29d0-c318-9044-4626963010e6.png)

1. Control：Blockのカタマリに作用。条件分岐や繰り返し、待機処理など<br>![スクリーンショット 2021-02-19 22.35.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/baf407c7-9a1f-06cb-e8ac-ea76733b4489.png)

1. Looks：targetObjectの見た目を変える<br>![スクリーンショット 2021-02-19 22.36.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/06c148ea-a325-8522-b9b9-457783daa690.png)

1. Sounds：音を鳴らす<br>![スクリーンショット 2021-02-19 22.36.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/7c21cb16-7864-b6a8-6331-90d7d548b66a.png)

1. Operators：論理演算子。Function Blockに用いる<br>![スクリーンショット 2021-02-19 22.36.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/99026094-e35e-eeb3-69e6-a8ad19f3c492.png)

1. Variable：変数に関する操作ブロック。新しい変数を作成するためのボタンがあり、新しい変数名を挿入して作成を確認するためのパネルが開く。<br>![スクリーンショット 2021-02-19 22.42.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/c359b867-8687-710c-bfc3-2ae263356ab6.png)<br>![スクリーンショット 2021-02-19 23.01.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/df503207-c414-e7e0-33d3-95f4019ca413.png)


  
## 各targetObjectがそれぞれ独自のPG環境を持つ
- シーン上に複数のtargetObjectがあり、それぞれにCode Blockのセットがある時もある。抽象化の目的で、各targetObjectはそれぞれ独自のCanvasを持つ。
- ブロックをプログラミング環境にドロップできる
- 目的のブロックを押したまま/右クリックすることで、アクションを含むメニューパネルを開くことができる。（コピーと削除）
- コードは保存及びロードができる


## Codeの保存機能の仕組み
- 現在のキャンバスブロックを単純なテキストベースのファイル（.BE）に変換し保存する。
- 任意のテキストエディタで開くことができ、データの操作が容易である。
- 逆に、ブロックのテキストへの変換をするカスタム関数がある。
- 保存したコードは、Application.persistentDataPathからUnityによって提供される永続データディレクトリの「SavedCodes」というフォルダに保存される。
- [保存]ボタンをクリックするとポップアップメニューが開く
  - 保存したコードのリストが見れる
  - 新規保存、上書き保存、ファイルの削除ができる。
![スクリーンショット 2021-02-19 23.06.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/a5b9dd14-bbb1-3705-7806-31b7d027df30.png)

## Codeのロード機能の仕組み
- .BEファイルに書かれたブロック配置をプログラミング環境に再現する。
- [コードのロード]ボタンをクリックするとメニューが開く
  - ロードおよび削除するファイルが選択できる。
![スクリーンショット 2021-02-19 23.07.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/653c4a08-c8d0-0696-1983-bb4e7a010734.png)

## Joy Stickについて
- ４つの方向ボタン（↑→↓←）と２つのアクションボタン（A,B）がある
- Eventsブロックに対応するイベントを発生させられる。（Aボタンが押されたら…など）
![スクリーンショット 2021-02-19 23.11.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/fd69e607-50ff-8dbe-232b-5fd68db3a345.png)

# Model Class
### BE TargetObject
ScratchでいうSpriteみたいなもの。再生ボタンや他のトリガーを介して自身のトリガーがActiveになった時に処理が走る

### BE Block
動かすコードブロックのこと。大きく次の５つに大別される

1. Trigger
   1. ブロックの塊の上に配置。
   2. イベントリスナーとして動作
   3. ブロックの塊をActiveにするのに必要なFunction Block

この形の<br>![スクリーンショット 2021-02-19 23.12.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/9b0a215c-7473-a73d-93fe-7780a229ef3c.png)

2. Simple
   1. サイクルごとに1回実行される
   2. 子ブロックをラップしないFunction Block

この形の<br>![スクリーンショット 2021-02-19 23.13.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/f501cb00-0c73-aa53-9bf3-d9a4eaff60a8.png)

3. Loop
   1. ループ命令を実行するFunction Block
   2. ループ条件が満たされている場合に、実行される子ブロックをラップ

この形の<br>![スクリーンショット 2021-02-19 23.20.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/3d15a4f0-4d58-ef80-9977-5a402457b595.png)

4. Conditional
   1. 条件付き命令を実行
   2. 条件が満たされた場合に実行される子ブロックをラップ

この形の<br>![スクリーンショット 2021-02-19 23.20.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/3d15a4f0-4d58-ef80-9977-5a402457b595.png)

5. Operation
   1. Function Blockのヘッダーに配置される
   2. 真偽値を文字列で返す
   
この形の<br>![スクリーンショット 2021-02-19 23.24.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/74c2e545-4246-92ea-eba4-6da812edb6d4.png)


### BE Inputs
各ブロックのヘッダーには入力値を入力/選択する箇所がある。
![スクリーンショット 2021-02-19 23.32.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/3779657a-4ef5-f572-00e3-bc4b0cefa753.png)
これらの値は、BEInputと呼ばれる型で、BEBlockオブジェクトに格納されている。
BEInputには、文字列および浮動小数点値としての入力のマップがある。

1. string[ ]型のstringValues
2. float[ ]型のnumberValues

これらはフラグisStringを持つ。（少なくとも1つの入力値を数値に変換できない場合にtrueになる）

【2つの入力を持つBE BlockオブジェクトbeBlockの例】

最初の入力値はbeBlock.BeInputs.stringValues [0]またはbeBlock.BeInputs.numberValues [0]で呼び出す。
2番目の入力値は、beBlock.BeInputs.stringValues [1]またはbeBlock.BeInputs.numberValues [1]で呼び出す。

すでに実装されているブロックでは文字列と数値の両方の入力を考慮しているため、プレーヤーは特に型を気にせずにブロックを使用できる。

# BE CONTROLLER
プログラミング環境に配置され、ターゲットオブジェクトに関連付けられたすべてのActiveなブロック処理を実行。
大きく次の役割を持つ。
1. トリガーブロックを開始
2. すべてのブロックグループを停止
3. 対応するブロックグループシーケンスの次のブロックをアクティブ化
4. その他のメソッド

## BE Instruction
BE Instructionは、コントローラーによって呼び出されるメソッド定義するコントローラークラス
BEFunctionメソッドとBEOperationメソッドの一方または両方が含まれる。


## BE Variables
実際のコーディング手法にできるだけ近くするため、Function Blockとして使用したり、Function Blockの入力値として設定したりする変数のこと。
BE変数には、変数の名前と値を格納するBEVariableと呼ばれるカスタムタイプのリストからアクセスする。
BE CONTROLLERには、SetVariable（string、string）とGetVariable（string）がすでにある。
ColorとSoundに対応するBE変数Listもある

## Play Next Method
名前の通り、次の命令処理に移行することを通知するメソッド。
BEFunctionメソッドの中には次のどちらかは必須

1. PLAY NEXT OUTSIDE：Simpleの最後やLoopの最後、Conditionalで条件を満たさない場合などに呼ばれる
2. PLAY NEXT INSIDE：Trigger、Loop、Conditionalで条件を満たす場合などに呼ばれる

![スクリーンショット 2021-02-20 0.05.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/d0e87740-dec4-98fe-aaaf-c5cff315ecae.png)


# プログラミング環境に組んだブロックが実行される仕組み
PlayNextMethodによって再帰的な構造になっている

- 何らかのトリガーで各BlockのisActiveがtrueになったら処理が動く。
- 処理が終わったらPlayNextOutsideが呼ばれ、次のBlockに入っていく
- その際に次のBlockのisActiveが変わる処理が走る（多分）

## Scene
BE Con​​trollerインスペクターの下部には、Sceneと呼ばれるセクションがある。
シーンで現在アクティブなすべてのターゲットオブジェクトを識別し、そのプログラミング環境のActivationを決定する。
次節のWorkshopを用いて複数のTargetObjectを用いる場合は、BE Controllerの持つ「Scene」からSingleSelectionを選択しておけば、一度に表示されるプログラミング環境を1つに制限できる。
![スクリーンショット 2021-02-20 0.18.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/b34930a4-edee-e97f-93de-1a8e26bf91f7.png)


## Workshop
BE Controllerのインスペクターの下部には、Workshopと呼ばれるセクションがある。
いくつかの変数を設定するだけで新しいTargetObjectやBlockを作成できる![スクリーンショット 2021-02-20 0.20.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/18c4775f-6609-53a8-ef40-e73761b420d1.png)


# オリジナルのブロックを作る方法
BE Controllerのインスペクターの下部のWorkshopから、いくつかの変数を設定するだけで新しいターゲットオブジェクトとブロックを作ることができる。まずはBlockタイプと動作ルールを設計する。

今回は例として、入力値を受け取って指定回数あいさつをConsole表示するようなBlockを作ってみる

以下の処理はWorkshopセクションで行う

- Blockの表示名とタイプを決める。（今回は名前はFuga、ブロックタイプはSimple）
- Blockヘッダー作成する。表示するテキストと入力タイプを正しい位置に配置してHeaderGuidelineを設定する

HeaderGuideline記入例は以下の通り

```
[inputfield = 1]
回あいさつする
```
![スクリーンショット 2021-02-20 0.33.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/0360ac6d-abb9-58f7-af11-e6d927adf014.png)

色々この辺を設定したら「ビルドブロック」ボタンを押すと、「ブロックと命令が作成されました」というメッセージが表示される。
スタックに命令をインポートしようとしています...」、メッセージが消えるまで待てば完了。

結果として、以下が作成される

- スクロールビューに表示される新しいブロック（名前はFuga）
- BE Instruction Script フォルダ内に新しいスクリプト(Fuga.cs)
- Instruction Stackにコンポーネントとして追加されたInstruction
- 新しいプレハブ（resources/prefabs/Blocks/Fuga.prefab）

スクロールビューでは一番上に生成されるので適当な場所に置き直すこと![スクリーンショット 2021-02-20 0.35.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/ed678db8-dbbf-1bee-4f4b-a91bf78ae089.png)
![スクリーンショット 2021-02-20 0.36.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/56d91a0e-57d6-ba26-ed3a-a273eedd1b62.png)

![スクリーンショット 2021-02-20 0.38.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/812f0147-ecf3-343b-0818-b18a2c032b27.png)


ここまででプログラミング環境にD＆Dできるブロックは完成したので、次はオブジェクトとスクリプトを対応させていく
新しく作成されたスクリプトには、命令動作のコーディングをするために必要なテンプレートがすでに含まれている
以下は作成されたばかりのスクリプト（コメントは適宜加えた）

```Fuga.cs
using UnityEngine;
using System.Collections;
using System;

public class Fuga : BEInstruction
{
	// Use this for Operations
	public override string BEOperation(BETargetObject targetObject, BEBlock beBlock)
	{
		string result = "0";
		
		//入力値に関してここで色々いじったり判定したりして結果を0か1の文字列で返す（なんでboolじゃないのかな？）
		
		return result;
	}
	
	// Use this for Functions
	public override void BEFunction(BETargetObject targetObject, BEBlock beBlock)
	{
		// 入力値を受け取るときはこんな感じ
		string option = beBlock.BeInputs.stringValues[0];
		
		// 子要素のBlockに進むときはPlayNextInside(beBlock)、それ以外はPlayNextOutside(beBlock)を用いる。BEFunctionメソッド内に必須
		BeController.PlayNextOutside(beBlock);
	}
}
```

今回はコンソール表示をしたいので、BEFunctionの処理を次のように記述

```Fuga.cs
public override void BEFunction(BETargetObject targetObject, BEBlock beBlock)
	{
		int option = int.Parse(beBlock.BeInputs.stringValues[0]);
		for (int i = 0; i < option; i++)
		{
			Debug.Log("おはよう");
		}
		BeController.PlayNextOutside(beBlock);
	}
```

結果はこんな感じ。下にBlockを繋げても問題なく動く
![画面収録-2021-02-20-0.49.29.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/3d2fb259-5999-c695-54f0-220353488422.gif)

# 最後に
25ドルでここまで遊べるのはなかなか強い。
一点気になるとすればブロックを繋げるのが少々難しい…
その辺の判定とかUIを修正すれば面白いものが作れそうな有料アセットだと思った。

ここまで読んでいただきありがとうございました。
この記事が何かのお役に立てたなら幸いです。