一日分の備忘録

ゲーム制作におけるいろいろな実装の備忘録。基本的にはゲームエンジン中心。その中でもUE4がメイン。プログラマーなのでプログラム的な記事が多くなるかも

場面転換的なクロスフェードの備忘録

お久しぶりでございます。

平成も終わり令和になりましたので久しぶりの記事を書きます。今回はクロスフェードの実装。

クロスフェード自体は一瞬で終わるので、クロスフェードの中でも、ワープや場面転換に使えそうなクロスフェードを実装。

一瞬で終わるクロスフェードの実装

マテリアル作成してMaterialDomainをPostProcessにして以下の通りにノード組んでBPでLerpを0-1で変動サせていくだけ!簡単!

f:id:oneday-memorandum:20190502142123p:plain

 

↑に追加実装をしてみる。

今回はこんな感じで実装。

youtu.be

 

SceneCapture2Dを使います。なんかこの間登壇したときもその前登壇したときもこれについて喋った気がする。SceneCapture2D大好き。

f:id:oneday-memorandum:20190502142730p:plain

 

例えばこんな感じで配置(ここに配置しているのはSceneCapture2Dではなく、普通のカメラです。)

f:id:oneday-memorandum:20190502142827p:plain

 

今回の実装用にマテリアルを組み直し。

f:id:oneday-memorandum:20190502145338p:plain

このときマテリアルの設定のBlendableLocationの設定次第ではかなり絵が暗くなるので、ポストプロセス的に問題なければ、BlendableLocationはToneMapping前にしておくと良いでしょう。(実はなんで絵が変わるかは雰囲気でしかわかってない。教えてほしい・・・切実に)

 

次にBPの実装

今回は汎用性は何も考えずにレベルブループリントに書いていく。

scenecapture2dで使う用のrendertarget生成

f:id:oneday-memorandum:20190502145149p:plain

 

パラメータ変更用にDynamicMaterialInstanceを生成(処理負荷的に少しでも抑えるために、SceneCapture2Dの毎フレームのキャプチャー処理をオフに)

f:id:oneday-memorandum:20190502145604p:plain

 

クロスフェード前処理

f:id:oneday-memorandum:20190502145731p:plain

f:id:oneday-memorandum:20190502145811p:plain

f:id:oneday-memorandum:20190502145902p:plain

f:id:oneday-memorandum:20190502150003p:plain

f:id:oneday-memorandum:20190502150044p:plain

Lerpのパラメータ更新にはタイムラインノードを使用
f:id:oneday-memorandum:20190502150137p:plain

 

以上で完成。

上記のままだと1回しかできない問題がある(わざと)ので、各々使い方に応じて適宜追加・修正してください。

なお激重なので基本的にはFrameGrabberを使うか、エンジン改造でシェーダー書かないとダメそうかも。

 

 

 

 

 

自作アセットインポートの備忘録。

音ゲー作りたいけどcsvで管理するといろいろ面倒だったのがきっかけで作ってみた。


独自アセットクラスの実装
今回は.txtファイルを読み込みたいのでFString1つだけで良さそう。独自の形式を作りたいときはそれ用に色々変数を作ると良いと思います。
以下のコードを書いた。

.cpp

