コンテキスト
PlayFab から世界データ拾ってきたいなぁ
でもログインまだだった 失敗
なんてことが絶対に起こらないような
PlayFab から世界データ取得を成功させる仕組みを考えたい
もう作ってた!
simplestar-tech.hatenablog.com
しばらく Unity から離れてたから自分で記事にしていることすら忘れていた
このアイディアをファイルに対しても使ってみます。
ファイルのダウンロードについてはこちらの Qiita の記事が参考になりました。(これも自分が書いてる)
qiita.com
実装詳細
using PlayFab; using PlayFab.ClientModels; using System; using System.Collections; using UnityEngine; using UnityEngine.Networking; public class PlayFabContentFile : MonoBehaviour { /// <summary> /// Content Delivery Network からファイルコンテンツを取得 /// </summary> /// <param name="key">PlayFabのTitleのRoot/以下のファイルオブジェクトキー</param> /// <param name="onComplete">完了時アクションでnullが返ってきたら失敗を意味する</param> public void GetContentFileData(string key, Action<byte[]> onComplete) { GetDownloadUrl(key, presignedUrl => { GetFile(key, presignedUrl, onComplete); }); } void GetDownloadUrl(string key, Action<string> onComplete) { PlayFabLogin.AfterLoginCall(() => { PlayFabClientAPI.GetContentDownloadUrl(new GetContentDownloadUrlRequest() { Key = key, ThruCDN = true }, result => onComplete?.Invoke(result.URL), error => Debug.LogError(error.GenerateErrorReport())); }); } void GetFile(string key, string preauthorizedUrl, Action<byte[]> onComplete) { StartCoroutine(this.GetData(key, preauthorizedUrl, onComplete)); } IEnumerator GetData(string key, string preauthorizedUrl, Action<byte[]> onComplete) { UnityWebRequest www = UnityWebRequest.Get(preauthorizedUrl); yield return www.SendWebRequest(); if (www.isNetworkError || www.isHttpError) { Debug.LogError(www.error); onComplete?.Invoke(null); } else { // 結果をバイナリデータとして取得する onComplete?.Invoke(www.downloadHandler.data); } } }
テストコード
// オンラインから現在のプレイヤーチャンクが所属する世界データを読み込む // this.playerChunkCenter→"000" var worldIndex = "000"; var objectKey = $"world/cubedata{worldIndex}.gz"; playFabContentFile.GetContentFileData(objectKey, cubedata => { if (null != cubedata) { Debug.Log($"download length = {cubedata.Length}"); cubedata = GZipCompressor.Decompress(cubedata); Debug.Log($"unzip length = {cubedata.Length}"); } else { Debug.LogError($"キー{objectKey}でダウンロードできなかったんだけど…"); } });
実行結果
一度も失敗を経験することなく、期待通りの動作を確認できました。
データの解凍についてはこちらの記事を参照
baba-s.hatenablog.com
自分はこんな実装が欲しかったのでちょっと変更
using ICSharpCode.SharpZipLib.GZip; using System.IO; // 使い方 //var compressedData = GZipCompressor.Compress(rawData); //var rawData = GZipCompressor.Decompress(compressedData); /// <summary> /// gzip で byte[] の圧縮や解凍を行うクラス /// </summary> public static class GZipCompressor { public static byte[] Compress(byte[] rawData) { using (var memoryStream = new MemoryStream()) { Compress(memoryStream, rawData); return memoryStream.ToArray(); } } public static byte[] Unzip(byte[] compressedData) { using (var memoryStream = new MemoryStream()) { Decompress(memoryStream, compressedData); return memoryStream.ToArray(); } } private static void Compress(Stream stream, byte[] rawData) { using (var gzipOutputStream = new GZipOutputStream(stream)) { gzipOutputStream.Write(rawData, 0, rawData.Length); } } private static void Decompress(Stream stream, byte[] compressedData) { var buffer = new byte[4096]; using (var memoryStream = new MemoryStream(compressedData)) using (var gzipOutputStream = new GZipInputStream(memoryStream)) { for (int r = -1; r != 0; r = gzipOutputStream.Read(buffer, 0, buffer.Length)) { if (r > 0) { stream.Write(buffer, 0, r); } } } } }
ゲームではこのデータの解凍処理が終わったところで、それぞれの世界データを確保してデータを書き込み
これを使って世界メッシュの生成が進むとよいだろうと思う