UE中SQLite的使用

using sqlite with unreal

Posted by Tao on Thursday, October 31, 2024

启用SQLite插件 Enable the SQLite plugins

  1. 编辑器界面下 Edit > Plugins 并搜索“sqlite”.

  2. Check “SQLite” and “SQLite Support”. This will add the following content to your .uproject file:

{
    "Name": "SQLiteCore",
    "Enabled": true
},
{
    "Name": "SQLiteSupport",
    "Enabled": true
}

如果是插件开发,则将上述内容添加到.uplugin文件中。

  1. Add the necessary private dependencies to your<Game>.Build.cs file:
PrivateDependencyModuleNames.AddRange(new string[] { "SQLiteCore", "SQLiteSupport" });

这样我们就能添加#include "SQLiteDatabase.h"#include "SQLitePreparedSatatement"了。

将数据库添加到project中(打包用)

  1. 打开项目的Content文件夹并为数据库添加一个新文件夹,可以叫Databases。我们将无法通过虚幻编辑器导入数据库,因此只需打开文件管理器并将其放入该文件夹即可。
  2. 为了确保该文件夹与您的游戏打包在一起,请转到 Edit > Project Settings > Packaging > Advanced > Additional Non-Asset Directories to Copy 。
  3. 添加一个数组元素,然后键入文件夹的名称,例如“Databases”。这可能会向您的DefaultGame.ini文件添加一堆条目,比如这一行:
+DirectoriesToAlwaysStageAsNonUFS=(Path="Databases")

构建数据库操作类

.h构建一个基类,后面操作不同的数据库获取不同的数据类型继承这个类。

#pragma once

#include "CoreMinimal.h"

class FSQLiteDatabase;
class FSQLitePreparedStatement;

/**
 *
 */
class EARTH_API TerrainDatabase
{
public:
    TerrainDatabase();
    virtual TerrainDatabase();

    virtual bool OpenDatabase(const FString& databasePath, bool readOnly);//创建或打开数据库

    virtual bool CreateTable();//创建表

private:
    FSQLiteDatabase* m_terrainDb;
    FSQLitePreparedStatement* m_prepareStat;
};

.cpp 在cpp中包含标头:#include "SQLiteDatabase.h"#include "SQLitePreparedSatatement"。 在构造函数中初始化类:

TerrainDatabase::TerrainDatabase()
{
    m_terrainDb = new FSQLiteDatabase();
    m_prepareStat = new FSQLitePreparedStatement();
}

OpenDatabase函数中调用FSQLiteDatabase类的打开函数进行数据库的打开操作,操作分为只读,读写和打开读写。设计的表分3列。

bOpened = m_terrainDb.Open(*databasePath, ESQLiteDatabaseOpenMode::ReadWriteCreate);
FString createTableSQL = TEXT(
    "CREATE TABLE IF NOT EXISTS TT_TerrainTrans ("
    "ID INTEGER PRIMIAY KEY AUTOINCREMENT, "
    "Name TEXT, "
    "TransMatrix BLOB"
    ");"
);

基类基本完成,这时构建子类FTerrainTransDatabase继承基类。 子类实现插入数据及查询数据,用以实现序列化后数据的入库及读取。 插入数据:

TArray<unit8> blobData;
const FString insertSQL = TEXT("INSERT INTO TT_TerrainTrans (Name, TransMatrix) VALUES (?,?);");
bool bPrepared = m_prepareStat->Create(*m_terrainDb, *insertSQL, ESQLitePreparedStatementFlags::Presisent);//构建状态
m_prepareStat->SetBindingValueByIndex(1, objectName);//绑定参数
m_prepareStat->SetBindingValueByIndex(2, blobData);
m_prepareStat->Execute();//执行语句

查询数据:

const FString selectSQL = TEXT("SELECT TransMatrix FROM TT_TerrainTrans WHERE Name = ?;");
if(m_prepareStat->Step() == ESQLitePreparedStatementStepResult::Row)
{
    m_prepareStat->GetColumnValueByName(TEXT("TransMatrix"), binaryData);
}

