げーむ開発徒然日記~怠惰のために勤勉~

Unreal Engineや3DCG制作について学んだことを記事にしていきます

GASでダッシュアビリティを実装しよう!(アビリティにキー入力を関連付ける)

 らくするです。
 今回は、いまアツいGameplayAbilitiesプラグインを使ったダッシュアビリティ(ダッシュ以外にも幅広く使える!)の実装について紹介したいと思います。
 本記事で紹介する方法はダッシュ以外にも色々と使い道があるため、GASの導入時についでに実装しておくといいかも…なんて思ったり。
※本記事の実装方法を用いて生じたいかなる問題に対しても責任を負えませんのでご了承ください。

 今回の記事を書くに至った経緯としては、GameplayAbilitySystemによるダッシュ(スプリント)を実装しようとしたとき、GameplayAbilityクラスを1つだけ使ってShiftキーのPress/Releaseの判定をするように実装するのがブループリンターには少し厄介だったためです。
 例えば、GameplayAbilityクラスを「歩行」と「走行」の2つのアビリティを用意して、インプットアクションによるイベントを使ってShiftキーを押したら「走行」アビリティを発動し、離したら「歩行」アビリティが発動するように仕組めば可能ではあるのですが、あまりスマートじゃないように思えます。

 GameplayAbilityごとにキー入力を関連付けるというのはかなり多用しそうな処理であるにも関わらず、紹介や解説の記事・動画はあまりないようでしたので、自分なりにまとめてみようと思います。

 誤っている部分やよろしくない記述が散見されるかもしれませんので、もしよろしければコメントやDMでご指摘いただけますと幸いです。

 今回の実装にあたって、GASShooterのコードの一部の他に、Twitterでのあいす氏(@koorinonaka)からの下記リプライを参考にいたしました。ご助言ありがとうございました。

GASの導入

 今回使用したのはUE5EAです。
 GASをプロジェクトに導入するにはC++を使った初期セットアップが必要ですが、これについては、他のブログ様などでもよくご紹介されている
おかわりはくまい氏によるセットアップ方法の紹介記事が非常に参考になります。
 当該記事についてはタイトルだけ下記しておきます。

・GameplayAbilitiesの使い方(セットアップ編) - おかわりのアンリアルなメモ

ダッシュの実装

前置き

 話を振り出しに戻しますが、なぜGameplayAbilityクラスを1つでダッシュを実装しようとすると面倒なのかについて少しだけ…。

 今回、肝要となるのが以下の、WaitInputPress/WaitInputRelease(特に後者)というキー入力のPress/Releaseの監視をしてくれるAbilityTaskになります。
f:id:raksul_01:20211226225749p:plain

 え、そいつら使えばすぐできるんじゃ…?

と思われるかもしれませんが、これらのAbilityTaskはすぐには使えません。
各GameplayAbilityをキー入力に紐づける必要があり、そのためにはプロジェクト設定のアクションマッピングだけでなく、C++を使って紐づける必要があるためです。

GameplayAbilityにキー入力を紐づける

 本記事では、おかわりはくまい氏によるセットアップ記事の通りにセットアップしたものを改変して実装していきます。

EnumでインプットIDを定義する

 定義する場所はどこが最適なのかわからないのでプロジェクトのヘッダーに定義しました。
 DisplayNameは適当でも大丈夫ですが、各アクション名はプロジェクトのアクションマッピングと同じ名前にしておきましょう。
 開発を進むにつれてインプットアクションを増やす場合はその都度、EAbilityInputIDも増やしていけば良いだけです。

プロジェクト名.h

#pragma once

#include "CoreMinimal.h"

UENUM(BlueprintType)
enum class EAbilityInputID : uint8
{
	// 0 None
	None				UMETA(DisplayName = "None"),
	// 1 Confirm
	Confirm				UMETA(DisplayName = "Confirm"),
	// 2 Cancel
	Cancel				UMETA(DisplayName = "Cancel"),
	// 3 Sprint
	Sprint				UMETA(DisplayName = "Sprint")
};

