見出し画像

UnityでSQLiteが使いたいだけなのに②

UnityでSQLiteが使いたいだけなのに
という記事をかきました。

Unityに対してSQLite4Unity3dという仕掛けでSQLiteに接続します。

が、私はSQLサーバーはSQLを流せばよいと思っている旧世代の人間でした。

SQLを直接流すのは、SQLインジェクションという攻撃に弱いのでやめましょう。ということで、世界はいつからかORM(Object-Relational Mapping)という考え方でデータベースの管理をするようになったようです。

SQLite4Unity3dは、SOLite-netを拡張するかたちでUnityに対応させているようです。

SQLite4Unity3dの作者は、SQLite-netのドキュメントを参照してほしいと、記載しておりました。

インターネットで、ORMについて調べますがなかなか要領を得ないですし、わたしにはSQLite-netのGitページのどこを読むべきかわかりませんでした。
そんな中、ChatGPTが使い方を教えてくれました。

ORMはマッピングします。データベースに保存するためのオブジェクトは、Unityのうえでもオブジェクトの定義をしましょう。オブジェクトを操作するように、データベースを操作できます。直感的にデータ制御の文が書けるLINQという方式にも対応していますので、そういう書き方も検討すると良いでしょう。

ふうむ、LINQは必要になったら別途考えます。参考です。

ORM(SQLite-net)を使用したセレクト文:

// SQLite4Unity3dを使用してSQLiteデータベースに接続
SQLiteConnection connection = new SQLiteConnection("your_database_path");

// SQLite-netを使用してクエリを実行
List<Player> highScorePlayers = connection.Table<Player>().Where(p => p.Score > 100).ToList();

LINQを使用したセレクト文:

// SQLite4Unity3dを使用してSQLiteデータベースに接続
SQLiteConnection connection = new SQLiteConnection("your_database_path");

// LINQを使用してクエリを実行
var highScorePlayers = from player in connection.Table<Player>()
                       where player.Score > 100
                       select player;
List<Player> highScorePlayersList = highScorePlayers.ToList();

この例では、LINQを使用して同様のクエリを実行しています。Table<T> メソッドを使用してデータベースのテーブルにアクセスし、LINQクエリで条件に合致するデータを取得しています。

どちらも同じ結果を返しますが、ORMの場合はSQLite-netが提供するメソッドやLINQ拡張メソッドを使用しています。LINQの方は、C#言語の標準的なLINQ構文を使用しています。どちらも好みやプロジェクトの方針に合わせて選択できます。

ChatGPTさま

ありがとうChatGPT.
この調子で、ボタン押下でユーザーのポジションを取得するコードを出してくれ。

と、お願いすると、
・データベースマネージャのクラスを作って遅延初期化と接続の一元管理をします。
・データ用のクラスをつくります
・データ用のマネージャクラスをつくります
・実際にボタンなどから実行するかたちをつくります

4段階の準備が必要なようでした。ので、以下で保存します。

データベースマネージャは以下。オブジェクトにアタッチなどしなくていいのだなというのが、私には新鮮でした…

using SQLite4Unity3d;

/// <summary>
/// SQLコネクションは、アプリケーションの中ではじめて呼び出された時に行う。
/// 遅延初期化である。
/// アプリケーションが終了する際にCloseする
/// 抽象化され、インスタンスのない状態で定義されている。
/// アプリケーションの中でひとつだけあればよい、シングルトンパターンである。
/// </summary>


public class DatabaseManager
{
    private static DatabaseManager instance;
    private string databasePath;
    private SQLiteConnection connection;

    private DatabaseManager(string dbPath)
    {
        databasePath = dbPath;
        Connect();
    }

    public static DatabaseManager Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new DatabaseManager("Assets/StreamingAssets/data.db");
            }
            return instance;
        }
    }

    private void Connect()
    {
        connection = new SQLiteConnection(databasePath);
        connection.CreateTable<PlayerPosition>(); // テーブルの作成
    }

    public void Close()
    {
        if (connection != null)
        {
            connection.Close();
            connection = null;
        }
    }

    public SQLiteConnection GetConnection()
    {
        if (connection == null)
        {
            Connect();
        }
        return connection;
    }
}

