Pythonプログラミングでマインクラフトの家を作るための5つのステップ

Pocket

みなさま、こんにちは。今回は、前回ご紹介したマインクラフトとpython をつなげてプログラミングができる環境を使って、家を作ってみました。

最初にお断りしておきますが、この記事はマインクラフトの攻略を目的にしたものではなく、あくまでもマインクラフトを通じてプログラミングの解説をする記事です。マインクラフトの詳しい内容に関して、著者は完全には理解していないこともありますので、ご了承ください。今回の記事は下記のwebサイトを参考にさせていただきました。この場を借りて御礼申し上げます。

https://www.q-movie.com/minecraft

さあ、5つのステップ行ってみましょう。

ステップ1 整地作業

 整地とは要するに広い平らな場所を作るという意味です。建物を立てるためにそれなりのスペースが必要になります。そこで整地をするためのプログラムを先ずは書きます。この mcpi のライブラリで用意されている関数(function)は、ブロックを一つ置くための関数 setBlock とブロックをまとめて置くための関数setBlocks があります。今回はまとめてブロックを置くための setBlocksを使います。ここまで書いて、あれ?と思った方がいるかもしれません。整地するためには、ブロックを消すのではないか?と。おっしゃるようにブロックを消すのですが、ブロックを消すための関数も setBlocksになります。setBlocks で与える引数(パラメタ)は次のようになっています。

setBlocks(x0, y0, z0, x1, y1, z1, blockType, [blockData])

(x0, y0, z0) で一つの地点を表していて、(x1, y1, z1) でもう一つの地点を表しています。この関数は、地点0 から地点1までの間を指定されたブロックで埋める、という動作になります。そして、blockType は埋めるブロックの種類になります。ブロックの種類は数百種類用意されており、ここで解説するためにはかなりの文章量が必要になるので今回は省略させていただきます。詳しくはこのページなどをご覧ください。また、blockDataというのは、ブロックによってはいくつかタイプが指定できるものがあり、そのタイプを指定したいときに使います。省略も可能です。今回の整地で使うのは、blockTypeを0とした場合です。0は「ブロックがない」ということを意味しているので、ブロックをなくすという動作になるのです。コードの中では、mcpi/block.py に規定されている定数の名前を使ってブロックIDを指定しました。block.AIR.id と指定すると、空気=なにもない、という意味になります。

ここで次のような関数を作ります。

def flatspace という部分が関数の名前とパラメタの宣言となります。flatspace という名前の関数で、パラメタが6つあるということを意味してます。x, y, zはある地点を表していて、width, height, depth はフラットにしてしまいたい幅、高さ、奥行きを示します。

main 関数の部分では、このflatspaceを使ってまっさらな空間を作ってみましょう。まず現在位置をgetPos()で取得します。次にその位置から2つ高さを下げた地点から、x方向、z方向にそれぞれ30マス、高さ方向に5マス削るようにしてみます。

実行する前の状態

before

実行した後の状態

実行後

絵で見てわかるようにまっさらなスペースが一瞬のうちにできています。これで整地は終わりました。

ステップ2 土台、柱、梁そして壁

土台

 次に土台作りになります。土台は石のブロックを使って家の周囲の柱と壁が出来る部分に作ります。土台のブロックには丸石を使いました。丸石のブロックIDは、1です。先ほども使った setBlocksを使って1列ずつブロックを並べていくことで土台ができます。

土台の図

それぞれの土台の線の終端の座標を図に示します。座標をそれぞれのsetBlocks関数で指定することで線を引くことができます。コードは次のようになりました。土台で使っているのは、block.STONE (石)です。

では、この関数を使うとどうなるでしょうか。メインの中では、先ほどのflatspaceに続けてmakeBase という関数を呼び出して実現しています。

そしてできたのが図のような土台です。

土台

柱と梁と壁

 次に柱を作ります。柱は図に示すように6箇所立てることにします。座標位置はそれぞれ図に示す通りですが、高さは3ブロック分を土台の上から立てることにします。

柱、梁、壁

用意するコードとしては、先ほどと同様に(x, y, z)を起点として、幅、高さ、奥行きを指定することで、図に示した位置に柱を立て、指定した高さに対して梁を入れます。梁は先ほどの土台と基本的には同じです。(本当は処理の共通化を図るべきですが、いまはそのままにしておきます。)

ここまでの状態で動作させた場合、次の図に示すような状態になるはずです。

柱と梁

 そして、壁を作ります。壁は、柱と梁の間にあるスペースの部分に木材のブロックを埋めていくことで作ります。そのため、基本的な考え方は土台や梁と同様なのですが、柱の隣から次の柱の隣までの範囲を指定することと、高さ方向にも土台から梁の間(今回の場合は2マス分)を指定する必要があります。また、柱が4本入っているところがあるので、そちらに関しては、玄関を作る真ん中のスペースを除き、壁を埋めることにします。さきほどしめしたコードに次の分を追加します。ここで、block.WOOD_PLANKS が木材のブロック、block.WOOD_PLANKS_BIRCH.data がバーチ材(黄土色の木材)のブロックタイプを意味しています。

動作結果は次のようになります。これで1階部分の柱、梁、壁までができました。

壁を入れる

ステップ3 玄関と窓

 次に玄関と窓を入れてみましょう。玄関は2本の柱5、6の間になります。下の土台をなくし、柱の外側に石のブロックでゲートを作る形にします。そしてゲートの中に扉を入れます。(扉に関しては、最後に説明します。)

次に窓を入れます。窓は両脇に柱で使った原木を立てて間にガラスを入れます。

