UnrealEngineのクロスプラットフォーム対応ってXamarin(.NET MAUI)・Flutter・Cordova等に代わる、スマホアプリやPCアプリの新しい開発手法になるのではないか?と思い、様々な検証を行っているシリーズの第一弾で「UnrealEngineでQRコードを読んじゃうぞ!」に挑戦していこうと思います。
最終目標はWindows/Android/iOSのカメラで撮影した画像からQRコードを認識する事がゴール!
これが最終目標となります。
今回は、ZXingをUEで使える状態にするまでの流れを解説していきたいと思います。
実は、お金を出せば解決
調査してみたところ、UnrealEngineマーケットプレイスにQRコードリーダーが存在しているようでした。
¥1,849で解決する問題ですが、月2万円の私のお小遣いの約10%を占める金額ということで、到底私には払える額ではないので自作していこうと思います。運よくプログラマーという職業ですしおすし。
ZXing
Java・Androidの世界では結構有名なZxingというQRコード・バーコードを読み取るライブラリがありまして、それのC++版を使えばUnrealEngineでもQRコードが読めそうです。
実際マーケットプレイスで見たプラグインもZXingの名を使ってますし、実績もあるってことでZXingのC++版で実装を試みることにしてみました。
ちなみに、ZXingの読み方ですが「ゼブラクロッシング」と読みます。シマウマの柄がバーコードに見えるからですかね?
ZXing C++版
Githubからzxing-cppをダウンロードすることができます。
ライセンスは Apache License 2.0になりますので「ロゴを改変して再配布したり、自分の作品のようにネットで公開する」以外は問題ないので、商用利用も全然OKですね。
また、ソースの中に各プラットフォーム用のラッパーも入っているのでAndroid/iOSでも動作させることが出来そうな点がうれしいですね。
環境準備
今回は以下の環境で構築します。インストール方法についてはネットの記事などを参考にしてみてください。(そんなに難しい部分は無いはずです)
- Visual Studio Community 2019
- MSVC v142 – VS 2019 C++ x64/x86ビルドツール
- Unreal Engine 5.1.1
C++周りの環境がよく分からないって方は、Visual Studio Communityをインストールして、Visual Studio Installerから「C++によるデスクトップ開発」を追加してもらえればOKです。
Windows 64Bit版のライブラリの作成
ソース一式をダウンロードして解凍してみてください、このようなフォルダ構成が展開されます。
Visual Studio Communityを起動し、上記フォルダを開いてください。するとCMakeファイルを自動的に認識し以下のような画面が表示されます。
画面上部の「スタートアップアイテムの選択」をクリックし、”ZXingReader.exe(example\ZXingReader.exe)”を選択します。
更に、その左側にある”x64-Debug”と表示されているドロップダウンを選択し、”構成を管理します”をクリック。表示されたCMakeSettings.jsonウインドウ内の「構成名」を”x64-Release”に修正し、「構成の種類」を”Release”に変更します。
で、Ctrl+Sで保存するとCMakeが生成されてビルドできるようになるので、画面上部の緑の再生ボタン(“ZXingReader.exe(example\ZXingReader.exe)”)をクリックします。
ビルドが完了すると、ZXingを解凍したフォルダ配下の”/out/build/x64-Release/core”配下にZXing.libが作られていればOKです。
UnrealEngine起動!
UnrealEngineを起動し新規プロジェクトを作成します。QRコードを読み取りたいだけですが、みんな大好き「サードパーソン」から開始してみましょう。なお、プロジェクトはC++で作ってください。(ブループリントで作っても大丈夫ですが、面倒な手順を踏みたくないのでC++にしといてください)
プロジェクト名は”ZXing”としました。
C++でプロジェクトを作成すると、C++開発環境に合わせたファイルの書き出しや、ビルド等が始まるのでちょっと時間が掛かりますが、見慣れた画面が表示されるまで少々お待ちください。
実家のような安心感のあるサードパーソンテンプレートが開きました。
プロジェクトにZXingを組み込む
作成したプロジェクトをエクスプローラーで表示します。
“Source”というフォルダがあるので、その配下に”ThirdParty”というフォルダを作成し、先ほどVisual Studio Communityで開いたフォルダをそのままコピーします。
コピーしたフォルダには不要なフォルダや、先ほど作成しらZXingのライブラリファイルも含まれるので、ちょっと整理していきましょう。
まずは先ほど作成したZXing.lib(/Source/ThirdParty/zxing-cpp-master/out/build/x64-Release/core配下)を/Source/ThirdParty/zxing-cpp-master配下にlibフォルダを作り、更にその配下にx64フォルダを作り、その中にZXing.libをコピーします。
次に/Source/ThirdParty/zxing-cpp-master配下の不要なフォルダを削除します。不要なフォルダは・・・
- .gtihub
- .vs
- example
- test
- フォルダ直下のファイル全て
となり、最終的にはこんな感じになればOKです。
Build.csを編集しZXingライブラリを読み込む
UnrealEngineでプロジェクトを作成した時にC++のIDEも一緒に起動しているかと思います。(私の環境ではVisual Studio Codeが設定されているので、Visual Studio Codeの画面で解説させて頂きますが、IDEで何かするワケではないのでツールは何でも構いません)
プロジェクト配下の/Source/ZXing/Zxing.Build.csを開きましょう。プロジェクト作成時には以下のようになっています。
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class ZXing : ModuleRules
{
public ZXing(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "EnhancedInput" });
}
}
これを以下のように修正します。
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System.IO;
public class ZXing : ModuleRules
{
public ZXing(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "EnhancedInput" });
// ZXingライブラリのパスを定義
string ZXingLibPath = Path.Combine(ThirdPartyPath, "zxing-cpp-master");
// スタティックライブラリのインクルードパスとライブラリパスを定義
PublicSystemIncludePaths.Add(Path.Combine(ZXingLibPath, "core/src"));
PublicAdditionalLibraries.Add(Path.Combine(ZXingLibPath, "lib", "x64", "ZXing.lib"));
}
private string ThirdPartyPath {
get { return Path.GetFullPath(Path.Combine(ModuleDirectory, "../../Source/ThirdParty")); }
}
}
ThirpPartyPathという文字列に先ほど作成したSource配下のThirdPartyパスを定義しておきます。これは参照ライブラリが追加された時の基準となるパスとなります。
またZXingLibPathという文字列には、ThirdPartyを基点としたZXingのパスを定義しています。これはGithubからダウンロードしたままであれば”zxing-cpp-master”となります。
更に更に、PublicSystemIncludePathsにはZXingのソースが入ったパスを、PublicAdditionalLibrariesにはビルドしたZXing.libのパスを追加しています。
新規C++の作成
UnrealEngineエディタに戻って、新規C++を作成します。
Blueprint Function Libraryを新規で作成します。
名前はそのまま”MyBlueprintFunctionLibrary”でいきます。
クラスを作成するとIDE(Visual Studio Code)が表示され、作成したMyBlueprintFunctionLibraryが追加されていることが分かります。
それでは、MyBlueprintFunctionLibraryにQRコード読み取り処理を行う”ReadQRCode”を追加していきましょう。
まずはヘッダーファイルです。ブループリントから呼び出せるようにBlueprintCallableで定義しています。引数にはTexture2Dを受け取り、結果の文字列を返却するだけの単純な関数となっています。
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "ReadBarcode.h"
#include "DecodeHints.h"
#include "ImageView.h"
#include "MyBlueprintFunctionLibrary.generated.h"
using namespace ZXing;
/**
*
*/
UCLASS()
class ZXING_API UMyBlueprintFunctionLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
// QRコード読取関数の追加
UFUNCTION(BlueprintCallable, Category = "QRCode", BlueprintPure)
static FString ReadQRCode(UTexture2D* texture2D);
};
実際の処理はこんな感じになりました。
引数で受け取ったTexture2Dから幅と高さを取得し、RGB配列を取得し、ZXingのReadBarcode関数に解析させた結果を、FString型に変換してBPに返しているという処理になります。
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyBlueprintFunctionLibrary.h"
#include "ImageUtils.h"
FString UMyBlueprintFunctionLibrary::ReadQRCode(UTexture2D* texture2D, TArray<uint8>& imgDataArray){
// 読み込んだQRコードを格納する
FString code = "";
// テクスチャデータ読込時のLockにてnullptrが返却されないように調整(要詳細確認)
texture2D->CompressionSettings = TextureCompressionSettings::TC_VectorDisplacementmap;
texture2D->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps;
texture2D->SRGB = false;
texture2D->UpdateResource();
// テクスチャの先頭mipmapの取得
FTexture2DMipMap& mipmap = texture2D->GetPlatformData()->Mips[0];
// 幅と高さを取得
int width = texture2D->GetPlatformData()->SizeX;
int height = texture2D->GetPlatformData()->SizeY;
// mipmapをロックしつつRGB配列を取得
uint8* Data = (uint8*)mipmap.BulkData.Lock(LOCK_READ_ONLY);
if (Data == nullptr) {
mipmap.BulkData.Unlock();
return code;
}
// ZXingにRGB配列を渡してQRコードを解析させる
DecodeHints hints;
Result result = ZXing::ReadBarcode({Data, width, height, ImageFormat::RGB}, hints);
// 結果文字列をFString型に変換
code = result.text().c_str();
// ロック解除
mipmap.BulkData.Unlock();
// 取得したQRコードを返却
return code;
}
UnrealEngineのビルドボタンをクリックすると・・・・
何故か大量のエラーが発生します。
この大量のエラーはビルド自体には成功しているけど、作成したZXing.libとの関連付け(Link)に失敗しているという内容になります。原因が分からなかったので調査していると、以下の情報を見つけました。
https://forums.unrealengine.com/t/getting-linker-errors-when-using-the-std-library/747644
どうもライブコーディングが悪さをしてるっぽいです。先ほどのエラーダイアログの一番上にビルドのコマンドが表示されているので、このコマンドを使って手動でビルドをしてみましょう。
コピーしてみるとこんな感じでした。
Running C:\Program Files\Epic Games\UE_5.1\Engine\Build\BatchFiles\Build.bat -Target="ZXingEditor Win64 Development -Project=""C:/Users/toshiyuki/Documents/Unreal Projects/ZXing/ZXing.uproject""" -LiveCoding -LiveCodingModules="C:/Program Files/Epic Games/UE_5.1/Engine/Intermediate/LiveCodingModules.txt" -LiveCodingManifest="C:/Program Files/Epic Games/UE_5.1/Engine/Intermediate/LiveCoding.json" -WaitMutex -LiveCodingLimit=100
ここからライブコーディングっぽい部分や、不要なダブルクォーテーションを削除すると、こうなります。(合ってるか分かりませんが、これで動いたので掲載しております)
"C:\Program Files\Epic Games\UE_5.1\Engine\Build\BatchFiles\Build.bat" -Target="ZXingEditor Win64 Development" -Project="C:/Users/toshiyuki/Documents/Unreal Projects/ZXing/ZXing.uproject"
これをコマンドプロンプトで実行してみます。この時、UnrealEngineを終了させておかないと、以下のようなエラーが表示されますので注意してください。
UnrealEngineを終了させてから再度ビルドすると、素直に正常終了してくれました。
再びUnrelaEngineを起動してプロジェクトを開きます。
QRコードの読み取りが正常に動作するか確認してみます。ネット上のQRコード生成サービス等でQRコードのpngファイルを作成し、UnrealEngineにテクスチャとして取り込みます。
BP_ThirdPersonCharacterにQRコード読み取りテスト処理を作ります。ReadQRCodeノード(これが先ほどC++で作成したノードですね)に、上記でインポートしたQRコードのテクスチャを渡します。
そして戻り値のReturn ValueをPrintStringノードの繋ぐだけです。
さー実行してみましょう!
QRコードに含まれるURLを読み込んで画面の表示することが出来ました!無事にZXingをUEで使う事に成功しているようです。
今回はここまでとなります。
次回はカメラ画像を使ったQRコード読み取りにチャレンジしてみる予定です。
参考にさせて頂いたサイトなど
まめお様 PaperSloth’s diary:UE4外部ライブラリの使用方法について
UnrealEngine公式ドキュメント:サードパーティライブラリ
EPIC DevCommunity:Getting linker errors when using the std library
砥才人様:【UE4 C++】 外部图片读取、Texture 保存 png
様々な知見をネットで公開して頂きありがとうございます。
No responses yet