#include "UTextObject.h"
UTextObject::UTextObject(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{

}

.h

#pragma once
#include "UTextObject.generated.h"

UCLASS()
class UTextObject : public UObject
{
	GENERATED_UCLASS_BODY()

private:

public:
	UPROPERTY(EditAnywhere)
	FString textData;
};

この時問題になったのが、generated.hがなくてインクルードで怒られるところ。
実際何で解決したのか正確にはわかっていない。
詳しくはまだわかってないので調べてます。
知っている方がいたらtwitterでこっそり教えてください。

GenerateVisualStudioFileをしたら良いっぽい?

次にFactoryクラスを実装。コレはアセットを作成できるようにしたり、インポートしたりをできるようにする感じで作ります。

.cpp

#include "UTextObjectFactory.h"
#include "UTextObject.h"

UTextObjectFactory::UTextObjectFactory(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{

	SupportedClass = UTextObject::StaticClass();
	bCreateNew = false;
	bEditorImport = true;
	bText = true;
	Formats.Add(TEXT("txt;TextFile"));

}

bool UTextObjectFactory::DoesSupportClass(UClass* Class)
{

	return (Class == UTextObject::StaticClass());

}

UClass* UTextObjectFactory::ResolveSupportedClass()
{

	return UTextObject::StaticClass();

}

UObject* UTextObjectFactory::FactoryCreateNew(
	UClass* InClass,
	UObject* InParent,
	FName InName,
	EObjectFlags Flags,
	UObject* Context,
	FFeedbackContext* Warn)
{
	UTextObject* NewMyAsset =
		CastChecked<UTextObject>(StaticConstructObject_Internal(InClass, InParent, InName, Flags));

	return NewMyAsset;
}

.h

#pragma once
#include "Factories/Factory.h"
#include "UTextObjectFactory.generated.h"

UCLASS()
class UTextObjectFactory : public UFactory
{
	GENERATED_UCLASS_BODY()

	virtual bool DoesSupportClass(UClass* Class) override;
	virtual UClass* ResolveSupportedClass() override;
	virtual UObject* FactoryCreateNew(
		UClass* _Class,
		UObject* _Parent,
		FName _Name,
		EObjectFlags _Flag,
		UObject* _Context,
		FFeedbackContext* _Warn) override;

};

ここまでできたら一旦ビルドして確認。新規作成>その他からTextObjectが作れるようになっているはず。
もしコレをインポートのみにしたい場合はbCreatenewをfalseにすると良い。
Formatsはファイル拡張子;説明と言った感じ。

bText = trueはテキストファイルからのインポートを指定している。コレをtrueにしていない場合は
以下でFactoryCreateText()を実装しているが、そのかわりにFactoryCreateBinary()を実装する必要がある。

Factoryクラスに以下を追加

UObject* UTextObjectFactory::FactoryCreateText(
	UClass* _Class,
	UObject* _Parent,
	FName _Name,
	EObjectFlags Flags,
	UObject* Context,
	const TCHAR* Type,
	const TCHAR*& Buffer,
	const TCHAR* BuferEnd,
	FFeedbackContext* Warn)
{

	FString Values;
	Values = FString(Buffer);

	UTextObject* NewMyAsset =
		CastChecked<UTextObject>(StaticConstructObject_Internal(_Class, _Parent, _Name, Flags));
	
	
	NewMyAsset->textData = Values;

	return NewMyAsset;
}

ここまで作ると、ひとまずtxtファイルがインポートできるはず。

次回はReimportについて書こうかな。

第3回UE4勉強会in大阪

参加してきました。というよりも、現在勤めている会社が主催でした。

今回はたいらも勉強会で「UE4でVR入門」という内容でしゃべらせてもらいました。

いかがそのスライドになります(なんかyoutube対応したら挿入される場所がいろいろおかしいくなってしまった)

 
togetterにもまとめられています↓

togetter.com

たいらの講演は、というよりも全講演なかなかいい感じの反応をもらえており、とても安心しました。

今回で弊社から登壇者が出たということで、今後もこの勉強会を開く意義?というものを感じられますね!(棒)

 

またツイートの方で、スライド中に出てくる参考資料などのurlを公開していますので、こちらにもはっつけておきます。

 

実は今日から東京出張に来ているので、今年の更新はもしかするとこれで最後になるかもしれません。あとVR触れない。つらい。

Twitterの方はたぶん2~3日おきくらいに更新すると思います。もしかするともう少し頻度は高いかもしれません。

ということで、簡単にですが第3回UE4勉強会in大阪の報告でした。

第4回は1月予定です。次回話すかどうかといわれるとおそらく平良は話しませんが、話すネタはいろいろ考えておきたいなぁと思います。

UE4で指定フォルダからファイルの読み込み(CSV編)

作っちゃったし備忘録書く必要あるかなとも思ったがとりあえず書いとく。CSV編としているが他のファイル形式を読み込んだものを書くかと言われると多分書かない。

.h

public:	

	UFUNCTION(BlueprintCallable, Category = "CSVReader")
	void LoadCSVData(FString Filename);
	UFUNCTION(BlueprintCallable, Category = "CSVReader")
	TArray<FString> GetRowDataByKey(FString Key);
	UFUNCTION(BlueprintCallable, Category = "CSVReader")
	TArray<FString> GetKey();

public:
	//変数
	TMap<FString, TArray<FString>> RowData;
	TArray<FString> KeyList;

.cpp

void ACSVReader::LoadCSVData(FString Filename)
{
	//CurrentDirectory取得
	FString dir = FPaths::GameDir();
	//ファイル名
	FString filename = dir + Filename + ".csv";
	//ファイル取得
	FString csvFullData;
	FFileHelper::LoadFileToString(csvFullData, *filename);
	//列で分解
	TArray<FString> row;
	csvFullData.ParseIntoArray(row, TEXT("\n"), true);
	//キーを登録
	row[0].ParseIntoArray(KeyList, TEXT(","), true);
	for(int i = 0 ; i != KeyList.Num() ; i++)
	{
		RowData.Add(KeyList[i]);
	}

	//キー以外の各要素をRowDataにキーと関連付けて登録
	TArray<FString> element;
	for (int i = 1; i != row.Num(); i++)
	{
		row[i].ParseIntoArray(element, TEXT(","), true);
		for (int j = 0; j != element.Num(); j++)
		{
			RowData[KeyList[j]].Add(element[j]);
		}
	}
}
TArray<FString> ACSVReader::GetRowDataByKey(FString Key)
{
	return RowData[Key];
}
TArray<FString> ACSVReader::GetKey()
{
	return KeyList;
}

どうやらFPaths::GameDir();でCurrentDirectoryが取得できるらしい。(ここだけ伝えたかった)
パッケージ化前は[各種ドライブ://~~~/MyProject/]
パッケージ化後は[各種ドライブ://~~~/exeファイルのある階層/MyProject/]
を取得する。
ちなみにブログ用に若干プログラムコードはサボってるので実装するときは各自ちゃんと作ってください。
使いみちあるかと言われると微妙になさそう。ユーザーに配布したゲームでCSV作ったらステージ自分で作れます!的な感じにしたらもしかするともしかするかもしれない。

UnityのProjectでDLLを使う備忘録

VisualStudio2015を開く
VisualC++ -> Win32コンソールアプリケーションを作成
Win32アプリケーションウィザードが開いたら次へ
アプリケーションの種類:DLL
追加のオプション:空のプロジェクトをonにして完了
.cppと.hを作る。

source.h

#pragma once

#define CPULUSSOURCE_API __declspec(dllimport) 

extern "C"//Unityで使うため
{
	CPULUSSOURCE_API float DllMultiPly(float a, float b, int loopNum = 100000000);
}

source.cpp

#include "CPlusSource.h"

float DllMultiPly(float a, float b, int loopNum)
{
	for (int i = 0; i < loopNum; i++)
	{
		float p = a * b;
	}
	return a * b;
}

ものすごい重い計算処理(仮)
ビルド前にソリューションプラットフォームをx64に変更。
ビルドしたらプロジェクトフォルダ内のx64内にDLLができている。
できたDLLをUnityProject/Assets/Plugins内に入れてUnity起動。
Scriptを作成しこんな感じで書く

script.cs

public class DllTestScript : MonoBehaviour {


    [DllImport("DLLTest", EntryPoint = "DllMultiply")]//今回作ったdll
    public static extern float CPlusOnlyDllMultiplyt( float a, float b, int loopNum = 100000000);
    void Start () {
        float tempFloatData = 0;

        //C++
        ProcessLoadTimeStart();

        tempFloatData = CPlusOnlyDllMultiplyt(8, 8);

        ProcessLoadTimeEnd();
        Debug.Log(tempFloatData);


        //C#
        ProcessLoadTimeStart();

        tempFloatData = TestCSharpLibrary.TestCSharpLibrary.SharpMultiply(100000000, 3, 5);//別で用意していたC#dllを使用
	//for(int i = 0 ; i < 100000000 ; i++)
	//{
	//	float p = 3 * 5;
	//}
	//ない場合はこれを関数化して使用

        ProcessLoadTimeEnd();
        Debug.Log(tempFloatData);

    }


    private float CheckTimer = 0;
    void ProcessLoadTimeStart()
    {
        // 現在の経過時間を取得
        CheckTimer = Time.realtimeSinceStartup;
    }

    void ProcessLoadTimeEnd()
    {
        // 処理完了後の経過時間から、保存していた経過時間を引く=処理時間
        CheckTimer = Time.realtimeSinceStartup - CheckTimer;

        Debug.Log("ProcessLoadTime : " + CheckTimer.ToString("0.00000"));
    }
    // Update is called once per frame
    void Update () 
    {
	
    }
}

Emptyを作ってScriptをアタッチして実行。
割りと苦戦したが1回できてしまえば後はあっさりできた。