「{acacia_fence_gate,air,bed,lit_redstone_lamp……}」のような感じです。配列は0から始まるので、Excel表の番号とblockList内の番地が同じになるわけです。
[HTTPリクエスト]
現在の座標を取得するために、ダミーでブロックをひとつ設置します。ポジションを取得できるAPIがあればよかったのですが、見当たらなかったので、この方法を使うことにしました。
setblockでブロックを配置すると、配置したブロックのポジションがJSONで戻ってきます。ここから現在の座標を取得します。
[HTTPリクエスト]アクティビティを配置し、ウィザードで次のパラメーターを指定します。
- エンドポイント
http://localhost:8080/setblock - パラメーター
position → ~0 ~0 ~0
tileName → stone
「~0 ~0 ~0」の「~」は相対座標を示す記号です。つまり、プレイヤーの現在位置にブロックを設置することになります。
なお、パラメーターは、ウィザードから設定するときは、「~0 ~0 ~0」のように、そのまま記入しますが、プロパティパネルで設定するときは次の画面のように「"~0 ~0 ~0"」のように、値を「""」で囲む必要があるので要注意です。
戻ってきた座標のデータは、プロパティパネルの「結果」で受け取ります。[Ctrl]+[K]キーを押して「retPosition」という変数を作成し、ここに格納しましょう(型はJObjectに自動設定される)。
[JSONをデシリアライズ]
座標データは、次のようなJSON形式になっていますが、そのままでは構造化されていませんので、このアクティビティを使って、JSONとして扱えるように再構造化する必要があります。
{
"position": {
"x": 184,
"y": 66,
"z": 21
}
}
座標が格納された変数(retPosition)を指定して読み込み、プロパティパネルの「出力」で[Ctrl]+[K]キーを押して「startPosition」変数を作成し、再構造化したデータをJSONオブジェクトとして、この変数に格納し直します。
[代入]で座標を取り出す
JSONから座標のデータを取り出しましょう。[代入]アクティビティを配置し、次のように、左辺で[Ctrl]+[K]キーで「startX」変数を作成、右辺に「Ctype(startPosition("position")("x"),Integer)」と入力します。
startX = Ctype(startPosition("position")("x"),Integer)
JSONオブジェクトが格納されたstartPositionから、「"posittion"」の「"X"」の値を指定し、これを「Ctype」でInteger(整数)に変換しています。これで、座標に値を足していけるようになりました。
同様に、Y座標(startY)、Z座標(startX)も取得しておきましょう。
後半パートを作る
続いて後半パートを作ります。ここでは、設計図を読み取り、列、行、シートごとに処理を繰り返しながら、ブロックを配置していきます。
Excelアプリケーションスコープ
まずは、設計図を読み込みます。ここではプロジェクトと同じフォルダーに保存された「"design.xlsx"」を指定しましたが、別のファイルの場合はパスを指定してください。
なお、今回はプロパティで[自動保存]のチェックを外します。結構、長い間、ファイルを開きっぱなしで操作をするので、その間に自動保存がかからないようにします。
[ワークブックの全シートを取得]
設計図は、シートがブロックの各層に対応していますので、シートごとに処理しなければなりません。このため、このアクティビティで、全シートの名前を取得します。
プロパティパネルの「出力」で[Ctrl]+[K]キーを押して「sheetNames」変数を作成し、ここに格納します。
[繰り返し(コレクションの各要素)]
シートを1枚ずつ処理していきましょう。[繰り返し(コレクションの各要素)]は配列などのコレクションから、値をひとつずつ取り出して、繰り返し処理ができるアクティビティです。
「コレクション」にシート名の配列「sheetNames」を指定し、「要素」を「snItem」に変更しておきます。
プロパティパネルでは、要素に取り出した値を扱うときの型を指定します。「TypeArgument」を「String」にしておきましょう。
また、「出力」の「現在のインデックス」で[Ctrl]+[K]キーを押して「sheetIdx」変数に格納します。これで、sheetIdx変数に最初のシートが「0」、次のシートが「1」というように順番に値が入ります。
シートは、ブロックの層、つまり座標では「Y座標」を示しています。この値は、後でブロックを配置するときのY座標を計算するときに使います。
[範囲を読み込み]
シートごとの処理をしていきます。まずは、現在のシートから設計図を読み込みます。[範囲を読み込み]アクティビティを使って、指定したシートの内容をデータテーブルとして格納します。
シートに「snItem」(繰り返し処理の現在のシート)を指定し、プロパティパネルの「データテーブル」で[Ctrl]+[K]キーを押して「designTable」と変数を指定します。
これで、設計図に記載されているブロックの番号が、縦横2次元配列のデータテーブルとして格納されます。
[繰り返し(各行)]
先ほどの[繰り返し(コレクションの各要素)]の中に、さらに繰り返し処理を配置します。今度は、データテーブルの値を処理するので、[繰り返し(各行)]を使います。このアクティビティで、データテーブルのような2次元配列を行ごとに処理できます。「コレクション」にデータテーブルの「designTable」を指定しましょう。
そして、プロパティパネルの「現在のインデックス」で[Ctrl]+[K]キーを押して「rowIdx」変数を作成します。この値は、現在処理している行の値が順番に入ります。
図面上では、行は奥行き、つまりZ軸の値となりますので、ブロックを配置するポジションを計算するときに、この値を使います。
[繰り返し(コレクションの各要素)]
3重繰り返しの最後の部分です。先ほどの[繰り返し(各行)]で、図面の行を順番に処理しますが、各行には、列ごとのデータが1次配列として格納されています。この列データを、ここで順番に処理するわけです。
「コレクション」に「row.ItemArray」と指定します。「row」は[繰り返し(各行)]の現在の行ですから、これを「.ItemArray」で配列として指定しているわけです。
プロパティパネルでは、「TypeArgument」で「Object」を選択します。これで、先の「.ItemArray」のようにオブジェクトとして扱えます(そういう意味では先にこちらを設定した方がわかりやすい)。
そして、他の繰り返しと同様に、「現在のインデックス」を設定します。列なので、X座標の値として使います。[Ctrl]+[K]キーで「colIdx」変数に格納しておきましょう。
[代入]で座標を作る
さあ、これでブロックを配置するために必要な座標の情報が集まりました。これをCode Connectionが認識できる座標の形式に整形します。
[代入]アクティビティの左辺で[Ctrl]+[K]キーで「setPosition」変数を作成し、右辺に「(startX+colIdx).ToString+" "+(startY+sheetIdx).ToString+" "+(startZ+rowIdx).ToString」と入力します。
setPosition = (startX+colIdx).ToString+" "+(startY+sheetIdx).ToString+" "+(startZ+rowIdx).ToString
最終的に作りたいのは、「184 66 22」のような座標の並びです。間にスペースを挟みながら、X、Y、Z座標を並べます。
座標は、「startX」で最初に取得したスタート地点の座標に図面上のX座標「colIdx」を足すことで求められます。これを文字やスペースを連結したいので、「.ToString」を付けて文字列に変換します。同様に、Y、Zを繋げていけば最終的な座標が完成します。
[条件分岐]
座標ができたので、次は、その座標に置くブロックを取得します。
ブロックは、設計図のセルに番号で記入されていました。これは、designTable変数にデータテーブルとして格納され、繰り返し処理によって、現在の行、現在の列の値が「item」に格納されています。
ただし、ここでひとつ注意点があります。設計図には、ブロック以外の値も存在します。それは、図面の両端を示す「XX」(前回記事参照)、そしてブロックの指定がない場所(空)です。
つまり、この2つの値だった場合は、ブロックを置く必要はないわけです。
そこで、[条件分岐]を設置して、次のような条件を「Condition」に記載します。
item.ToString = "XX" or String.IsNullOrEmpty(item.ToString)
つまり、itemを文字列にした値が、「XX」もしくはNullか空だったら、「Then」で何も処理をしないという分岐です。
[代入]でブロック番号をブロック名にする
一方、ブロック番号が記載されている場合は、ブロックを配置する必要があります。それが、Else側の処理です。
ここには、まず[代入]アクティビティを配置します。itemの値は、ブロック番号ですが、この値は文字列として格納されています。これを、数値として扱えるようにするために、一旦、Integerに変換します。
左辺で[Ctrl]+[K]キーを押して「blockNum」変数を作成し、右辺に「CType(item,Integer)」と入力します。これで、itemの値を整数に変換できます。
blockNum = CType(item,Integer)
HTTPリクエスト
いよいよ最後のアクティビティです。
座標も、設置するブロック(の番号)もわかったので、APIを呼び出して、ブロックを配置します。
新しくHTTPリクエストを配置してもかまいませんが、冒頭で使ったHTTPリクエストを再利用しましょう。「UiPath Studio」では、アクティビティをコピペで複製できるので、上でコピーして、この場所に貼り付けます。
さて、元のHTTPリクエストは、「~0 ~0 ~0」に「stone」を設置する命令です。エンドポイントは同じですが、パラメーターが違うので、プロパティパネルでこの部分を変更します。
「オプション」の「パラメーター」で「…」をクリックし、「パラメーターパネル」を表示後、次のようにパラメーターを書き換えます。
- パラメーター
position → setPosition
tileName → blockList(blockNum).toString
setPositionは、先の代入で生成した現在の座標です。一方、blockList(blockNum).toStringは、ブロック名を指定しています。
冒頭でitemlist.xlsxから読み込んだブロック名の一覧は「blockList」変数に格納されています。一方、配置したいブロックは、先にblockNumとして番号を取得済みです。この番号を指定して、blockListからブロック名を取り出し、「.toString」で文字列に変換すればいいわけです。
これで、すべてのアクティビティができました。実際に実行して試してみてください。もちろん、実行する際は、「Code Connection for Minecraft」と「Minecraft」を接続しておく必要があります。前回の記事を参考に、接続した状態でワークフローを実行しましょう。