※今回は参考にしたGASShooterにConfirmやCancelといったInputIDが定義されていたので同様に追加しております。
これはおそらく、WaitForCancelInput / WaitForConfirmInputといったAbilityTaskで用いるものと思われますが、詳しく調べてないためここでは説明無しです。

GameplayAbilityクラスを拡張する

 BPでアビリティ毎のバインド先の変更が簡単にできるよう、MyGameplayAbilityクラスを作成します。

MyGameplayAbility.h

#pragma once

#include "CoreMinimal.h"
#include "プロジェクト名/プロジェクト名.h"
#include "Abilities/GameplayAbility.h"
#include "MyGameplayAbility.generated.h"

UCLASS()
class プロジェクト名_API UMyGameplayAbility : public UGameplayAbility
{
	GENERATED_BODY()
	
public:
	UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Ability")
		EAbilityInputID AbilityInputID = EAbilityInputID::None;
};
MyCharacterクラスを改変する

(変更点)
・インクルード対象にMyGameplayAbilityクラスおよびプロジェクトのヘッダーを追加する。
・AbilityListをMyGameplayAbilityクラスの配列にする。
・InputIDに対してキー入力をバインディングするためのBindASCInput()を宣言。

MyCharacter.h

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "プロジェクト名/プロジェクト名.h"
#include "AbilitySystemInterface.h"
#include "MyGameplayAbility.h"
#include "MyCharacter.generated.h"

UCLASS()
class プロジェクト名_API AMyCharacter : public ACharacter
{
	GENERATED_BODY()

・・・

public:
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Abilities, meta = (AllowPrivateAccess = "true"))
		class UAbilitySystemComponent* AbilitySystem;
	UAbilitySystemComponent* GetAbilitySystemComponent() const
	{
		return AbilitySystem;
	};

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Abilities)
		TArray<TSubclassOf<class UMyGameplayAbility>> AbilityList;
};

protected:
	void BindASCInput();

	bool bASCInputBound;

(変更点)
・InputIDを削除。
・MyGameplayAbilityクラスを継承した各アビリティのBPクラスで設定したAbilityInputID != 0であればその値をInputIDに、それ以外はInputIDに-1が割り当てられるように。
・BindASCInput()の処理を記述。

MyCharacter.cpp

・・・

void AMyCharacterBase::BeginPlay()
{
	Super::BeginPlay();
	if (AbilitySystem)
	{
		if (HasAuthority() && AbilityList.Num() > 0)
		{
			for (auto Ability : AbilityList)
			{
				if (Ability)
				{
					if (static_cast<int32>(Ability.GetDefaultObject()->AbilityInputID) != 0)
					{
						AbilitySystem->GiveAbility(FGameplayAbilitySpec(Ability.GetDefaultObject(), 1, static_cast<int32>(Ability.GetDefaultObject()->AbilityInputID), this));
					}
					else
					{
						AbilitySystem->GiveAbility(FGameplayAbilitySpec(Ability.GetDefaultObject(), 1, -1, this));
					}
				}
			}
		}
		AbilitySystem->InitAbilityActorInfo(this, this);
	}
}

void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);

・・・

    BindASCInput();
}

void AMyCharacter::BindASCInput()
{
	if (!bASCInputBound && IsValid(AbilitySystem) && IsValid(InputComponent))
	{
		AbilitySystem->BindAbilityActivationToInputComponent(InputComponent, FGameplayAbilityInputBinds(FString("ConfirmTarget"),
			FString("CancelTarget"), FString("EAbilityInputID"), static_cast<int32>(EAbilityInputID::Confirm), static_cast<int32>(EAbilityInputID::Cancel)));

		bASCInputBound = true;
	}
}