以下は、データの定義

public class PlayerPosition//プレイヤーポジションクラス。通常ひとつのモデルクラスにひとつのファイル
{
    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }
    public float X { get; set; }
    public float Y { get; set; }
    public float Z { get; set; }
    /*
    public override string ToString()
    {
        return string.Format("[Position: x={1}, y={2},  z={3}]", x, y, z);
    }
    */
    public override string ToString()
    {
        return $"[PlayerPosition: Id={Id}, X={X}, Y={Y}, Z={Z}]";
    }
}

さらに、プレイヤーポジション管理用のクラス

///<summary>
///ここからは、DBへの問い合わせを別途記載している
/// </summary>
/// 

// ボタンが押された時の
public class PlayerPositionManager//プレイヤー位置管理クラス
{
    private DatabaseManager dbManager;

    public PlayerPositionManager()
    {
        // DatabaseManagerのインスタンスを取得
        dbManager = DatabaseManager.Instance;
    }

    public PlayerPosition GetPlayerPosition()// 現在のプレイヤーポジションを取得するメソッド
    {
        // データベースへの接続取得
        SQLiteConnection connection = dbManager.GetConnection();

        // プレイヤーポジションの取得
        PlayerPosition currentPlayerPosition = connection.Table<PlayerPosition>().FirstOrDefault();

        return currentPlayerPosition;
    }

    public void SavePlayerPosition(float newX, float newY, float newZ)//保存メソッド
    {
        // データベースへの接続取得
        SQLiteConnection connection = dbManager.GetConnection();

        // 現在のプレイヤーポジションの取得
        PlayerPosition currentPlayerPosition = connection.Table<PlayerPosition>().FirstOrDefault();

        // 新しいポジションの設定
        if (currentPlayerPosition == null)
        {
            // データベースにポジションがまだ存在しない場合、新しいPlayerPositionを作成
            currentPlayerPosition = new PlayerPosition();
        }

        currentPlayerPosition.X = newX;
        currentPlayerPosition.Y = newY;
        currentPlayerPosition.Z = newZ;

        // プレイヤーポジションの保存
        if (currentPlayerPosition.Id == 0)
        {
            // IDが0の場合は新規に挿入
            connection.Insert(currentPlayerPosition);
        }
        else
        {
            // 既存のポジションが存在する場合は更新
            connection.Update(currentPlayerPosition);
        }

        // クエリの確定
        connection.Commit();
    }
}

そして、実際に運用するのは以下

using UnityEngine;
using UnityEngine.UI;

public class SaveButtonHandler : MonoBehaviour
{
    // PlayerPositionManagerのインスタンス
    private PlayerPositionManager playerPositionManager;

    // Unityのボタンを関連付ける(Inspectorから設定)
    public Button saveButton;

    private void Start()
    {
        // PlayerPositionManagerのインスタンスを生成
        playerPositionManager = new PlayerPositionManager();

        // ボタンのクリックイベントにメソッドを追加
        saveButton.onClick.AddListener(OnSaveButtonClicked);
    }

    private void OnSaveButtonClicked()
    {
        // ボタンがクリックされた時の処理

        // 新しい座標を仮に設定
        float newX = 1.0f;
        float newY = 2.0f;
        float newZ = 3.0f;

        // プレイヤーポジションの保存
        playerPositionManager.SavePlayerPosition(newX, newY, newZ);
    }
}

これらを設定すれば、Unity上で、プレイヤーポジションのセーブとロードができるようになる。

データベースの考え方を根本から覆されるような学習体験でした。

データオブジェクトがあれば、その管理を行うManagerクラスを作る。
アプリケーションの要求にしたがって、Managerの考え方は違う。
必要なメソッドだけをあらかじめ書いているのがオブジェクト指向ということなのでしょう。

ボタンひとつひとつにSQLを書きたがるのは、オブジェクト指向的な作り方ではないのですね。

ドキュメントが少ないなあ…なんでかな?と思っていましたが、ドキュメントが少なくて当然なのでしょう。アプリケーションの要件ごとに要求することが違うのですから。


ここまで、お読みいただきありがとうございました。

この記事が気に入ったらサポートをしてみませんか?