序列化时的一些参数配置

利用FObjectAndNameAsStringProxyArchive设置资产的一些属性,进而影响后续序列化的结果。

TArray<uint8> binaryData;
FObjectWriter objectWriter(binaryData);
UTileTransformAsset* transAsset = NewObject<UTileTransformAsset>(GetTransientPackage());//UTileTransformAsset继承与UObject
FObjectAndNameAsStringProxyArchive writeArchive(objectWriter, true);
writeArchive.SetIsPersisent(true);
transAsset->Serialize(writeArchive);

序列化数据的压缩与解压

要压缩 FObjectAndNameAsStringProxyArchive 序列化的数据,可以在将其数据存储到TArray<uint8>后使用 Unreal Engine 提供的压缩工具。常用的方法是 FCompression 类,它支持多种压缩算法,如 Zlib、Gzip、LZ4 等。

  1. 序列化并压缩数据

首先,将对象序列化到一个字节数组TArray<uint8>中,然后对数据进行压缩。

#include "Serialization/ObjectAndNameAsStringProxyArchive.h"
#include "Serialization/MemoryWriter.h"
#include "Serialization/MemoryReader.h"
#include "Misc/Compression.h"

bool SerializeAndCompressObject(UObject* Object, TArray<uint8>& OutCompressedData)
{
    // 创建一个字节数组用于序列化数据
    TArray<uint8> UncompressedData;
    FMemoryWriter MemoryWriter(UncompressedData, true);

    // 使用 FObjectAndNameAsStringProxyArchive 进行序列化
    FObjectAndNameAsStringProxyArchive Ar(MemoryWriter, true);
    Object->Serialize(Ar);

    // 压缩数据
    return FCompression::CompressMemory(
        NAME_Zlib,                      // 使用 Zlib 算法压缩
        OutCompressedData,              // 输出压缩后的数据
        UncompressedData.GetData(),     // 输入未压缩的数据
        UncompressedData.Num(),         // 输入数据的字节数
        COMPRESS_BiasMemory             // 压缩方式,可根据需要选择
    );
}

在这个代码中:

  • SerializeAndCompressObject 函数接受一个 UObject 指针,并将序列化后压缩的数据存储在 OutCompressedData 中。
  • FCompression::CompressMemory 使用 Zlib 算法对序列化的数据进行压缩。
  1. 解压缩并反序列化数据

在需要读取压缩数据时,首先解压缩数据,然后使用 FObjectAndNameAsStringProxyArchive 将其反序列化到对象中。

bool DecompressAndDeserializeObject(UObject* Object, const TArray<uint8>& CompressedData)
{
    // 创建一个数组用于存储解压后的数据
    TArray<uint8> DecompressedData;

    // 获取解压后的数据大小
    int32 UncompressedSize = FCompression::GetUncompressedSize(NAME_Zlib, CompressedData.GetData(), CompressedData.Num());

    if (UncompressedSize <= 0)
    {
        return false; // 无法获取解压后的大小
    }

    // 设置解压后的数据大小
    DecompressedData.SetNum(UncompressedSize);

    // 解压数据
    if (!FCompression::UncompressMemory(
            NAME_Zlib,
            DecompressedData.GetData(),
            DecompressedData.Num(),
            CompressedData.GetData(),
            CompressedData.Num()))
    {
        return false; // 解压失败
    }

    // 使用 FMemoryReader 和 FObjectAndNameAsStringProxyArchive 进行反序列化
    FMemoryReader MemoryReader(DecompressedData, true);
    FObjectAndNameAsStringProxyArchive Ar(MemoryReader, true);
    Object->Serialize(Ar);

    return true;
}

Reference

  1. https://codingbabble.com/posts/using-sqlite-with-unreal/

「如果这篇文章对你有用,请随意打赏」

Heisenberg Blog

如果这篇文章对你有用,请随意打赏

使用微信扫描二维码完成支付


comments powered by Disqus