①GiveAbilityで渡すFGameplayAbilitySpecにInputIDを指定することで、各GameplayAbilityとInputIDとを紐づけます。
②BindASCInput()内のAbilitySystem->BindAbilityActivationToInputComponentによって、先ほどEnumで定義したInputIDに対応するキーがPress/Releaseされた際にインプットアクションが発行され、さらに①で紐づけられたInputIDに対応するGameplayAbilityがActivateされるようになっています。

 なお、AbilitySystem->BindAbilityActivationToInputComponentの実行タイミングはSetupPlayerInputComponentの後にしていますが、
 おそらくネットワーク対応ゲームの場合はOnRep_PlayerStateのときも含めて二回とすることが望ましいです(InputComponentが存在しない可能性があるため)。
※bASCInputBoundは二重にBindASCInput()が実行されないようにするための値なので、ネットワーク対応でない場合は不要。

ダッシュのGameplayAbilityクラスを作成する

 MyGameplayAbilityクラスをBP継承したGA_Sprintを作成します。
 作成したら、クラスのデフォルト設定にてAbilityInputIDをSprintにしておきます。
ちなみに、上記②のようにインプットアクションをGameplayAbilityにバインディングしているため、キャラクターのBP内で「インプットアクション○○」→「TryActivateBy~~」などを記述しておく必要はありません。
 つまり、ダッシュ以外のGameplayAbilityを追加した際は、
・EAbilityInputIDと、プロジェクトのアクションマッピングに同名のアクションを追加する。
・追加したアビリティBPの設定から、EAbilityInputIDを選択する。
これだけを行うだけで、キャラクターのBP内でアビリティ発動の処理を記述する必要がなくなるというわけです。

f:id:raksul_01:20220104044647p:plain

 中身は必要最低限で以下のような感じにしました。
Start/Stop Sprintingの中身はキャラクターのMovement Componentを取得してSetMaxWalkSpeedで移動速度を変更しているだけです。

f:id:raksul_01:20220104044924p:plain

完了

 うまくいけば、ダッシュのキーを押すとアビリティがActivateされてStart Sprintingによってスピードが上がり、キーを離すとWaitInputReleasedのOnReleaseから続くEndAbilityが実行され、Stop Sprintingによってスピードが下がって歩行に戻る といった挙動が実現しているはずです。

 無駄な部分やより良い方法があればぜひ教えていただけると幸いです。

「オーストラリアの自然」アセットで使われている道路マテリアルの中身を見てみる(1)

※この記事は、シェーダーに精通していない私の勉強のための備忘録みたいなものです。そのため、間違った部分がある場合がございます。

こんにちは、らくするです。

すっかりクリスマスシーズンですね。

今回は、永続無料コンテンツである「オーストラリアの自然」で使われている道路マテリアルの中身がどうなっているのか、ざっくりと見ていきたいと思います。

www.unrealengine.com

なぜ中身を見ようと思ったのかと言いますと、この道路、単純なマテリアルではなく地面と馴染ませるようにされていたり(馴染ませる部分の詳細についてはまた後日ですが…)、その他いろいろと工夫されている点が多い(らしい?)ので一度見てみたかったからです。

道路のメッシュについて

f:id:raksul_01:20211213105242p:plain

道路メッシュ

道路のメッシュは画像のように、おおよそ長さ3500cm,幅897cmとなっています。

緑色のフレームは単純コリジョンです。

f:id:raksul_01:20211213110240p:plain

ベースカラーテクスチャ

また、ベースカラーテクスチャは上のように4096*1024となっており、メッシュのUVはV座標0→1までぴっちり配置なので長さ方向に対して若干縮められますね。

マテリアルについて

それでは中身を見ていきます。全体は以下の画像の通りです。

f:id:raksul_01:20211213110644p:plain

道路マテリアル(全体)

これでは少しわかりづらいので、一旦下側の部分と最後のContactShadows部分を排除します。ちなみに排除した下側部分は、ランドスケープの地面マテリアルとより馴染ませるための部分、ContactShadowsは陰影の調整部分です(内部でワールドポジションオフセットとピクセル深度オフセットを調整している)。

f:id:raksul_01:20211213152208p:plain

今回見る部分以外を排除したマテリアル

大まかな内容は画像中にコメントで入れておきました。

以下、備考です。

