simplestarの技術ブログ

目的を書いて、思想と試行、結果と考察、そして具体的な手段を記録します。

マイクロワールドの試作1

今回は、こちらの記事の続きです。
simplestar-tech.hatenablog.com
具体的にマイクロワールドを作っていきます。

世界を構成している要素

構想で登場したものは以下の三つでした

  • ワールド(ブロックによって敷き詰められた世界)
  • ブロック(内部に複数のオブジェクト)
  • エージェント(風ブロックの中を移動する、ブロックや内部のオブジェクトとメッセージのやり取り)

まずはワールドについて構想してきたことを列挙します。

  • 平面充足は正三角形、正三角柱を敷き詰めた3次元空間
  • 三角柱は四元素のいずれかに分類される(火、風、土、水)
  • 火ブロックは面接続されたブロックが土ブロックだった場合、火の耐性に応じてアクションを誘発するブロック間の火隣接メッセージを伝える(受け取ったブロックが燃える、溶ける、高温になる、など)
  • 風ブロックは面接続されたブロックが同じ風ブロックだった場合、風隣接メッセージを伝える(熱、ガス、臭い、メッセージをバケツリレー的に伝播させるなど)
  • 土ブロックは基本的に重力メッセージによって落下したがる、周囲に接着するブロックか、直下に落下しないブロックがあれば、落下はしない。内包しているオブジェクトから周囲オブジェクトへ伝えたいメッセージがあれば伝える。(におい、音、熱など)
  • 水ブロックは基本的に低い場所へ流動したがる、周囲に土ブロックと直下に土ブロックがれば固定され、そのいずれかに風ブロックがあれば、その方向へ流動する。水位という属性があり、隣接する水ブロックの水位が低い方へも流動し、自身の水位を移動した分だけ減らす。

すでにブロックについて書いてきましたが、そのブロックとオブジェクトとメッセージについて動的な部分を詳しく見ていきます。

例えば、水ブロックですけど、水位パラメータは水ブロックが持つのでしょうか?
そのほか、水ブロックは内部に液体オブジェクトを持ち、そのオブジェクトに粘性や熱といったパラメータがあるのでしょうか?
複数や単体のブロックで構成される、海、湖、池、沼、バケツの水、溶岩だまりといった概念や
それを構成している、海水、真水、溶岩、ヘドロといった概念を
ブロックとして持たせるのか、オブジェクトとして持つのか、すべてメッセージとするのか
設計していく必要があります。

エージェントについて考えると

こうしたワールドの一部を切り取って、未来予測ともいえるシミュレーションを行い、アクションと結果の関係から行動の利益を導き出せるようにする必要があります。
ワールドは世界共通の現象だったとしても、エージェントはその観測した空間をブラックボードに一度コピーして、時間軸を操作してシミュレーションを行い、時に途中までのシミュレーションに戻って
何度も選択できるアクションをやり直し、最適な解を導き出す努力ができなくてはいけません。
これを可能にする設計にする必要もあります。

うーん、いざ試作するといっても、また構想に入ってしまいました。

水ブロックは、水としてふるまうための情報を、液体オブジェクトから引き出すということになる感じでしょうか?
その液体オブジェクトには必ず、流動の振る舞いを決定するパラメータをすべて持っていなくてはなりませんよね。
ある液体オブジェクトという基底クラスがあり、そこには液体特有のパラメータ(粘性、熱、揮発性)があるとします、海水、真水、溶岩、ヘドロはそれらを継承して作られたものとすれば、コード量は少なくて済みますね。
また、そうした派生オブジェクトに、メッセージを流し込んだときに、例えば溶岩に海水が面接続したときに、それぞれがリアクションを取ることになり、派生したコードの振る舞いが実行されるという設計です。
溶岩のリアクションは接続されたブロックに奪われた熱の分だけ、凝固へ向かい、ブロックを土に変えて、そのブロックの構成オブジェクトが岩石や金属、鉱石にすげ変わったり、海水のリアクションは受け取った熱の分だけ蒸発して、隣接する空気ブロックへ水蒸気を流したり、自ブロックが空気ブロックに変わって、水位とは異なる構成成分としてミネラルである塩オブジェクトが空気ブロックの中に残り、その成分はその成分量を保持したまま落下する、たい積すると塩の土ブロックになるなど。
さらに、圧力・応力というものが土ブロックにはかかり、最大応力をオーバーすると土ブロックが破断し、それに接着していたブロックが落下可能なら落下するなど

重要なことは、そのワールドを構築する際の基本的なメカニズムを正しく抽象化して、それを抽象クラスとして設計に盛り込むことです。

また、新しい概念が登場してきたので、メモします。
水位に似た、構成割合というものです。
確かに液体が熱せられて蒸発したときに、100%の土ブロックになるのは質量保存に反しすぎですね。
物質オブジェクトについても個体、液体、気体という状態変化をするという、基底の振る舞いというものが見えてきました。

マイクロワールドを動き回るエージェントには、こうした化学要素のシミュレーションも行えてほしいわけで、そのためにはワールドにそうしたシミュレーションが入る必要があります。
また、土ブロックも圧力・応力を与えると、一定の閾値で破壊、破断するという点も、建築において重要になってきます。

