simplestarの技術ブログ

目的を書いて、思想と試行、結果と考察、そして具体的な手段を記録します。

UnrealEngine4でUTexture2Dをシステムメモリから更新する

まず Unreal Engine 4 ではマテリアルに適用するテクスチャは UTexture2D というものだそうです。(調べただけでまだ触っていない)
基本的にはGPU側で超並列演算を行うもののようですが、特別にシステムメモリのbyte配列からテクスチャの内容を更新するUpdateTextureRegions関数が定義されています。(正しく動くかは知らない、調べただけ)

関連して C++ から WebCam にシンプルにアクセスするには
www.iki.fi/sol - Code - ESCAPI
が知られていて、ライセンスフリーでカメラの解像度を決定できて
各種カメラパラメータを手動 or オートで切り替えられつつ、サンプル時におそらく argb の int 値配列を返すもののようです。(日本語が変でごめん、情報が正しく伝わればよいので、書き換えず、そのままにしてます。)

escapi は IMFSourceReaderというDirectShowの後継を利用していて、ソースも公開されていて、そのバッファを書き込んでいるコードを見ると
https://github.com/jarikomppa/escapi/blob/master/escapi_dll/capture.cpp
どうも、一段不要かもしれない for 文を回して書き込みを行っている箇所があったので
各自、ここを独自に高速化するように書き換えてカメラ画像情報を受け取れるようにすると良さそう。

あとはこれらの知識をつないで、Unreal Engine にて簡易に画像ビューア、コンピュータビジョンを用いたゲームを組めます。

実際に動かしてみないと、調査内容は信用できないので、実際に動いたら追記します。
という、ただの個人メモ

UnrealEngine4.12のC++プラグインでスタティックリンクライブラリを利用する

入門書や公式リファレンスを読んでいてびっくりしたのですが
Unreal Engine におけるプラグイン機能は、次の図で示す通りゲームのプロジェクトにC++ソースコードが配置される感じです。

f:id:simplestar_tech:20160703175311j:plain

プラグインは独立したプロジェクトにならないことに衝撃を受けました。

こんな感じのドキュメントがあるほど
けっこう面倒な処理を手で加えないと
スタティックリンクライブラリを使うプラグインというものを作れないそうなのですよ。

私に限ってかもしれませんが
自分の書くコードにはほとんど価値なんてなくて、世にある優れたライブラリの力を借りつつプラグインって作るものですよね?
そんな作り方が簡単にできない仕組みになっているとは、どういうことなのか!

と、また落とし穴にハマったので抜け出してみたいと思います。

OpenCVをUnreal Engine4で使用する、というドキュメントに参考になるコードがあったので利用します。

追記;
で、実際にスタティックリンクライブラリを介してDLLの実装内容を利用できるところまで確認しましたので要点を記載します。

1.Win32コンソールアプリのテンプレートを利用してDLLを作成します。(シンボルのエクスポートを忘れずに行うこと)
今回は単純に動作確認したいだけだったので、SimpleMathという適当なDLLプロジェクトを用意し、関数を次の図に示す通りに作りました。
f:id:simplestar_tech:20160704081254j:plain
落とし穴がさっそくあります。Unreal Engine 4はx64プロジェクトですので、このDLLもx64でビルドしなければいけません。(DLL機能を使うコードを書いてリンクする時にエラーが出ます。)
作った成果物のうち .lib ファイル(スタティックリンクライブラリ)は次の通りの場所に配置します。
f:id:simplestar_tech:20160704081901j:plain
ThirdParty製ライブラリファイルの配置時にも落とし穴があります。.libとセットで.dllファイルも配置したいところですが、プロジェクトが.dllの配置を見ている箇所は一つしかありません。
次に示すBinaries\Win64フォルダです。(ここにThirdPartyの.dllファイルをすべて配置します。試していませんがたぶんサブフォルダ切れると思います。私が設計担当ならそう作りますので)
f:id:simplestar_tech:20160704082417j:plain