・(ベースカラーについて)

 中央上のオーバーレイについてはみつまめ杏仁さんが以下のツイートで非常にわかりやすく図説されていますが、思った以上に計算コストが高そうです。

・(PDOについて)

 道路メッシュは略半円筒形状になっており、ピクセル深度オフセット(PDO)によって、メッシュのぶっ刺し感を低減しています。PDOが低いほどカメラ奥行方向の深度が深い(奥にある)と見做されるやつで、ディザ抜きと併用した馴染ませ用途なんかでもよく使われてます。

 このマテリアルではベースのPDOと詳細のPDOとを乗算していますが、実際には今回簡易化のために排除してしまった部分が馴染ませの肝となる部分なので、ここまでではたぶん見栄え…というかぶっ刺し感の低減には大きく影響はしません(大事なところが抜けていてごめんなさい)。

・(法線の合成について)

 上の画像中ではだいぶ割愛して書きましたが、法線の合成はおそらく慣用的な手法で計算しているものと思われます。合成計算について気になる方は是非調べてみてください。BlendAngleCorrectedNormalsなんかでもいけるのではないでしょうか。

 また、MF_ReconstructZ中で使われているDeriveNormalZは、二軸の成分から残りの成分を計算するためのものです。わかりやすさのために、FP_GunのノーマルマップからRG成分だけを抜き出して、法線の再構築(B成分の計算)を行いました。

f:id:raksul_01:20211213153325p:plain

DeriveNormalZによる法線の再構築

 このように、法線情報は2軸成分があれば残り1軸の成分は計算により求められるので、通常のノーマルマップのBチャンネルに別の情報を入れることもできます。これはメモリ節約術として時々見かけることがあります。圧縮形式などなどが変わってくるのでテクスチャの品質に影響したりするかも…。

 

ストアのコンテンツは本当に教科書ですね。

次回、今回排除した部分について見ていけたらと思います。

第16回UE4ぷちコン振り返り その③~カーソルを筆に変更する(筆先の動きもつける)~

ぷちコンの審査結果発表会も終わりましたが、ありがたいことに賞をいただくことができたのでこのまま逃亡しようとしていた(嘘です)振り返り記事を再開します。

今回紹介するものはこちら↓

f:id:raksul_01:20211005011523g:plain

概要

前回の記事で紹介させていただいたペイントシステムでは、カーソルがそのままデフォルトのものなのでそれを自前の筆に置き換えよう!っていう内容です。

また、上の動画をご覧の通り、筆の移動に合わせて筆先も動いていますね。

単純にカーソルを画像に置き換えるだけなら

Set Mouse Cursorを使う方法

・UIに配置したimageをTickでマウス位置に追従させる方法

が一般的だと思いますが、これらの方法では筆先の動きをつけることができません。やっていることはかなり後者の方法に近いですが、少し工夫して実装することにしました。

要約

実装方法を簡単にまとめると

・筆スケルタルメッシュをシーンキャプチャーでテクスチャに描画(常に更新)し、そのテクスチャをUIのimageに設定

・筆スケルタルメッシュのアニメーションBPにマウスの移動量を与える

です。

他にも、筆スケルタルメッシュを直接カメラに映し出すといった方法もあるかもしれませんが個人的には上記の方法のほうが手っ取り早いと感じたのでこちらを採用しました(ほかにも理由はありますが後述します)。

実装方法

