前書き
お久しぶりです
仕事忙しかったり、ゲームの仕組みの一部を作り直したりでなかなか更新できませんでした
あんまりネタがないので今回はTechな話をしたいと思います
あと、最後にちょっとだけ今のゲームの進捗貼ります
セーブデータの話
結論
SQLite使うようにした
今までと変えるに至った経緯
もともとはセーブデータは暗号化してMsgPackを用いて保存してました
採用理由はデシリアライズ速度でした
しかし、作っていった結果。。。。
細かいデータを保存したりするのに毎回デシリアライズ用のコード書くのめんどい!
ってなりました
まぁ、MsgPackのコンテナ系のデシリアライズの対応がいまいち悪いのも原因の一つです
作りずらいことこの上ないという状態でした
シームレスに大量のデータをリアルタイムで保存したいなー
↓
DBみたいな仕組みあればいいなー
↓
DBでいいじゃん
↓
SQLiteがあるじゃん
といった感じで採用に至りました
実際の作り
大まかな部分はこちらの記事を参考にしています
UnityにSQLite-netを導入する
↑に加えてある程度のラッパーと自動生成する機構を作ったりしました
<Entity>
<Name>TestUser</Name>
<Param>
<Name>user_id</Name>
<Type>uint</Type>
<Primary>true</Primary>
<Comment>ユーザーID</Comment>
</Param>
<Param>
<Name>name</Name>
<Type>string</Type>
<Comment>name</Comment>
<DefaultValue>Test</DefaultValue>
</Param>
</Entity>
こんなXMLを書くと
///Don't write
///This is Generated Code
using System;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using SQLite;
using UniRx;
[Table("TestUser")]
public class TestUserEntity
{
/// <summary>
/// ユーザーID
/// <summary>
[PrimaryKey]
[Column("user_id")]
public uint user_id { get; set; }
/// <summary>
/// name
/// <summary>
[Column("name")]
public string name { get; set; }
}
public class TestUserModel
{
private static bool Initialized = false;
private static List<TestUserModel> items = null;
public static IList<TestUserModel> Items
{
get
{
if(items == null)
{
GetAll();
}
return items;
}
}
private TestUserEntity entity = new TestUserEntity();
private CompositeDisposable disposable = new CompositeDisposable();
public uint UserId => entity.user_id;
private ReactiveProperty<string> name = new ReactiveProperty<string>();
public IReadOnlyReactiveProperty<string> Name => name;
public static async UniTask<TestUserModel> Get(uint user_id)
{
if (!Initialized)
{
await SaveDataCore.CreateTable<TestUserEntity>();
Initialized = true;
}
if(items == null)
{
await GetAll();
}
if(items.Exists(i => i.UserId == user_id))
{
return items.First(i => i.UserId == user_id);
}
var entity = new TestUserEntity();
entity.user_id = user_id;
entity.name = Test;
await SaveDataCore.InsertData(entity);
var model = new TestUserModel(entity);
items.Add(model);
return model;
}
private static async UniTask GetAll()
{
if (!Initialized)
{
await SaveDataCore.CreateTable<TestUserEntity>();
Initialized = true;
}
if (items == null)
{
var datas = await SaveDataCore.GetAllData<TestUserEntity>();
var models = new List<TestUserModel>();
foreach (var data in datas)
{
models.Add(new TestUserModel(data));
}
items = models;
}
}
private TestUserModel(TestUserEntity _entity)
{
entity = _entity;
name.Value = entity.name;
name.Do(v => entity.name = v).Subscribe(_ => UpdateData()).AddTo(disposable);
}
private async UniTask UpdateData()
{
await SaveDataCore.UpdateData(entity);
}
public void UpdateName(string _name)
{
name.Value = _name;
}
~TestUserModel()
{
disposable.Dispose();
}
}
こんなコードができます
どうやって作っているのといわれると、ある程度のテンプレートを作成してreplaceとstreingBuilderで頑張って作ってるって感じです
かなりゴリラな作り方なのである程度できる方ならわかっちゃうと思います
こんな感じセーブデータを保存するクラスをバンバン量産できるようにしてます
フラグ管理などもDBなのでいっぱいデータ作れる安心感が良いです
作ってるゲームの話
今作っている画面はこんな感じです
街中でのショップ機能とか作ってます
Gif
スクリーンショット
本来であればShop機能などが右側に乗るはずなので、こちらも少々お待ちを。。。。
小言
最初はランダム生成でキャラのバリエーション増やすつもりだったのですが、プチプチと作っていくうちに、ある程度ユニークなキャラがいたほうがいいなと思い至り↑のShop店員さんたちを作成することになりました
こうやっていろいろ作っちゃうと、ランダム生成キャラは汎用的なエロで済ませるつもりだったのが、ユニークなキャラには専用の何かを設けたくなってしまいますね。。。。