2.プラグイン側でThirdPartyのスタティックリンクライブラリ、ダイナミックリンクライブラリを参照します。
さっそく落とし穴があり、ハマりました。例で示したOpenCVを利用するものを参考にするとコンパイルできなくなります。
Unreal Engine 4.12 からは次のコードにするとコンパイルできるようになるそうです。(ファイルはプラグインのSimplePlugin.Build.csファイルです。C#ファイルなので、C++プロジェクトから参照が正しく機能せず、Unreal Engine特有のクラス定義がどんなフィールドを持っているか、ランタイムで解析するなども行いました。しかし、簡単にプロジェクト名を取得する方法が無いようなので、おかしな文字列操作コードを仕込んでいます。TargetInfoにプロジェクト名とかあると良かったのですけどね、なかったです。)

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
using System;
using System.IO;
using System.Reflection;

namespace UnrealBuildTool.Rules
{
	public class SimplePlugin : ModuleRules
	{
		public SimplePlugin(TargetInfo Target)
		{
			PublicIncludePaths.AddRange(
				new string[] {
					// ... add public include paths required here ...
				}
				);

			PrivateIncludePaths.AddRange(
				new string[] {
					"Developer/SimplePlugin/Private",
					// ... add other private include paths required here ...
				}
				);

			PublicDependencyModuleNames.AddRange(
				new string[]
				{
					"Core",
					// ... add other public dependencies that you statically link with here ...
				}
				);

			PrivateDependencyModuleNames.AddRange(
				new string[]
				{
					// ... add private dependencies that you statically link with here ...
				}
				);

			DynamicallyLoadedModuleNames.AddRange(
				new string[]
				{
					// ... add any modules that your module loads dynamically here ...
				}
				);


            LoadThardParty(Target);
        }

        private string ModulePath
        {
            get {
                FileReference CheckProjectFile;
                string ProjectNameModuleRules = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
                string ProjectName = ProjectNameModuleRules.Substring(0, ProjectNameModuleRules.Length - "ModuleRules".Length);
                UProjectInfo.TryGetProjectForTarget(ProjectName, out CheckProjectFile);
                RulesAssembly r = RulesCompiler.CreateProjectRulesAssembly(CheckProjectFile);
                FileReference f = r.GetModuleFileName(this.GetType().Name);
                return Path.GetDirectoryName(f.CanonicalName);
            }
        }
        private string ThirdPartyPath
        {
            get { return Path.GetFullPath(Path.Combine(ModulePath, "../../ThirdParty/")); }
        }

        public bool LoadThardParty(TargetInfo Target)
        {
            bool isLibrarySupported = false;

            // Create Plugin Path 
            string SimpleMathPath = Path.Combine(ThirdPartyPath, "SimpleMath");

            // Get Library Path 
            string LibPath = "";
            bool isdebug = Target.Configuration == UnrealTargetConfiguration.Debug && BuildConfiguration.bDebugBuildsActuallyUseDebugCRT;
            if (Target.Platform == UnrealTargetPlatform.Win64)
            {
                LibPath = Path.Combine(SimpleMathPath, "Libraries", "Win64");
                isLibrarySupported = true;
            }
            else if (Target.Platform == UnrealTargetPlatform.Win32)
            {
                LibPath = Path.Combine(SimpleMathPath, "Libraries", "Win32");
                isLibrarySupported = true;
            }
            else
            {
                string Err = string.Format("{0} dedicated server is made to depend on {1}. We want to avoid this, please correct module dependencies.", Target.Platform.ToString(), this.ToString()); System.Console.WriteLine(Err);
            }

            if (isLibrarySupported)
            {
                //Add Include path 
                PublicIncludePaths.AddRange(new string[] { Path.Combine(SimpleMathPath, "Includes") });

                // Add Library Path 
                PublicLibraryPaths.Add(LibPath);

                // Add Dependencies 
                if (!isdebug)
                {
                    //Add Static Libraries
                    PublicAdditionalLibraries.Add("SimpleMath.lib");

                    //Add Dynamic Libraries
                    PublicDelayLoadDLLs.Add("SimpleMath.dll");
                }
                else
                {
                    //Add Static Libraries (Debug Version)
                    PublicAdditionalLibraries.Add("SimpleMath.lib");

                    //Add Dynamic Libraries (Debug Version)
                    PublicDelayLoadDLLs.Add("SimpleMath.dll");
                }
            }

            // Definitions.Add(string.Format("SOME_PREPROCESSOR={0}", isLibrarySupported ? 1 : 0));

            return isLibrarySupported;
        }
    }
}

3.プラグインコードでThirdPartyのライブラリを利用する。
f:id:simplestar_tech:20160704083700j:plain
この赤い波線が気になるのですよね。
実行時はIncludesへのパスが設定されているのでコンパイルエラーは起こりませんが、Visual Studio でコーディング中はパスの参照が切れているのでヘッダファイルが見つからない旨を教えてくれます。
直接ゲームプロジェクトのインクルードディレクトリをいじれば解決しそうですが、試してはいません。(たぶん解決しますが、プロジェクトを自動生成している関係でいつか誰かがクリアしちゃうものなのだと覚悟する必要があるでしょう)
プラグイン側のテスト実装は次の通りです。(ここにあるコメントの通り、相対パスでIncludesにあるヘッダファイルを指定するとプロジェクト設定に非依存で参照問題を解決できます。)

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.

#include "SimplePluginPrivatePCH.h"
#include "SimpleMath.h"
// #include "../../../ThirdParty/SimpleMath/Includes/SimpleMath.h"

class FSimplePlugin : public ISimplePlugin
{
	/** IModuleInterface implementation */
	virtual void StartupModule() override;
	virtual void ShutdownModule() override;
};

IMPLEMENT_MODULE( FSimplePlugin, SimplePlugin )



void FSimplePlugin::StartupModule()
{
	// This code will execute after your module is loaded into memory (but after global variables are initialized, of course.)

	CSimpleMath a;
	int sum = a.Sum3Int(1, 2, 3);
	return;
}


void FSimplePlugin::ShutdownModule()
{
	// This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
	// we call this function before unloading the module.
}

デバッグ実行時に落とし穴がありました。
エディタを起動しながらステップ実行してプラグインのコードを動作確認をしたいなら、ビルド時の構成は次のものを利用します。(初回ビルドに5分ほど時間を要しますよ。)
f:id:simplestar_tech:20160704084404j:plain
ディフォルトではDevelopment Editorになっているようで、そのときの挙動をみるとリリースビルドに近かったです。
Sippingはさらに最適化が施される形なのかなと予想(選んで実行はしていない)

はい、ここまでの1.2.3.で示したステップを踏むことで私の環境ではThridParty製のライブラリをプラグインで利用できることが確認できました。
自分は自分で独自にOpenCVなどのThridParty製のライブラリをUnreal Engineプラグインで利用する予定です。

UnrealEngine4.12でAndroidアプリ作成とインストール

フィーリングで探すと File メニューからパッケージ化するというものが見つかります。

パッケージ化メニューからAndroid(すべて)を選べばインストールバッチファイルが作成されます。
これを実行すればAndroid端末にUnreal Engineのゲームがインストールされます。

やった、インストールできた!なんて喜んでいるあなた
はい、アウト~
実はすでに落とし穴x3にハマっていますので、一つずつ抜け出しましょう。

落とし穴1.スタートアップコンテンツ有りにしているとパッケージ化に時間を要します。(30~40分くらい?)
落とし穴2.ディフォルトのレベルを指定していないとブラックスクリーンになります。(エディタのアクティブなレベルは表示されないのですよ。)
落とし穴3.すべてのAndroidでパッケージ化すると、ファイルがデカ過ぎてゲームを実行できなくなります。

最初の落とし穴から抜け出すのは簡単ですね、プロジェクト作成時に余計なコンテンツを入れなければよいだけです。(コンテンツブラウザから削除してもよい)
二つ目の落とし穴から抜け出すのは難しいですね。Unityの感覚でシーンを指定していなくても、エディタでアクティブだったらそれが表示されると思っていたら大間違いです。
Unreal Engine は Blank プロジェクトを実行すると Black Screen になるのが正しい動作なのです。
適当なこと書いているブログだと Mobile HDR のチェックを外すとかありますが、そんな情報には惑わされないでください!

具体的な解決方法は次の図の通り、プロジェクト設定にてディフォルトのレベルを指定します。
f:id:simplestar_tech:20160703115846j:plain
これでゲーム起動時のブラックスクリーン問題は解決します。

最後の落とし穴もまた抜け出すのが難しい。
スタートアップコンテンツ有りですべてのAndroid向けにビルドすると2.3GBほどのファイルサイズになってしまい
Androidにインストールが成功しても、ストレージに余裕がないなどの理由でOBBファイルは一緒に入らず、ファイルにアクセスできずNo Google Play Store Keyのダイアログが出て先に進めなくなります。

同じような問題はこちらに示されています。
No Google Play Store Key - UE4 AnswerHub

具体的な解決方法としては Blank プロジェクトにして、スタートアップコンテンツは無しにして、パッケージ化の項目で Android(ETC1) を選びます。
パッケージ化も高速で終わり、OBBファイルも小さく(それでも50MBあったけど)インストールで入りきらないなんてこともなくなります。

はい、そんな落とし穴x3にハマったのは私です!
で、すべての落とし穴から抜け出すと次の図で示すように、お手持ちのAndroid端末にてUnreal Engineで作ったゲームが遊べるようになります。

f:id:simplestar_tech:20160703121445j:plain

ちなみに、fps は 60 出てました。
fps を表示したいときは指4本でタップして、コンソールコマンドに stat fps を打ち込むと表示されるようになります。

ゲームエンジン初心者が一番最初に行うだろう作業に
空のプロジェクトをビルドしてAndroid端末で動作チェックするという作業がありますが
Unreal Engineには、こんな落とし穴があるということをここに記録しておきましょう。

プラグイン作成中ですが、つい共有したくなったので書きました。
では、プラグイン作成の作業に戻ります。

UnrealEngine4.12のC++プラグイン機能を利用する

オブジェクト(UE4だとActor)のTransformをC++のコードから編集したいだけなんです。

私がどんな落とし穴にハマっていくか、記録していきます。

Visual Studio 2015 を Community からインストールしました。
あ、ここまでは大丈夫です。
C++がディフォルトで入っていないことは下調べして知っているので、ちゃんとC++の項目にチェックを入れてインストールしました。

7/2 追記:
公式ドキュメント入門書もバッチリ読んでます。
間違いようがないです。

そしてその作業フローを実行すると

コンパイル失敗」

Unreal Engine を 4.12, 4.11, 4.10 と試しても同じエラーが出ました。
これは…Visual Studio の Update に問題があるのかな?

と調べると、確かにそんな記述が数時間前から公式フォーラムにちらほら上がりだしてきた。

When i start UE4 with C++ it gives me compile failed every time - UE4 AnswerHub
Unreal Error "This project could not be compiled. Would you like to open it in Visual Studio?"
Compile Failed when creating Cpp project - UE4 AnswerHub

どうも、Unreal Engine 4 のビルドエラー祭りにナイスタイミングでハマったようだ。
4.12.4 の次期バージョンでは直る予定とのことで
お急ぎの方はひとまず Visual Studio 2015 の Update 3 を Update 2 に戻して使ってほしいとのことです。

めっちゃ時間がかかっていますが Visual Studio のバージョン差し戻し作業しています。
なんか…Visual Studioのバージョン戻しても同じエラーが起こりそうな予感はしているのだけど、とりあえずやってみます。

具体的には、先に Update 3 が適用された Visual Studio をアンインストールして
次のリンクから Update 2 のインストーラ(Visual Studio Community 2015 with Update 2)を取得してインストールするだけです。
Visual Studio Release Notes | Visual StudioVisual Studio 2015 Update 2 now available for download


追記:
ぎゃあぁあああ!
update-2 のインストーラでインストールしても update-3 になってしまった!
終わった。
どうやったら update-2 のまま使えるのか…戻す方法とか無いの!?
何よりここまで時間を無駄にしたことに腹が立っている

追記:
と、ここで転機がおとずれる
実はリンク先で取得した Visual Studio は英語版だったので、今までUE4が文字化けして表示していたエラー内容が表示されるようになった
そこをキーワードに検索をかけて解決策の提示にたどり着く

forums.unrealengine.com

具体的な解決方法は
C:\Program Files (x86)\Epic Games\4.12\Engine\Source\Runtime\Core\Public\Windows\WindowsPlatformCompilerSetup.h
ファイルの末尾に#pragma warning(disable : 4599)を追記して Unreal Engine 4.12 をC++で起動してみると良いとのこと。

うおぉおお!初めてコンパイルエラーが起きずにプロジェクトが立ち上がったぞ!
私の環境では解決した!
やっと入門書の通りに作業が開始できる。

できた!
f:id:simplestar_tech:20160702184120j:plain

Unity の MonoBehaviour を継承したスクリプトコンポーネントに感覚が似ていました。
Unreal Engine だと Actor の Location をいじることで Actor の位置を変えることができました。
以下は実装コードの一部です。

#include "GameFramework/Actor.h"
#include "FloatingAnimActor.generated.h"

UCLASS()
class HEADERREPLACEPROJECT_API AFloatingAnimActor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AFloatingAnimActor();

	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	
	// Called every frame
	virtual void Tick( float DeltaSeconds ) override;

	float RunningTime;
	
};

