Advertisement
hevohevo

マイクラ(&Forge#1180)の個人的な解析メモ

Aug 14th, 2014
352
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.56 KB | None | 0 0
  1. # マイクラがWorld内のブロックのデータをどのように取り扱っているかのメモ
  2. ## ブロックは、1種類につきゲーム内で1つしか存在しない
  3.  
  4. - 独自追加したものも含めて、Block(を継承したクラス)はゲーム開始時に1種類1個ずつインスタンスが作成される。
  5. - マイクラ世界の個々のブロックはBlockクラスのインスタンスではなく、マッピングデータ。(そりゃそうだ、万単位のブロックが個別のインスタンスだとPCがチヌ)
  6. - (イメージ) 座標(1,1,1)と(1,1,2) にはBlockID3のブロック(1つのインスタンスを指す)が設置されているよ。
  7. - ```world.getBlock(x,y,z)```、 ワールドの座標をキーにBlockインスタンスを取り出す。取り出したオブジェクトは、同じ種類のブロックなら常に同一のもの。
  8. - だから、独自ブロックを追加したときに、クラス変数どころか、インスタンス変数も全ブロックで共有されることになる。(要調査:違うワールドではどうなる?)
  9.  
  10. ##上記の内容を検証したサンプルコード
  11.  
  12. - (基本知識)独自ブロックを追加するためには、Modの初期化メソッド中で最低限、以下を実行する
  13. 1. Blockクラスを継承(extends)した新しいクラスを作成。
  14. 2. new して、そのインスタンスオブジェクトを、一意な名前とともにGameRegistryに登録
  15. - 元とするMaterial(例:rock)を指定することでその性質をそのまま引き継いだブロックができる
  16. - 独自機能をつけたいなら@Overrride
  17. - 以下のコードでは、説明簡略化のためにテクスチャ関係は省略
  18.  
  19. ```
  20. // このコードだけでは動きません
  21. // 初期化作業として以下が最低でも必要
  22. // Block blockSample = new BlockSample(); //インスタンス生成 
  23. // GameRegistry.registerBlock(blockSample, "block_sample"); //それをゲームに登録
  24.  
  25. // import は省略
  26.  
  27. public class BlockSample extends Block {
  28. public static Integer varC=0; // クラス変数
  29. public Integer varI=0; // インスタンス変数
  30.  
  31. public BlockSample() {
  32. super(Material.rock); // 基本的な性質は rockのものを使う
  33. this.setCreativeTab(CreativeTabs.tabBlock); //Creativeモードでブロックを取り出すとき、ブロックタブから取り出せるように
  34. }
  35.  
  36. /**
  37. * Blockクラスにもとからあるメソッドを上書き @Override
  38. * このメソッドはブロックを右クリックすると呼び出される
  39. * 本プログラムは右クリックでカウンタの値が増える。ポイントは、ワールド中に設置した全てのブロックでカウンタ(2種類)が共有されていること。
  40. * なぜならば、どれも同じ BlockSampleインスタンスを参照しているからである。
  41. * ついでに、サーバー/クライアントでそれぞれ実行されるので、1クリックで2回呼び出されていること、
  42. * そしてさらに、サーバー/クライアント間でも共通のインスタンスを参照していることがわかる。(←シングルだからです。マルチなら違うはず)
  43. */
  44. @Override
  45. public boolean onBlockActivated(World world, int blockX, int blockY, int blockZ, EntityPlayer player, int hitSide, float par7, float par8, float par9) {
  46. BlockSample.varC++;
  47. this.varI++;
  48. System.out.println("BlockSample.onBlockActivated"+world);
  49. System.out.println(this.toString()); // 参照しているオブジェクトの情報表示
  50. System.out.println("Class var: "+BlockSample.varG); // こちらが共通なのは当然といえば当然の結果
  51. System.out.println("Instance var: "+this.varI); // 問題はこちら
  52. return super.onBlockActivated(world, blockX, blockY, blockZ, player, hitSide, par7, par8, par9);
  53. }
  54. }
  55. ```
  56.  
  57. ## (補足) サーバー/クライアントごとのコードの書き分けについて
  58.  
  59. - 基本的な考え方は以下を参照。
  60. - http://minecraftjp.info/modding/index.php/%E6%96%B0%E3%81%97%E3%81%84%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0%E8%A8%98%E6%B3%95%E3%81%A8Universal_Modding
  61. - なお、@SideOnly(Sides.CLIENT)アノテーションは、シングル環境ではうまく働かない模様。
  62. - ```if (!world.isRemote){}```による書き分けが必要?
  63. - 情報源。 http://forum.minecraftuser.jp/viewtopic.php?f=39&t=18801
  64. - サーバークライアント間での同期、通信方法については要調査。
  65.  
  66. # ブロックごとに個別の情報を格納したいときにどうするか
  67. ## 小さなデータはMetaDataで
  68.  
  69. - 小さなデータならMetaData(メタ値)、複雑なデータはTileEntityを使う。
  70. - 小さなデータなら、メタ値として情報を格納できる。たぶん容量は4bit(10進数で0-15)。
  71. - ```world.getBlockMetadata(x,y,z)``` 、座標をキーにメタ値取り出し。「ブロックをキー」にではないところに注意。
  72. - メタ値によって表示するテクスチャを変えることで、違うブロックのように見せかけることが可能。羊毛とかね。あとは原木の向きとか
  73. - ブロックが返すテクスチャ切り替えるには、Blockを継承したクラスで、以下のメソッドを@Override すればいいのかな?(要調査)
  74. - ```public IIcon getIcon(){};```
  75.  
  76. ## より大きなデータはTileEntityで
  77.  
  78. TileEntity はゲーム内でどのように取り扱われているか
  79.  
  80. - たとえば個別にインベントリを持つブロックなどは、各ブロックごとに大量のデータを保持しなくてはならない。
  81. - あるBlockがTileEntityを持つとあらかじめ宣言(手順は後述)しておくことで、ワールドにブロックを設置するごとに、新しいTileEntityインスタンス生成&保存
  82. - イメージとしては、個別ブロックとTileEntityが直接結び付けられているようだが、実装としては、World座標ベースで保存&管理されている。以下は根拠。
  83. - ```world.getTileEntity(x,y,z)``` : 座標をキーに個別のEntityを取り出し。「ブロックをキー」にではないところに注意。
  84.  
  85. # TileEntityをブロックと結びつける方法
  86. ## 最低限の手順
  87.  
  88. - TileEntityの用意
  89. 1. TileEntityクラスを継承した独自のTileEntityクラスを作っておく
  90. - ```public class TileEntitySample extends TileEntity{}```
  91. - この中で独自機能や独自変数を定義しておく
  92. 2. GameRegistoryに一意な名前と一緒に追加して、これはTileEntityなんだよということをシステムに知らせる
  93. - ```GameRegistry.registerTileEntity(TileEntitySample.class, "TileEntitySample");```
  94. - ブロックの追加とは異なり```TileEntitySample.class```というクラスそのものを追加していることに注意。
  95. - Block側の用意
  96. 1. Blockクラスではなくそれを拡張したBlockContainerクラスを使って、新しいBlockを定義
  97. 2. BlockContainerクラスの中で、createnewTileEntityメソッドを@Overrideする。その中で結び付けたいTileEntityをnewして返すようにする。
  98. - ```public TileEntity createNewTileEntity(World world, int param) { return new TileEntitySample() }```
  99.  
  100. これらの手順を踏むことで、システムは、ワールドにブロックが設置されるたびにそのBlockのcreateNewTileEntity()メソッドを呼び出し、
  101. 新しいTileEntityオブジェクトを得て、それをワールドデータに(位置座標をキーに)保存する。
  102.  
  103. - (要検討事項)
  104. - createNewTileEntity()の2番目のint paramは何が入ってくるのか謎。
  105. - たぶんこの値によって、呼び出すTileEntityを変えるよう実装できるのかな?
  106.  
  107. ## 以下、知らなくても別にいいけれど、知っておくと得した気分になれる情報
  108. - マイクラソースコード中では、BlockContainerは以下のように定義されている
  109. - ```public abstract class BlockContainer extends Block implements ITileEntityProvider{}```
  110. - ITileEntityProvider インタフェースは、```TileEntity createNewTileEntity(World world, int param)```だけが定義されている
  111. - つまり、TileEntityを取り扱うブロックは、原則としてBlockContainerクラスを使わなくてはならない(もちろんこのProviderインタフェースを独自実装したなら話は別)。
  112.  
  113. # 今回のまとめ
  114.  
  115. - ブロックの実体そのものは、ゲーム内で1種類あたり1個しか存在しない
  116. - ゲーム内で特定の座標に設置されたブロックが何であるかという情報は、Worldごとに、座標をキーとしたマッピングデータで保存されている
  117. - ```world.getBlock(x,y,z)``` でそのブロックの実体を参照できる。
  118. - ブロックの実体は1種類あたり1個しか存在しないので、座標が違っても種類が同じなら、結局指すものは同じもの。
  119. - そのため、ワールドに設置したブロック1つずつに独自情報を保存するためには別の手段が必要
  120.  
  121.  
  122. - 手段1: MetaData(メタ値)によって小さなデータ保存(4bitかな?10進数で言う0-15)
  123. - 原木の向きや羊毛の色などはこの値によって管理されている
  124. - 独自データは、ブロックではなく、座標に結び付けられて保存されていることに注意。
  125. - ```world.getBlockMetaData(x,y,z)``` でその「座標」のメタデータを入手できる。
  126. - もし、ブロックマッピングデータとMetaDataマッピングデータに齟齬がでたら・・・ガクブル
  127. - 手段2: EntityDataによって様々なデータを保存
  128. - 内部インベントリを持つブロックなど、大量のデータを保存するときはこちらの手段を使う。
  129. - ``world.getTileEntity(x,y,z)``` 「座標」をキーに個別のEntityオブジェクトを入手できる。
  130. - もし、ブロックマッピングデータと(略
  131.  
  132. # 感想(というか妄想みたいなもの)
  133.  
  134. きっと、最初期のマイクラはデータ保存の方法としてBlockマッピングデータとMetaDataマッピングデータしか用意していなかったんじゃないかな?
  135.  
  136. そしてバージョンアップするうちに、これじゃ領域が足りん!ということで、後付でTileEntityという保存手段を実装したんじゃないかな?
  137.  
  138. # 次に何を調べるかまとめるか
  139.  
  140. - マイクラ世界に存在するブロックとは別のオブジェクトであるEntity。それがどのようにデータ管理されているのか、そしてどうやってEntityを追加するのか。
  141. - マルチプレイヤー環境に対応できるMOD作成のために、
  142. - サーバー・クライアントでどのように処理を分けるのか。ロジック面と実装面の両方について。
  143. - もちろん具体的な実装方法も。サーバークライアント間の同期をどのように行うのかについても調べる。
  144. - 直方体のブロックにテクスチャを貼るだけではなく、独自レンダリングをどうやって行うのか。噂に聞くと、内容理解は相当ハードらしい。
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement