はじめに
ゲーム開発をしていると必ずぶつかるのが「データをどの形式で保存するか」という問題です。
特にセーブデータやマスターデータなど、ゲーム内で扱うデータの保存形式は、パフォーマンスや開発効率、さらにはチート対策にまで影響してきます。
代表的な選択肢は バイナリ形式 と JSON形式 の2つです。
今回はこの2つについて、それぞれの特徴とメリット・デメリットを簡単にまとめてみます。
バイナリとは?
バイナリ形式とは、データを人間が読めるテキストではなく、コンピュータが直接扱える0と1の羅列(バイト列)として保存する形式です。
セーブデータであれば、int型やfloat型の値をそのままバイト列として書き込み、読み込み時もその配置通りに解釈して復元します。
メリット
-
データ容量が少ない
テキストのようなキー名や記号を含まないため、同じ情報量でもファイルサイズを小さくできます。 -
高速な読み込み・書き込み
パースの必要がなく、メモリへの配置がほぼそのまま対応するため処理が高速です。 -
中身が読みづらい
通常のテキストエディタで開いても意味のある文字列としては表示されないため、簡易的な難読化になります。さらに暗号化を組み合わせれば、セーブデータの改ざんや解析のハードルを上げることができます。
デメリット
-
可読性が低い
中身を直接確認するにはバイナリビューアや専用ツールが必要で、デバッグがしづらいです。 -
柔軟性が低い
データ構造(フィールドの追加・削除・型変更)を変えると、読み込み側のロジックも合わせて修正しないと正しく復元できなくなります。バージョン互換を保つ設計が必要になります。 -
実装コストが高い
自前でバイナリの読み書き処理を実装する必要があり、手作業だとミスが起きやすいです。
C#での実装例
C#ではBinaryWriter/BinaryReaderを使うことで、比較的簡単にバイナリの読み書きができます。
using System.IO;
public class SaveData
{
public int Level;
public float Hp;
public string PlayerName;
}
public static class SaveDataBinarySerializer
{
// 書き込み
public static void Save(string path, SaveData data)
{
using var stream = new FileStream(path, FileMode.Create);
using var writer = new BinaryWriter(stream);
writer.Write(data.Level);
writer.Write(data.Hp);
writer.Write(data.PlayerName);
}
// 読み込み
public static SaveData Load(string path)
{
using var stream = new FileStream(path, FileMode.Open);
using var reader = new BinaryReader(stream);
var data = new SaveData();
data.Level = reader.ReadInt32();
data.Hp = reader.ReadSingle();
data.PlayerName = reader.ReadString();
return data;
}
}
書き込みと読み込みの順番と型を完全に一致させる必要がある点がポイントです。Levelをintからlongに変えたり、フィールドの順番を入れ替えたりすると、既存のセーブデータが正しく読み込めなくなります。フィールドが増えるたびにSave/Loadの両方を手動で直す必要があり、これが前述の「実装コストが高い」「柔軟性が低い」というデメリットに直結します。
JSONとは?
JSONはテキストベースのデータ形式で、キーと値のペアで構造化されたデータを表現します。人間が見てもそのまま理解できるのが最大の特徴です。
メリット
-
可読性が高い
テキストエディタで開くだけで中身を確認・編集できるため、デバッグや調整がしやすいです。 -
柔軟性が高い
フィールドの追加や削除があっても、多くの場合パース処理側の対応が緩やかで済み、構造の変化に強いです。 -
標準ライブラリが充実
多くの言語・エンジンで標準的にサポートされており、シリアライズ/デシリアライズの実装コストが低いです。
デメリット
-
データ容量が大きい
キー名や記号({},",,など)を含むため、バイナリに比べてファイルサイズが大きくなりがちです。 -
読み込み速度が遅い
テキストのパース処理が必要なため、バイナリと比べると処理コストがかかります。 -
中身が誰でも見える
そのままではセーブデータの改ざんが容易なので、チート対策としては別途暗号化や署名などの対策が必要になります。
C#での実装例
C#では標準のSystem.Text.Json(またはJsonUtility、Newtonsoft.Jsonなど)を使うことで、クラスをそのままシリアライズ・デシリアライズできます。
using System.IO;
using System.Text.Json;
public class SaveData
{
public int Level { get; set; }
public float Hp { get; set; }
public string PlayerName { get; set; }
}
public static class SaveDataJsonSerializer
{
// 書き込み
public static void Save(string path, SaveData data)
{
string json = JsonSerializer.Serialize(data, new JsonSerializerOptions
{
WriteIndented = true
});
File.WriteAllText(path, json);
}
// 読み込み
public static SaveData Load(string path)
{
string json = File.ReadAllText(path);
return JsonSerializer.Deserialize<SaveData>(json);
}
}
出力されるファイルは以下のようにそのまま人間が読める形になります。
{
"Level": 5,
"Hp": 87.5,
"PlayerName": "Chigadio"
}
バイナリ版と違い、フィールドの順番を気にする必要がなく、新しいフィールドを追加してもキーが存在しない場合はデフォルト値で補完されるなど、構造変化に強いのが分かります。一方で、このファイルはメモ帳などでそのまま開いてLevelやHpを書き換えられてしまうため、改ざん耐性は低くなります。
どちらを使うべきか?
基本的には バイナリ形式がおすすめ です。データ容量や読み込み速度、簡易的な難読化の面で優位性があり、特に商用タイトルやスマホゲームなど、パフォーマンスや不正対策が重要になる場面では効果を発揮します。
一方で、個人開発やオフライン専用のゲームであればJSONでも十分 です。デバッグのしやすさや実装の手軽さは、開発スピードを重視したい個人開発において大きなメリットになります。要件や開発規模に応じて使い分けるのが良いでしょう。
自作ツールの紹介
最後に、自分が開発しているUnity向けの支援ライブラリ「SupportChigadio」を紹介します。
このツールはデータ形式としてバイナリを採用していますが、バイナリの読み書き処理を自分で手動実装する必要はありません。データ定義さえ作れば、先ほどのようなBinaryWriter/BinaryReaderを使った読み書きコードをC#側で自動生成してくれます。
興味がある方はぜひ覗いてみてください。