// 以下は実装ファイルの記述

// Called every frame
void AFloatingAnimActor::Tick( float DeltaTime )
{
	Super::Tick( DeltaTime );

	FVector NewLocation = GetActorLocation();
	float DeltaHeight = (FMath::Sin(RunningTime + DeltaTime) - FMath::Sin(RunningTime));
	NewLocation.Z += DeltaHeight * 20.0f;       //Scale our height by a factor of 20
	RunningTime += DeltaTime;
	SetActorLocation(NewLocation);
}

いや、驚きましたよ。
エディタのコンパイルボタンを押したら、即上記のコードの編集内容が反映されてゲームが実行できました。
エントリの目標達成はまだですが、ひとまず一行目に書いたやりたいことまでは確認できました。

リンクメモ

この記事は、無視してください!


まずここ
OpenGL+Objective-C編 - 勉強

理解したらここ
livedoor Techブログ : 猫でもわかるiPhoneで画像にフィルターをかける方法

あと、ここのオプティカルフローの説明良い
https://courses.engr.illinois.edu/cs543/sp2012/lectures/Lecture%2008%20-%20Feature%20Tracking%20and%20Optical%20Flow%20-%20Vision_Spring2012.pdf

うーむ、これ?
http://blog.negativemind.com/2015/05/15/point-cloud-for-unity/
からの
床井研究室 - シェーダで Point Sprite

ここ、いい
画像処理 — OpenCV-CookBook

ヒストグラム平坦化の原理について
ヒストグラム平坦化のアルゴリズム

関数の説明はここ
ヒストグラム — opencv 2.2 documentation


それでもうまくいかなかったら試す
ゴリラになる知識: ガンマ補正



これ試してみるか
【iOS】UIImageとNSString(Base64)の相互変換 - Qiita


まずは動くかみとけ
Unity C# JSONObjectを使って簡単なJSON文字列をパースする - Qiita

Windowsでキーボード割り当てを変更

先日、GPUが壊れてPCが起動できなくなったことがあり
問題の切り分けのためOSの再インストールを行いました。

そのため、使っている英字キーボードのカスタマイズ情報が無くなりました。
再度割り当てるので、次の記事を参考に再割り当てしました。

www.gigafree.net

設定はこちら
f:id:simplestar_tech:20170717140025j:plain

うまくいきました。
環境は Windows 10 64bit

あと、日本語キーボードを接続したのに、英字キーボードとして認識されたときの問題に直面
Windows 10 環境では 設定→地域と言語 からキーボード配列を設定する項目があるので、そこで英字→日本語にして
再起動すると期待通りに日本語キーボードで日本語を打てた(あたりまえだけど)