エージェントには、作物を育てたり、建築して自然の驚異に立ち向かったり、お金を使った経済活動を行ったり、集団で力を合わせたりしてほしいところだけど
そういった細かい部分は、ワールドの土台が固まってきてからにします。

では、ここまで構想してきたものを少しずつ形にしてみようと思います。

世界を構成している要素の実装

リソースは有限ですので、地上というものがあるのならば、地下深くまで掘り進めると限界深度となる、それ以上掘り進められない岩盤ブロックが現れるものとし
天高く積み上げていっても、一番下の土ブロックが圧壊または溶岩と化して、積み上げたブロックが沈み込み、それ以上高いところには行けないものとします。
いつか、空を飛ぶ機械をエージェントが発明して飛んだとしても、限界高度までたどり着いたら、それ以上上昇できない風ブロックがあり、風ブロック以外が面接続すると破壊されることとします。

これらが示すのは3次元空間でいうところの高さのバッファ長であり、まずはテストのためにすぐに限界が来る狭い空間として、海抜を深度0とするなら、地底に-10、天空に10ブロックとします。
まずは、深度-10に落下も破壊もない土ブロック(岩盤)を敷き詰めます。
世界が巨大な球体とするなら、地平は循環するため、例えばx方向にまっすぐ20進んだら、元のブロックに到達するという循環があるものとします。
なので、地平の果ては存在せず、海が断崖絶壁を滝のように流れ落ちるような果ては存在しないものとします。(エージェントがそうした迷信を信じるのはあり)

これでブロックにはすべて一意のインデックスが振られることになり、隣接するブロックへのインデックスも類推することができるようになります。
例えば、座標 x, y, z が 0, 0, 0 のブロックにとって、0, 0, 1 のブロックは1m上のブロックであると類推でき、同様に直下のブロックは 0, 0, -1 と類推できます。
自身のブロックの直下が風ブロックだったら、液体オブジェクトを与えられる水位だけ水ブロックが落下させるとしたとき、メッセージとしてそうした現象が成り立つようなことが伝播します。
上下は簡単ですが、周囲のブロックはどのようにインデックスが打たれるべきでしょうか?平面充足が三角形であるため、この点は簡易に決めることができません。
一辺を1unitとする正三角形なら、横方向はブロックの重心を結ぶ長さも1unitになります、縦方向は{ \sqrt{3} }unitになりますね。
これでセーブデータなどの場所の座標から、特定のブロックへのアクセスが可能ということになります。

重要な認識としては、ブロックの位置は不変で、面接続しているブロックも切り替わることはありません。
移動するのはあくまでブロックを構成しているオブジェクトであって、それらが様々なふるまいによって気化して周囲に伝播したり、支えを失って落下したり、融解して周囲に伝播したりします。
ブロックには具体的なインデックスが振られていなくても、ブロック同士で互いに面接続しているブロックへのアドレスを保持していれば、アクセスに支障はありません。

ブロックには面接続している、合計五つのブロックへのアドレスを保持する必要があることがわかってきました。
ここまでのことを具体化すると、次のクラスが記述されました。

public class Block
{
    int _position_east;
    int _position_north;
    int _position_depth;

    public Block _northEastBlock;
    public Block _southBlock;
    public Block _northWestBlock;
    public Block _upBlock;
    public Block _downBlock;
}

public class SoilBlock : Block
{

}

public class WaterBlock : Block
{

}

public class AirBlock : Block
{

}

public class FireBlock : Block
{

}

これをワールド depth 20 x east 20 x north 20 blocks の 8,000 blocks の世界に配置するように
まずは、ハードディスクに初期化した世界情報を記述し、これを読み込んでメモリ上に再構成するサンプルを作り
そのメモリ上のワールド情報を Unity でビジュアライズする部分を書いてみましょう。

うーん、その後時間をおいて考えてみたのですが
すべてのブロックを大気ブロックとして、水位、密度、割合、圧力、熱という表現にオブジェクトがその割合だけ存在するというのはどうでしょうか?
つまり、空気さえもオブジェクトとなり、ブロックを何パーセント占めているかという表現になります。
例えば、海水ブロックに大気が接していたら、徐々に大気に水蒸気が伝播していき、その分海水が入っていたブロックの水位が蒸発した分だけ下がり続け、上昇した塩分濃度がほかの接している海水ブロックに伝播して同じ塩分濃度になるといった具合です。
そうすると、常にブロックは位置も変わらず、種類も変わらず、含んでいるオブジェクトの伝播の振る舞いに従って自身や周囲のブロックの構成成分を変容させていくだけとなります。

しかし、Minecraft と Unity で作ってみたっていう動画を探してみたのですが、例がわんさか出てきますね。
オブジェクト数が数千、数万と出ていそうなビューなのにフレームレートがあまり落ちないような動画とかもあるし、いったいどういうマシンで遊んでいるんだろう?
それと、みんなどれだけ Unity で Minecraft 作りたいんだって感じです。
AIを作りたいかは別として、同じようなことを考える人はいっぱいいるんですね。

また、記事が長くなってきたので、次の AI カテゴリーの記事へ続きを書きます。