はじめに
PlayFab ログイン成功後、CDNから世界圧縮データを取得し
Unity で展開してから、それぞれのチャンク配列を確保し、データを詰めていく
詰め終わったら、ゲームシーンを開始する
世界圧縮データを最初は作らなければならない
世界データは16,777,216のint配列
ゲーム内のチャンクインデックスの振り方をおさらい
チャンクにはキーがあり、3次元であらわされたものを一次元に直すと次の通り
/// <summary> /// チャンクキーから世界で一意のチャンクインデックスを取得 /// </summary> internal int ChunkKeyToChunkIndex(Vector3Int chunkKeyXYZ) { var byteMax = (byte.MaxValue + 1); return chunkKeyXYZ.x * byteMax * byteMax + chunkKeyXYZ.z * byteMax + chunkKeyXYZ.y; }
つまり、y方向にインクリメントで、z方向に 256 単位オフセット、x方向は 256x256 オフセットが足されて特定されます。
同じようなやり方なら y 方向にインクリメントで、z方向に 16 単位オフセット、x方向は 16x16オフセットが足される形でチャンクのインデックスが得られるとする
一つのチャンクが 16 x 16 x 16 の 4096 キューブ情報を持つので、常にチャンクインデックス x 4096 がチャンクのデータポインタとなります。
ゲーム内のキューブインデックスの振り方をおさらい
/// <summary> /// キューブの3次元位置表現から一次元の配列インデックスを取得 /// </summary> int CubeInt3ToCubeIndex(Vector3Int cubeInt3) { return cubeInt3.x * ChunkConst.ChunkSizeZ * ChunkConst.ChunkSizeY + cubeInt3.z * ChunkConst.ChunkSizeY + cubeInt3.y; }
つまり、 y 方向にインクリメントで、z方向に 16 単位オフセット、x方向は 16x16オフセットが足される形でキューブのインデックスが得られる
チャンクの時と同じですね。
これでキューブのチャンク内の3次元位置から 0~4095 のインデックスになる
世界を y 方向に二分するインデックス範囲は?
y軸上半分を 0 とし
y 軸下半分を 1 とする値埋めをしたいとしたら
チャンクインデックスを各軸 0~15でループさせる 3重ループを作ったとして
その時にチャンクインデックスをy 方向にインクリメントで、z方向に 16 単位オフセット、x方向は 16x16オフセットが足される形でチャンクのインデックスを求め
その時にチャンクインデックス x 4096 がチャンクのデータポインタとなり
そこから連続して 4096 個を処理するとして
条件はチャンクインデックスが y < 8 とするときに 1 を入れれば良い
では golang で作ってみます。
golang で水平な世界埋め
まずはおさらい
前回のコードに
simplestar-tech.hatenablog.com
simplestar-tech.hatenablog.com
次の main を書けば下準備がそろう
func main() { // s3 init err := PersistentS3Store.Init(s3bucket, "ap-northeast-1") if err != nil { log.Fatal("s3 Init error.", err) return } // s3 download backup gzdata, err := PersistentS3Store.Get(cubedataObjectKey) if err != nil { log.Fatal("s3 Get error.", err) return } // unzip backup cubedata, err := gUnzipData(gzdata) if err != nil { log.Fatal("gUnzipData error.", err) } // zip data compressedData, err := gZipData(cubedata) if err != nil { log.Fatal("gZipData error.", err) } // local save writeFileData(compressedData, cubedataObjectKey) // s3 upload // err = PersistentS3Store.Set(cubedataObjectKey, compressedData) // if err != nil { // log.Fatal("s3 Set error.", err) // } }
肝心の for 文を次のように書きます。
乱数生成あり
random := rand.New(rand.NewSource(1)) deight := 16 for x := 0; x < deight; x++ { chunkX := x * deight * deight for z := 0; z < deight; z++ { chunkZ := z * deight // random blocks for y := 0; y < 8; y++ { chunkOffset := (chunkX + chunkZ + y) * deight * deight * deight for index := 0; index < deight*deight*deight; index++ { dataIndex := (chunkOffset + index) * 4 cubedata[dataIndex+0] = 1 cubedata[dataIndex+1] = (byte)(random.Intn(24)) side := (byte)(random.Intn(67)) cubedata[dataIndex+2] = side cubedata[dataIndex+3] = side } } // sky for y := 8; y < deight; y++ { chunkOffset := (chunkX + chunkZ + y) * deight * deight * deight for index := 0; index < deight*deight*deight; index++ { dataIndex := (chunkOffset + index) * 4 cubedata[dataIndex+0] = 0 cubedata[dataIndex+1] = 0 cubedata[dataIndex+2] = 0 cubedata[dataIndex+3] = 0 } } } }
出力した gz ファイルのサイズは 16.7 MB
さすがに乱数で作ったファイルはそこまで小さくたためなかった
これを s3 に上げて、そのあとキャッシュサーバーを再起動すれば PlayFab の CDN が 16MB ほどのファイルになるはずです。
やってみましょう。
動作確認
PlayFab のスケジュールタスクはタイムアウトエラーが出ているけど
CDNには 17MB のデータがアップロードされていました。
go には何かそういうの回避するのあるのかな?
go側はタイムアウト設定した方がリークがなくなってよいのだとか
christina04.hatenablog.com
あと
>http.request呼び出しタイムアウトは2.5秒です。
解決方法は今のところないのだとさ まぁタイムアウトが起きても、呼び出されたキャッシュサーバーは仕事を完遂するので今は良しとします。
community.playfab.com