ここまでくると1階部分はほぼ完成です。最後に天井を入れます。天井は同じブロックで1階層敷き詰めるだけにしています。開始地点と幅、奥行きを指定して作ります。次のような関数を用意しました。

ステップ4 2階を作る

 次に2階部分を作ります。2階は基本的には1階とほぼ同様のパーツを使います。

  • 柱、梁、壁を立てる
  • 窓を作る
  • 天井を作る

実際にコードを書くと次のような形になりました。柱、梁、壁を入れて、窓を入れて、天井を入れる関数を順番に呼び出しています。この要領で3階、4階と作ることができます。

ステップ5 屋根を作る

 最後に屋根を作ります。屋根は断面図で書きますと次の図のような形で作ります。ブロックを7段積み上げて作りますが、外側の色が変わっている部分はレンガとします。色のない部分は壁だけを作ります。レンガは、一番上以外は階段のブロックを使うことで、それらしく見えるようになります。

屋根

 処理としてはまず壁の部分を作ります。壁は座標でいうとx と x+13 の2枚作ります。1段上に上がるごとに、右側と左側の長さを短くするように配置します。こういう処理の場合は、繰り返し文の for 文をつかい、次のような繰り返し処理をして6段分作ります。

次に外側のレンガで屋根を作ります。左側と右、そして中央部分の3箇所の屋根をそれぞれのパーツで作ります。ここで、setBlocks関数の後ろ二つのパラメタにおいて、ブロックIDを108としているのが、階段レンガのブロックのIDです。ただ、なぜかライブラリで用意されているblockクラスの中に定義がなく、今回はとりあえず数字を入れています。また、type を2もしくは3としているのは、階段の向きを指定しています。

以上を踏まえて作った関数が下記です。

以上を組み合わせて作った家が下記のような形になりました。見た目だけで内装入れてませんが、これだけの建物を作ろうとするとそれなりに時間がかかかるかと思いますので、プログラムで書くことができると楽になるのではないでしょうか。

house

a roof of house

最後に、とりあえず書いてみたコードを示します。最初の説明では、1階部分はメイン部分に直接描いてましたが、makeFirstFloorという関数を作り、処理を分けました。

まとめ

 いかがでしたでしょうか。今回は、マインクラフトで家を作るプログラムをご紹介しました。作る過程やどのように考えながら作るかがわかってきたかと思います。内装や階段などは入れてません。この点は future work とさせてください。

 また、エントランス部分のドアが実は正しく設置できていません。いろいろ調べているのですが、type を指定して正しそうな値を入れてみたのですが、まだうまくできていないので、もう少し時間をください。分かりましたら、記事修正させていただきます。

次回は違う建物か何かを作る内容で書いてみたいと思います。お楽しみに。

訂正文(2016/9/13)

block id について

先日記事をリリースした後に、問題点が発覚しました。まず、最初にお詫びをしないといけないのですが、若干説明が曖昧な箇所があるので、今一度説明をします。

マインクラフトで扱うブロックに関して、mcpi/block.py に定義されていると説明しました。ただ、この部分の理解を少々曖昧なまま進めてしまったのがよろしくなかったようで、元のコードの書き方だとあまりよろしくないことがわかりました。

setBlocks(x0, y0, z0, x1, y1, z1, blockType, [blockData])

setBlocksの説明で、blockType を指定するとしていますが、この blockType を指定するときに、block.AIR とすると、いう記述を最初に説明していました。ただ、これは正しくなく、正しくは、block.AIR.id と指定する必要があります。理由としては、block は Block クラスで定義されており、メンバ変数として、id, type を持っているためです。block.AIR という指定では、AIR の1インスタンスをたまたま呼び出していて、たまたま最初のデータの値が、id であるため、うまく動いているように見えるだけ、ということでした。

実は、stairs を使った場合、うまく動かない場合があるので、かならずid を最後に指定するようにしてください。

扉について

次に、扉の部分がうまくできていない状態で記事をリリースしてしまったのですが、調査の結果、どうすべきか判明しました。扉は特殊なブロックで、通常のブロックは1マス分しか使わないのですが、1つの扉で2マス分使用するため、かならずブロックを2つ対で作る必要があります。

https://hydra-media.cursecdn.com/minecraft-ja.gamepedia.com/f/fd/12w06a-doors-idx.png

どのように作るかですが、上記のページの絵に描かれているデータをblockData として指定します。分類としては、人とドアの位置関係、ドアが開いているか、しまっているか、取っ手が右か左かの3つの要素があります。

  • ドアが西側に向いていて、人が東にいる ー> 1
  • ドアが東側に向いていて、人が西にいる ー> 3
  • ドアが北側に向いていて、人が南にいる ー> 0
  • ドアが南側に向いていて、人が北にいる ー> 2

1バイトのデータで表現しますが、上の4ビットが向き、下の4ビットが取っ手やドアの状態を示しています。リンクの先の図にあるように、取っ手のデータはドアの上側のブロックになります。

  • 取っ手が右 ー> 8
  • 取っ手が左 ー> 9

下のブロックは、3ビット目(0x4)が、

  • あいている  ー> 1
  • しまっている ー> 0

下位2ビットで

  • 西 0
  • 北 1
  • 東 2
  • 南 3

となります。

今回の家で使ったドアに関しては、西向きでしまっているドアを作ることから、下のブロックは0です。そのため、下記のようなコードを使うことで、ドアが2枚作られることになります。ここに訂正させていただきます。

Pocket

この投稿へのコメント

コメントはありません。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

この投稿へのトラックバック

トラックバックはありません。

トラックバック URL