スケルタルメッシュを用意する(blender

今回は、グレイマン人体改造ロボット改造したものを筆としました。

※意外にも審査員の方たちは初見で気づかなかったようですが…。その①の記事でも述べた通り、当初作成予定だったウケ狙いゲームの名残ですw

blenderを使って、インポートしたグレイマンのデフォルトのスケルトンを削除し、引き延ばした頭に新しいスケルトンを作成した至極雑な作りです。

f:id:raksul_01:20211005001656p:plain

これにSubstance Painterでペペっとテクスチャを作りました。

アニメーションBPを用意する

筆スケルタルメッシュができたら、UE4にインポートします。

筆スケルタルメッシュのスケルトンをベースにアニメーションBPを新規に作成し、AnimGraph内でCCDIKを使用して筆先がいい感じに動くようにしました。

f:id:raksul_01:20211005004357g:plain

アニメーションBPは以下のように組んでいます。

f:id:raksul_01:20211005004534p:plain

後述するキャンバスウィジェット(前回記事のWB_TestCanvas)からTarget Effector Location X/Yに値が代入され、実際のCCDIKにはTarget Effector Location X/YにFInterp Toで徐々に値が近づいていくEffector Location X/Yが入力として与えられています。

※BPにいっさい処理を組まず、WB_TestCanvasから直接Effector Location X/Yに値を代入しても大丈夫ですが筆先の動きが固くなってしまいます。

筆アクターとシーンキャプチャーをレベルに配置する

Actorクラスを継承したBP_GrayBrushを新規作成し、Skeletal Mesh Component(筆スケルタルメッシュ)を追加し、上記で作成したアニメーションBPを設定しておきます。

このBP_GrayBrushをレベルに配置し、さらにScene Capture 2Dがうまい具合にBP_GrayBrushをテクスチャ内に収めるように配置します。

※応募作品ではサブレベルに配置しました。

f:id:raksul_01:20211005010135p:plain

Scene Capture 2Dの設定

ここで、投影方法は平行投影にしました。なぜ今回の実装方法を採用したのかという理由の2つ目(要約のところでほかの理由と述べたやつです)は、筆の投影を平行投影にしたかったためです。

→この記事を書いている途中で透視投影に変更してみたらそっちのほうがいい感じになりましたw

上手くできるとこんな感じでキャプチャしてくれます。テクスチャレンダーターゲットTRT2D_Brushのサイズは480*480にしました。

f:id:raksul_01:20211005010614p:plain

ここまでできたら、新規マテリアルM_TRT2D_Brushを作成します。

TRT2D_Brushのままでは筆の部分だけがアルファの値が0という情報を持っているので、反転させます。

f:id:raksul_01:20211005012716p:plain

キャンバスウィジェットに追加処理を組む

最後に、前回記事で作成したWB_TestCanvasを若干変更していきます。

まず、ウィジェットに新規のイメージ(BrushのImageにM_TRT2D_Brushを設定)を追加します。

f:id:raksul_01:20211005012136p:plain

上の画像のようにAlignmentなども調整しておきます。

また、テクスチャサイズが先ほど述べた通り480*480に対して、ウィジェット上では600*600となっていますが、解像度より負荷軽減を優先しました。

で、前回記事にてオーバーライド処理を施した関数On Mouse Moveの途中に以下の処理を追加します。

f:id:raksul_01:20211005175535p:plain

マウスの移動量を筆スケルタルメッシュのアニメーションBPに与える前にノーマライズして値をベクトルの大きさが1になるように抑えています。2.0を乗算しているのは調整用です。

あ、それとコンストラクトイベントなどで筆アクターのAnim Instanceを取得しておくこともお忘れなく

他にも細かい処理(例えば、描いてる時より描いてない時のほうが筆の位置が高くしたり、音を鳴らしたり)を色々追加してますが記事の本題から逸れるので説明は省略します。

→完成!

最後に

実装方法は簡単ですが、私は思いつくのにだいぶ時間がかかってしまいました…。

しかしペイント中に筆が現れるようにするだけで、クオリティや描き心地がずいぶん上がったように感じたのでやりがいがありました!

次回の記事その④(ラスト)では「描いた線にそって道を生成する方法」について紹介します。

→力尽きて断念しました。。

 

 

第16回UE4ぷちコン振り返り その②~画面上にペイントする~

f:id:raksul_01:20210921175136p:plain

今回は、第16回UE4ぷちコンで実装した画面上へのペイントシステムをどのように実装したかなどを紹介したいと思います。

 

★目次★

 

概要

作るもののイメージはこちら↓

マウスの左ボタンを押している間は黒いインクが塗れるようなものになっています。

さて、やり方としてはUE4を触っている方であれば

TextureRenderTarget2D(以下、TRT2D)を用意し、ウィジェットに描画。マウス座標に応じてDraw TextureあるいはDraw Materialを極短い間隔(Tickなど)で使用することによってTRT2Dに対して疑似的にペイント。

といった方法が通常は思いつくかなと思います。

しかし、今回は少し違ったアプローチで実装してみました。というのも、上記の動画をご覧の通り、インクが滲んで広がるような表現をしたかったためです(大神の筆しらべもよく見ると広がっていますね)。

※上述したような通常の方法で、DrawTextureを用いて同じ座標にサイズを徐々に上げたテクスチャを描画するような手法を使ってみたところ、TRT2Dの更新頻度が上がって負荷が半端なくなりました(当然そうなるのはわかっていたことですが)。

また、以下の前回記事で紹介した、大神の筆しらべをUnityで再現したものUE4で言うTRT2Dのようなものを使用しており、インクが広がるような表現までは再現していませんでした

要約

使用したUE4のバージョンは4.26となります。

今回のアプローチを簡単にまとめると、

  • ウィジェット上にNiagaraエミッタをマウス座標にフレーム毎に更新
  • 当該Niagaraエミッタからスプライトパーティクル(TRT2Dを使用するやり方で描画するTextureの代わり)をスポーン

という手法になります。

Niagaraパーティクルをウィジェットに描画するにあたって、下記のプラグインを使用しました。無料です。

 使い方はプラグイン制作者様のチュートリアルを見た方が早いので、本記事では割愛します。

実装方法

ナイアガラシステムの準備

それでは実装方法に入りますが、UE4(特にウィジェットブループリント)の基礎知識があるものとして細かいところは省略しつつ進めます。

本記事の真似をするだけではできないかもしれませんのでご容赦ください。また、名付けは適当なので任意でお願いします。

まずは、ナイアガラシステムNS_Brushを作成します。エミッタとしてSimpleSpriteBurstを追加しておくとやりやすいと思います。

f:id:raksul_01:20210921185717p:plain

今回、最低限下記のエミッタ設定を行いました。

  • Emitter StateのLife Cycle ModeをSelf, Loop BehaviorをInfinite
    →パーティクルのスポーンが止まらないように
  • Particle StateのKill Particles When Lifetime Has Elapsedのチェックを外す
    →パーティクルが消えないように
  • Spawn Rateを750以上
  • パーティクル更新にScale Sprite Sizeを追加して、1秒で1.2倍くらいのサイズになるようScale Vector 2DBy Curveを設定

あとはスプライトに適用するマテリアルですが、以下のような適当なものを用意しました。

f:id:raksul_01:20210921191908p:plain

ナイアガラエディタでのプレビュー用マテリアル(M_BrushParticle)

これは、あくまでもナイアガラエディタ上でプレビューするためのマテリアルです。

使用しているプラグインNiagara UI Rendererの都合上、マテリアルドメインUser Interfaceにしたマテリアルも別途用意しておいてください。

結局、応募作品もこの雑なマテリアルのままにしてしまいました。

f:id:raksul_01:20210921192015p:plain
後述するNiagara System Widgetで使うマテリアル(M_BrushParticleUI)

ウィジェットの準備

キャンバス用に新規ウィジェットWB_TestCanvasを作成し、パレットからNiagara System Widgetを追加します(NS_BrushInkと名付けました)。

また、Borderを最上層に追加します(試したところ、これがないと後のOn Mouse Button Downなどが動作しないみたいです)。

f:id:raksul_01:20210921184916p:plain
f:id:raksul_01:20210921210629p:plain

そして、NS_BrushInkを選択した状態で詳細タブから以下のように設定します(マテリアルの名前が誤字ってますが気にしないでください)。

f:id:raksul_01:20210921192728p:plain

Material Remap Listはナイアガラエディタでスプライトに適用していたM_BrushParticle(左)をウィジェットへの描画用にM_BrushParticleUI(右)に置換する設定です。

また、Auto Activateのチェックを外さないとマウスのボタンを押してなくても常に描かれてしまいますので外しておきます。

次に、On Mouse Button Down関数をオーバーライドして以下のようにノードを組みます。

f:id:raksul_01:20210921193231p:plain

変数PointsVector 2Dの配列で、今回は使いませんが後に「描いた線に沿った道の生成」の紹介記事で使うことになります。

ちなみにこの処理は、左ボタンを押したポイントのみにインクを塗るためのものであり、このまま連続して線を描くときには次に説明する処理が必要となります。

この処理のために、次はOn Mouse Move関数をオーバーライドして以下のようにノードを組みます。

f:id:raksul_01:20210921194005p:plain

始めの部分でエミッタの位置をカーソル位置に更新しています。

f:id:raksul_01:20210921194140p:plain

マウス位置にエミッタ位置を更新

その後のブランチ処理は、左ボタン押下状態かつマウスが一定距離移動した場合にのみナイアガラシステムをアクティベートするものです。これがないとエミッタの設定上、カーソルを動かさなくても同じ場所に永遠にパーティクルがスポーンされ続けてしまいPCが爆発します

f:id:raksul_01:20210921194502p:plain
マウスが一定距離移動したらナイアガラシステムをアクティベート

最後の処理(拡大画像無し)は、先ほどと同様、Pointsにアイテムを追加するものです。

ポーンの用意

適当なポーンを用意して、インプットアクションやキー入力でWB_TestCanvasをビューポートに表示する処理を追加します。

また、必要に応じてSet Show Mouse CursorやSet Input Mode Game And UIなども入れる必要もあります。

f:id:raksul_01:20210921211429p:plain

※上記のようなFlip Flopの使用はバグの元となりやすいため推奨しません。真面目に作るならbool値を使いましょう。

---セットアップ完了!---

上手くいけば、Hキーを押せばペイントできるようになり、再びHキーを押すことでキャンバスが排除されるようなシステムができているはずです。

最後に

  • 実際の応募作品ではBPC_MagicBrushというコンポーネントウィジェット作成だとかスペシャルスキルだとかほとんどの機能を入れて、どのポーンに対してもペイント&道生成&スキルを簡単に実装できるようにしていました。
    f:id:raksul_01:20210921212107p:plain
    絵筆の機能をまとめてコンポーネント化したもの

以上、間違いなどがあればDMやコメント等で教えてください!

第16回UE4ぷちコン振り返り その①~構想について~

らくする (@raksul_01)です。今回がブログ初投稿となります。

今回は、株式会社ヒストリア様主催の

「第16回UE4ぷちコン(https://historia.co.jp/ue4petitcon16)」に参加したので、

開設しっぱなしだった本ブログの重い腰を上げて、その振り返りについて書いていきたいと思います(-。-)y-゜゜゜

ちなみにぷちコン参加は今回で3回目でした。

目次

まず初めに

今回のぷちコンのテーマは「みち」

「みち」に通ずる何らかの要素(道でも未知でも)が入っているゲームであれば基本的には何でもOKというルールになっています。

いやあ、このテーマは苦戦しました。前回の「かわる」というテーマが如何に何でもできてしまうテーマであったかを痛感しました。

着想について

テーマが発表されたときに漠然と考えていたアイデア

  • 筆でを自分で描く2~2.5Dのゲーム
  • MetaSoundとNiagaraを組み合わせて音のを見立てたオーディオビジュアライザー的なもの
  • 未知の筆を持った伝説の書ミッチーがひたすらを爆走する筆アクションFPS、名付けて「Shodo-ka Simulator

です。マインドマップの作成などは一切行っていません(^O^)

そして実際にこれらをすべて途中まで作っては、すべてボツとなりました(この時点で約2週間を浪費。MetaSoundの勉強にはなりましたが)。

 

で、結局何を作ったの?の前に・・・

上述の漠然としたアイデアをご覧の通り、当初から謎に「」というものに頭が捉われていた私は、以下のような動画を見つけました。

www.youtube.com

そう、「大神」です。超有名タイトルですね。私は未プレイですが(ん?)

上の動画は、その大神における筆しらべをUnityで再現したものです。

 

私はこの動画に衝撃を受けました。

・・・いったいぜんたいWhat happens?

これはUnityを使用しているけど、UE4でも何とかして似たようなものを作ってみたい!そして真似するのはお絵描きの見た目部分だけに留めて、それを活かして今回のテーマに沿ったゲームを作りたい!と・・・

 

こうして、”筆しらべの見た目をUE4で似せてみる”という険しい道のりへ進む決意をしたのでした。

応募作品について

長々とお話しましたが、今回の応募作品はこちら↓

youtu.be

個人的には概ね満足いく結果が得られたと感じます。

※完全な真似ではなく、ゲーム内容に合致する見た目に調整しています。

 

裏話的なあれですが本当はストーリー構想もあって、ムービーシーンなども作成していました。

ですが、作っているうちに応募要件である動画時間や、提出期限を考えると適切でないと考えて途中で路線変更して、レベル(ページ)選択形式のパズルゲームにしようという流れになりました。

そうすれば、基本システムさえできてしまえばそのあとはギミック追加などでボリュームを補えますし(;'∀')

必要タスクの抽出

一番重要である、筆しらべの見た目再現を実行するにあたり、大神の動画を見ながら細かいタスクを書き出しました。

  1. 筆しらべ突入時に画面が停止し、ポストプロセス処理&紙面が斜めになる
    →作品の仕様上、停止&斜めにならないほうが良かったため除外
  2. 描いた部分から徐々に墨汁が幅方向に広がる(滲む)
    実現
  3. 描いている途中、墨汁がかすれる
    →おそらく私の作ったものにも実装可能と考えるが、時間の都合により断念
  4. 描く動きに合わせて筆先が動く、また、柄の部分が傾く
    →筆先の動きは実現
    柄が傾くこと完全に忘れていた・・・

他にも色々あるとは思いますが、このようにして、プレイヤーが描くときの感覚が気持ちよくなるための、様々な工夫がなされていると感じました。

そして、これら一つ一つが私の頭を悩ませるものとなったのです。

なんせ調べて出てくるようなものではなかったので(いくら調べても一切のヒントも得られませんでした_( _´ω`)_)。

 

なお、今回の作品の「インクで線を描く方法」「描いた線に沿って道を生成する方法」「筆先に動きをつける方法」の実装の仕方については以降の記事で紹介予定ですので、お楽しみに。

その他・感想

最後に、意識した点、良かった点/反省点などを挙げていきたいと思います。

  • ある意味、見た目再現(どうやってるのかな?という部分を自分なりに考えて再現してみること)が一番学習したかったことだったので、時間の都合上不足した点もあるがほぼ満足。
  • リセット機能も用意していた。
    非効率なOpen Levelで再読み込みする形式ではなく、GameModeに用意されたReset Level()などを使用するリセット。この場合、アクタごとにリセット時のイベントを用意する必要があり、面倒だった。
    面倒だった割に、動画では紹介できていない。
  • これまた動画では紹介できていないが、特定範囲の外に出ようとすると「絵本の外には出られないよ!」というメッセージ表示も用意してあった。
  • レイマンの形をした筆は「Shodo-ka Simulator」という当初作りかけていたバカゲーの名残だったりする。
  • 実は見た目再現と道生成を実装した後、ゲームにどう落とし込むかで相当苦労した。プロシージャル生成マップをひたすら突き進むゲームにするのか、単発ステージを複数用意したパズルゲームにするのか、など。
  • 絵本の世界を題材としており、ドローモード時のアウトライン描画とのギャップが激しく生じるようにしたかったので、背景グラフィックはあえてリアリスティックなものに
  • 上記に時間をかけたので、UI等に関してはクオリティを損なわない程度のものに。ただし、ちょっと雑すぎたかも吹き出しは頑張った。
  • ゲームってユーザビリティがすごい大事だと思ってるので、ぷちコンと言えどもチュートリアルを丁寧にすることを心がけた
  • 今回もめちゃめちゃに学びが多かった。開催ありがとうございました。