じゃっくぽっとラボ

UE4の技術ログあつまれ~

GASのデザインパターン まとめ

概要

本記事はUnreal Engine (UE) Advent Calendar 2021 24日目の記事になります。

qiita.com

GameplayAbilitySystem(以下GAS)はプレイヤーに紐付いた大量のアクションの実行可否を簡単に設定できたり、エフェクトやサウンドなどのアセット参照を抑えて設計できるなど沢山のメリットがあります。
とはいえ、いざ自分のプロジェクトに使おうとしたとき使いこなせるかは別。
そこで、自分が普段から使っているデザインパターンをまとめました。

※この記事には目次が2種類あります。用途に合わせてお選びください。

※本記事ではGameplayAbilityのみについて取り上げます。GameplayAttributeやGameplayEffectなどについては触れません。

目次

デザインカタログ

実例で記事を追いたい方はこちらからどうぞ。対応したロジックの場所へ飛びます。

GASの導入【初心者向け】

今回は実例を載せるのが趣旨なので、導入は他のサイトを参考にして下さい。 自分が導入の際に参考にした記事を羅列します。
okawari-hakumai.hatenablog.com

wvigler.hatenablog.com

後々GASを自分で拡張する時に見直すことが多いので今でも重宝してます。

最終的に下記の状態になったことを前提で進めます。

  • キャラクターにGASコンポーネントが実装されている
  • キャラクターにGameplayAbilityクラスの配列がある。
  • Try Activate GameplayAbility By Tagを使うことが出来る。 f:id:JackpotDevelop:20211223123721p:plain

デザインパターン解説

アビリティ起動時のGameplayTagを処理判定に使う

使用例:ダメージリアクション、被ダメージエフェクト切り替え

通常、アビリティが実行されるとActivateAbilityイベントが実行されます。
このイベントに似たActivate Ability From Event というものがあります。
これの良いところは、GameplayEventDataという構造体データをアビリティ起動元からアビリティクラスへ渡せるんですね。

参考: 第15回ぷちコン「たぬ吉の大冒険」振り返りその1<AbilitySystemComponent、GameplayAbility、GameplayTag編> - そうだ、ゲームを作ろう

このイベントを使うことで、アビリティの起動に使われたGameplayTagを利用して処理を切り替えることが出来ます。

例:ダメージリアクション

アクションゲームでは攻撃されてダメージを受けた時、そのダメージの威力によって吹っ飛んだり少しよろけたりします。
このリアクションをひとつのアビリティで実装します。

GameplayTagの作成

攻撃された時、受けたダメージの大きさ(大・小)をGameplayTagで判定します。
新しいタグ Damage.Hit、さらにHitの下の階層にHeavy, Smallと2つのタグを作ります。
エディタのツールバー[設定]→[プロジェクト設定]、[プロジェクト]の中の[GameplayTags]からタグを作成できます。

ダメージの大きさ タグ
Damage.Hit.Heavy
Damage.Hit.Light

f:id:JackpotDevelop:20211223124347p:plain

GA_DamageHitの作成

新しいアビリティ GA_DamgeHitを作成。
f:id:JackpotDevelop:20211223124735p:plain

作成したGAクラスの詳細欄、AbilityTagsにはDamage.Hitを指定します。
起動条件に親階層のHitを設定しておけば、Hitの下の階層にあるHeavyとLightどちらが指定されても起動するアビリティとなります。

f:id:JackpotDevelop:20211223124850p:plain

さらに詳細欄の下の方にある[Triggers]の項目に設定を追加します。
AbilityTriggersの+マークを押して配列を一つ増やし、またDamage.Hitタグを追加します。 f:id:JackpotDevelop:20211223125512p:plain

ここまで行うことで、Send Gameplay Event to Actor ノードでGameplayTagを指定された時に起動するアビリティとなります。 またGAクラスの中身は何も書いていません。ダメージを与える側

次にノードの実装です。
ActivateAbility From Eventからパラメータを取得し、
プレイヤーがどんな種類の攻撃をしたのかこの構造体から取得します。
取得したタグをSwitchノードで分岐します。
f:id:JackpotDevelop:20211223130250j:plain

f:id:JackpotDevelop:20211223130444p:plain

あとで再生するアニメーションモンタージュの変数を作り、分岐先でそれぞれ指定します。
これでダメージを受けた際のアニメーションが切り替わるようになりました。
※Switchノードの前にSequenceノードを追加しました。 f:id:JackpotDevelop:20211223133305p:plain

テスト

新しいBPクラス BP_DamageHitTest アクタを作成します。
f:id:JackpotDevelop:20211223133905j:plain

StaticMeshコンポーネントとHitイベントのみのシンプルなアクタです。
こいつにプレイヤーがヒットしたらダメージリアクションを取るようにしてみます。
StaticMeshは何でもよいです。自分は1MCubeを選びました。 DoOnceノードは、ヒットしたときに何度も実行されるのを防止するために挟んでいます。

  • Damage.Hit.Light と Damage.Hit.Heavy の2つを用意しました。
  • それと、アニメーションモンタージュはAnimBPにスロットノードを追加しておかないと再生されません。ですので、忘れず追加しておきましょう。
  • ThirdPersonCharacterのBPに作成したGA_DamageHitを追加します。

f:id:JackpotDevelop:20211223134659j:plain

f:id:JackpotDevelop:20211223134940p:plain

f:id:JackpotDevelop:20211223135106p:plain

結果

youtu.be

無事ダメージリアクションの切替が出来ました。
今回は2種類のダメージで作成しましたが、Damage.Hit.Electroとか Damage.Hit.Fireなどの属性攻撃も追加出来ます。モンタージュだけでなく受けた攻撃のエフェクトも差し替えることも可能です。
リアクションを増やしてもSwitchノードで分岐を作るだけですからGAクラスはひとつで済みますね。

さらに応用としては、このアビリティを親にした子クラスを作成して、プレイヤー用、敵A用、敵B用とアニメーションを用意して、モンタージュの変数に入れることで使い回すこともできます。

WaitGameplayEventノードを使う

使用例:ジャストガード、ノックバック終了判定
アビリティ起動後に外部からイベントを受け取ったタイミングで処理を実行することが出来ます。
「能力実行中に攻撃を受けたら、この処理を実行」「起動後この状態になったら終了」といったロジックが組めます。

例:ジャストガード

敵から攻撃を受けた際にプレイヤーがタイミングよくガードをしていれば、ジャストガード判定となりダメージを無効化できるという能力です。
厳密にいえばアニメーションモンタージュの特定のフレームの間に攻撃を受ければ発動というものですが、シンプルに説明するためにモンタージュ中全てをジャストガード判定フレームとします。

Gameplayタグの作成

敵の攻撃、プレイヤーが行うガード、ジャストガードされた敵のリアクション。
それぞれGameplayタグにします。

説明 タグ
敵の攻撃 Action.Melee
敵の攻撃ヒットで送るタグ Damage.Hit.Melee
プレイヤーのガード Action.Guard
敵のリアクション Action.Parryed

GA_Guard、GA_Meleeの作成

GA_Guard
AbilityTagsとActivationOwnedTagsの2つにAction.Guardを指定します。
AbilityTagsはプレイヤーからこのアビリティを呼び出すために、
ActibationOwnedTagsはアビリティ実行中、オーナーであるプレイヤーにこのタグを付与させるために指定します。
敵の武器が当たった時このタグが付与されているのであれば、ダメージは与えられずガードします。
f:id:JackpotDevelop:20211223183846p:plain
ガードのモンタージュを再生した後「Wait Gameplay Event」ノードをつなぎます。
Event Received からSend Gameplay Event to Actor ノードで攻撃した相手に「ガードしましたよ」というイベントを送ってあげます。
相手の情報はWait Gameplay Eventの payloadからGameplayEventDataから取り出します。
といってもまだ相手側の処理を書いていませんので現時点では何もデータが入っていません。
f:id:JackpotDevelop:20211223161423p:plain

GA_Melee
攻撃する敵のアビリティです。
攻撃のモンタージュを再生後、弾かれたイベントを待つための処理を書きます。
ジャストガード判定のためのBool変数 Parryedを作成しています。ジャストガードが確定した時点で最初のモンタージュ終了がアビリティ終了のフラグにならないようにしています。 f:id:JackpotDevelop:20211223163900p:plain

モンタージュを再生しただけではプレイヤーに攻撃がヒットしませんので、敵キャラクターに武器としてStaticMeshコンポーネントを追加しました。
このメッシュがプレイヤーにオーバーラップしたとき、SendGameplayEventtoActorノードでDamage.Hit.Meleeタグを送っています。
同時にPayloadには自分の情報をInstigatorとして入れています。 f:id:JackpotDevelop:20211223183716p:plain

忘れる前にプレイヤーのBPにアクタタグ「Player」を追加します。 f:id:JackpotDevelop:20211223165331p:plain

テスト

プレイヤー側にガードアビリティ実行のための処理を書きます。
また、作成したアビリティをプレイヤーのBPに追加します。
f:id:JackpotDevelop:20211223185401p:plain f:id:JackpotDevelop:20211223193106p:plain

レベルBPに「繰り返し敵がアビリティを実行する処理」を書きます。 f:id:JackpotDevelop:20211223185517p:plain

結果

youtu.be

通常時は攻撃されるとダメージリアクションを取り、ガードするアビリティ実行中に攻撃されると相手がのけぞります。
Wait Gameplay Event の他の使い道としてはダメージを受けて吹き飛ばされた時に地面に着地したタイミングでアビリティを終了するようなことに使うことが出来ます。

状態判定タグを使ってアビリティの実行可否を切り替える

使用例:状況でアクション切替|コンボ攻撃

例:状況でアクション切替

「状況によって違うアビリティを呼びたい」ときのロジックです。 例としてジャンプで説明します。
下記2つの状況でジャンプボタンが押されたとき

  • 地面に足がついている時→通常ジャンプ
  • 崖にぶら下がっている時→崖上に登る

というように、同じボタンでも違うアクションにしたい場合があります。
GAクラスはGameplayTagを使った起動許可・不可、キャンセルといったフィルタリング機能があります。
コレを利用して、状況判定用のGameplayTagを作ってアクションの切り替えを行います。

GameplayTagの作成

GameplayTagとアビリティの関係をそれぞれ表にすると下記のようになります。

アビリティ AbilityTags(起動タグ) Activation Blocked Tags(起動不可タグ) Activation Required Tags(起動許可タグ)
GA_Jump Action.Jump State.Hang なし
GA_Hang State.Hang なし なし
GA_JumpHang Action.Jump なし State.Hang

GA_Jump、GA_Hang、GA_Climbの作成

ジャンプをGAS風に書き換える C++プロジェクトを作成すると、ThirdPersonCharacterはC++で書かれた処理の中でスペースバーとジャンプの処理をしています。
そこでBPでJumpインプットアクションノードを出してPressedでGA_Jumpが呼ばれるように書き換えます。
f:id:JackpotDevelop:20211223194714p:plain

GA_Jump
GAクラスの中でJump関数を呼ぶのはなんだか冗長な気がしますが、アビリティのフィルタリングをするためには仕方ありません。
そして詳細欄のタグ設定ではActibation Blocked TagsにState.Hangを追加しました。
このタグが付与されている状況ではGA_Jump(通常ジャンプ)はしないようにするためです。 f:id:JackpotDevelop:20211223194801p:plain f:id:JackpotDevelop:20211223194819p:plain

GA_Hangの作成
崖につかまるアビリティに加え、掴めるポイントであるBP_HangPointを作成します。
ジャンプ中にプレイヤーとBP_HangPointがオーバーラップしたら、掴まり状態に移行することにします。
流れとしては下記のようになります。

  1. スペースバーを押してジャンプする
  2. BP_HangPointにプレイヤーがオーバーラップしてGA_Hangが起動
  3. その状態でスペースバーを押して崖を登る

BP_HangPoint
BP_HangPointは二つのStaticMeshで構成されています。
ひとつは崖の端に配置するためのメッシュ(黄色)。
もうひとつはプレイヤーが掴む状態になったとき、来て欲しい位置を表すメッシュ。
これの向きと位置にプレイヤーを調整するので、コンポーネントは壁の方を向けておきます(画像で赤いX軸になっている方です)。 f:id:JackpotDevelop:20211223214119p:plain

位置調整用メッシュにプレイヤーがオーバーラップすると、State.Hangのタグをプレイヤーへ送ります。
また、滑らかに位置と向きが調整されるようMoveCharacterHoldPosイベントを作成しました。

f:id:JackpotDevelop:20211223214545p:plain

MoveCharacterHoldPosイベント f:id:JackpotDevelop:20211223214728p:plain

GA_Hang f:id:JackpotDevelop:20211223214836p:plain

ぶら下がりの姿勢を繰り返すアニメーションモンタージュを再生。
掴むポイントへ位置を調整された後、引力で落ちてしまわないようにCharacterMovementのMovementModeをFlyingにします。
ジャンプの慣性を消すためVelocityを0にします。

タグの設定はAbilityTagsと Activation OnwnedTagsの2か所。 この設定でGA_Hangが起動するとState.Hangがプレイヤーに付与されます。
f:id:JackpotDevelop:20211223220323p:plain

GA_Climb State.Hangタグが付いている状態でジャンプを押すと崖を登ります。
登るモンタージュを再生するのと同時に、キャラクターのコリジョンを一時的に無効にします。
登るモーションは崖とプレイヤーがかなり近づくので当たり判定が邪魔になるためです。
アビリティ終了後は、変更していたパラメータをもとに戻します。

f:id:JackpotDevelop:20211223220638p:plain

タグの設定は下記のとおりです。
GA_Hangを終了させるためにCancelAbilityWithTagにState.Hangを設定。
また掴み中のみこのアビリティが起動するようにActivation Required Tags にも設定しています。

f:id:JackpotDevelop:20211223222218p:plain

テスト

結果

youtu.be

同じボタンで違うアクションを呼び出すことが出来ました。
アビリティの起動フィルタリングはコンボ攻撃にも使用できます。
株式会社ヒストリア様の記事をご参照ください。

historia.co.jp

補足
前項目のダメージリアクションのように「GA_Jumpというアビリティ1つの中で通常ジャンプと崖上に登るのを切り替える」という作り方でも出来ます。ただ、「地面が泥沼だったらジャンプ力を下げたい」「崖の途中だったら上に向かって飛びついて欲しい」といった仕様が追加される可能性が考えられます。
その時、アビリティがひとつだと玉石混交のクラスとなるため望ましくありません。
「この機能は別アビリティで作るべきか」 という判断は最初のうちは難しいですが、やり方だけでも知っておくとデザインの引き出しが増えて出来るようになります。

まとめ

「初心者のための導入の方法」とか「GASとは?」みたいな記事は数あれど、実例がまだまだ少ないなと思ったので、ロジックがバンバン出てくる記事を書きました。
素早く実装する力を上げるには、例を見て引き出しを増やすのが手っ取り早いものです。
この記事を見て実装のヒントになれば幸いです。

「デフォルトプロパティ警告とエラー」を直す

f:id:JackpotDevelop:20210419004246j:plain
デフォルトプロパティ警告とエラー

なんか起動するたびエラー出るなぁとずっと思って放置してたけど、解決したのでメモ。

検索したら日本語の記事は出てこず、AnswerHUBがヒットしました。ほぼ下記のページの解説になります。
answers.unrealengine.com

まずエラー内容

Error:CDO Constructor("プロジェクト名"GameMode):Failed to find /Game/ThirdPersonCPP/Blueprints/ThirdPersonCharacter.ThirdPersonCharacter_C

GameModeで指定されてるクラスが見つかりませんでした~と言っています。

直し方

このエラーはプロジェクト作成時にC++を選択した場合発生すると思われます。 プロジェクトを作成すると、自動で「”プロジェクト名”GameMode.cpp」が作成されます。
このcppファイルを見てみると、

f:id:JackpotDevelop:20210419005026j:plain
GameMode.cpp

10行目で/Game/ThirdPersonCPP/Blueprints/ThirdPersonCharacterと指定されてる部分がありますね。

この場所にあったクラスを移動させたりリネームさせたりしていると今回のエラーが出ます

今回、再現のためにThirdPersonCharacterを削除していたので、新しくCharacter型でクラスを作成し同じ名前にしました。

f:id:JackpotDevelop:20210419005531j:plain
再作成したThirdPersonCharacter

これで起動時にエラーが出なくなりました。

ボス前カットシーンを作る(2/2)シーケンサー編

前回の記事の続きです。
主にマスターシーケンサーの使い方となります。
長いです。

最終的にこうなります。

参考記事
[UE4] Sequencerでカットシーン制作(4) Blueprintからの再生制御|株式会社ヒストリア
【連載】第4回:UE4のシーケンサーを使用した映像制作~ワークフロー編~ - Unreal Engine

Boss_Cutscene

ムービーなので、ほぼシーケンサーの使い方になります。  

シネマティクスから、マスターシーケンスを作成

f:id:JackpotDevelop:20210413233415j:plain
マスターシーケンスはここから作るんや
レベルシーケンスとの違いは、複数のレベルシーケンスを組み合わせてムービーを作りたいときはマスターを使うという感じです。

作成するとウインドウが出てきます。

f:id:JackpotDevelop:20210414001935j:plain:w300
マスターシーケンスのウインドウ
名前だけ「Boss_Cut」に変更して他はデフォルトです。後から変更できるので。
デフォルトだとShotというものが5つほど作成され、シーケンサータブが作成されます。
f:id:JackpotDevelop:20210414002812j:plain
マスターシーケンスの中に5つのShotが入ってます
このShotをクリックするとそれぞれにカメラが入っているので、このカメラを位置やズーム等を調整して、どこから撮って、どれくらいの長さそのカメラにするのか決めることが出来ます。
ざっくりいうと、マスターシーケンスを作ると、デフォルトでレベルシーケンスがいくつか入ったセットが出来上がるという感じです。  

ムービーが自動で開始されるように設定

シーケンスを作成すると、レベル上にシーケンサーのアイコンが出現します。
これを選択して詳細パネルを見ます。

f:id:JackpotDevelop:20210414201411j:plain:w300
シーケンサの設定
画像の通り、4か所チェックを打っておきます。 レベルの開始とともにシーケンサが自動で再生。プレイヤーは操作できないようにします。

キャラクターをアニメーションさせる

やり方としては2つあります。

DCCツールを使って、あらかじめすべて作ってしまう方法

フローについてはこちらの記事が参考になりました。

www.unrealengine.com

地形に合わせてアクションさせたり、そのカットだけで使うアニメーションを使う場合はこの方法が強いです。 作ったアニメーションを原点に置いて、再生するだけですからね。  

アニメーションBPのスロットをシーケンサー上で上書きする方法

docs.unrealengine.com

ゲーム内で使うために作成したアニメーションを使いまわして低コストでムービーを作りたいときはこの方法です。
歩く・走る・しゃべる等を再生して、足が滑らないようにトランスフォームをシーケンサーで編集します。

キャラクターの配置とアニメーション

今回はアニメーションBPを使った方法で作りたいと思います。
まずはカメラワークを決めるために、とりあえずキャラクターの動きを作ります。

この時点で、絵コンテや展開を決めておきます。 今回は、

  1. プレイヤーが扉から出てくる。
  2. ボスが上から落ちてきてプレイヤーの行く手を阻む。
  3. プレイヤーが身構える。

この流れにします。

プレイヤーのアニメーションBPをレベルに置きます。  

f:id:JackpotDevelop:20210414203611j:plain
アニメーションBPを配置

レベル上のアニメーションBPが選択された状態でシーケンサータブを開き、「+トラック」ボタンから アニメーションBPのトラックを追加します。

f:id:JackpotDevelop:20210414203907j:plain
トラックを追加

扉をシーンに追加し、アニメーションBPを初期位置に移動させました。
ここで、トランスフォームにキーを打ちます。

f:id:JackpotDevelop:20210414204409j:plain
初期位置に移動させてキーを打った

シーケンサーのカーソルを180フレーム目に移動してから、アニメーションBPを選択して前方に移動させてキーを打ちます。
すると、前のキーの位置と、現在のキーの位置を結ぶ線が出現します。

f:id:JackpotDevelop:20210414210713j:plain:w500
線が現れた
これで最初のプレイヤーの動きが出来ました。
スペースキーでシーケンサーを再生することが出来ます。移動させた位置によっては早すぎたり遅すぎたりするので、位置を調整するか、キーをドラッグアンドドロップで移動させて調整しましょう。

補間なしの動きをさせたいときは、キーにマウスを乗せて右クリックすると「キー補間」を選ぶことができます。 三角のやつにしました。

f:id:JackpotDevelop:20210414213406j:plain
キー補間をリニアにしました

アニメーションを割り当てたいところですが、先にボスの方も動きを作っておきます。
プレイヤーの時と同じように、ボスのアニメーションBPを配置し、動きを作ります。
配置したら、選択した状態でシーケンサーの「+トラック」ボタンから追加します。
上空に配置して160フレームくらいにキーを打ち、170フレームにカーソルを移動した後、プレイヤー前方に配置してキーを打ちます。

f:id:JackpotDevelop:20210414212026j:plain
ボスのキーが出来ました

再生してみると、ボスがヌルっと着地した後、プレイヤーが止まります。
とりあえずここで、それぞれの動きは完了とします。次はアニメーションです。

配置したアニメーションBPを開き、アニムグラフにスロットを追加します。
このスロットにシーケンサーからアニメーションを流し込みます。

f:id:JackpotDevelop:20210414212509j:plain:w300
スロットの追加

シーケンサーからアニメーションBPを操作する場合、もうひと手間必要になります。
アニメーションBPの詳細タブ「アニメーション」の「AnimationMode」を「Use Custome Mode」に変更します。

シーケンサーのトラックにアニメーションというトラックがあると思うので「+アニメーション」からThirdPersonWalkを選択。タイムラインにアニメーションが追加されました。

f:id:JackpotDevelop:20210414212943j:plain
アニメーション追加

再生してみて足が滑る場合は、タイムライン上のアニメーションを右クリックして、プロパティからPlay Rateを変更します。
ここではアニメーションの差し替えやアニメーション終了後の挙動等を設定することができます。

f:id:JackpotDevelop:20210414213706j:plain
アニメーションの再生速度を変更

ボスの方も落下中、着地のアニメーションを割り当てます。タイムラインに置いたアニメーションはブレンドのカーブを調整できるようになっているので調整しておきます。

f:id:JackpotDevelop:20210414214357j:plainf:id:JackpotDevelop:20210414214429j:plain
見辛いけどコレ、動かせます

アニメーションの割り当て、ブレンドをしてキャラクターの動きが完成しました。

カメラワーク

Shot1

マスターシーケンスのショットトラックから最初のショット(shot0010_01)をダブルクリックします。
CineCameraActorのトラックのカメラマークを押すと、「パイロットモード」となりカメラに乗り移って位置を変更できます。 ここで気づくのが、やたらズームされてるなぁということ。使いにくいので変更します。
CineCameraActorのトラックを選択すると詳細タブにカメラ設定が出てくるので、「現在のカメラ設定」→「レンズ設定」から「12mm Prime f/2.8」を選びます。

f:id:JackpotDevelop:20210414222050j:plain:w300
レンズ設定から拡大率を変更
視点を好きな位置に動かしたら、エディタ左上もしくは、CineCameraActorトラックのカメラマークを押してパイロットモードを解除します。

最初のショットのままだとプレイヤーが見切れてしまうので、マスターシーケンスに戻って、適度な長さに調整します。

2番目のショットを詰めて配置して、ボスが落下するキーまで伸ばします

f:id:JackpotDevelop:20210414223901j:plain
1番目と2番目のショットの長さを調整

Shot2

2番目のショットをダブルクリックして編集します。 このショットではフィールドがどんな場所かプレイヤーに見せるため、主人公を画面内に収めつつカメラを動かしてみます。 キャラクターのトランスフォームをアニメーションさせた要領で、カメラのトランスフォームを調整します。
初期位置を決めたらZ位置を下げてキーを打ちます。

f:id:JackpotDevelop:20210415013624j:plain
カメラがZ方向に下がるようにアニメーション
キャラクターの時と同様、これでアニメーションされます。
f:id:JackpotDevelop:20210415013958g:plain
Shot02の結果(ボスの足見えてる…)

Shot3

3番目のショットでは、落下してくるボスにカメラをフォーカスします。
ショット3はボスの落下のキーから着地のキーの少し後までの長さにします。

f:id:JackpotDevelop:20210415015100j:plain
ショット3の長さ

フォーカスさせるにはカメラの詳細パネルから「現在のカメラ設定」→「カメラ追跡の有効化」にチェックします。

f:id:JackpotDevelop:20210415015414j:plain
カメラ追跡の設定
「追跡するアクタ」にボスのABPを指定し、相対オフセットのZ値を変更します。
相対オフセットはデフォルトだと対称アクタの原点になっているので、Z値の調整が必要になります。

Shot4

身構えるプレイヤーを映すショットです。
ゆっくり主人公の顔にカメラを近づけます。エディタ上でカメラを選択し、ギズモの座標システムをローカルに切り替えます。カメラの正面方向に動かせるようになるので、初期位置と主人公の手前でキーを打ちます。

f:id:JackpotDevelop:20210415023354j:plainf:id:JackpotDevelop:20210415023356j:plain
ギズモをワールドからローカルに切り替える

カメラワークはこれで完成です。

仕上げ

アクタを非表示にしておく

Shot2でボスの足が見えてしまっていました。落下のアニメーションまでは非表示にしておきましょう。 ボスのアニメーションBPのトラックに可視性のトラックを追加します。

f:id:JackpotDevelop:20210415024345j:plain
Actor Hidden In Game(可視性)を追加する

「可視性」のトラックが追加されます。チェックを外した状態、つけた状態でそれぞれキーを打てば「このタイミングでは非表示」と設定することができます。Shot2の間は表示されないようにしました。

f:id:JackpotDevelop:20210415024521j:plain
可視性の切替え

フェードイン・アウトを追加

マスターシーケンサーの「+トラック」ボタンから、フェードトラックを追加します。

f:id:JackpotDevelop:20210415192406j:plain
フェードトラックを追加する
0フレームの時にフェードを1,少し後にカーソルをずらし0のキーを打ちます。
画像で赤丸で囲った部分を編集すると、カーソルの位置に自動でキーが打たれます。 トラックの色もフェードしてくれるので直感的に分かります。
f:id:JackpotDevelop:20210415192718j:plain
フェードトラックに2つキーを打った

ムービーの終わりにもフェードアウトを作りましょう

f:id:JackpotDevelop:20210415192912j:plain
フェードアウトを作りました

ディレクターブループリントでマップを読み込む

マスターシーケンサーの「+トラック」ボタンからイベントトラックを追加。

f:id:JackpotDevelop:20210415193058j:plain
イベントトラック

ムービーの一番最後にキーを打ちます。うすーくキーが作成されるのでここにマウスを重ねて右クリックします。

f:id:JackpotDevelop:20210415193344j:plain
イベントのバインド
プロパティ→アンバウンド→新規エンドポイントを作成をクリックします。
マスターシーケンサーのディレクターブループリントが開くので、OpenLevelノードを追加します。
f:id:JackpotDevelop:20210415193839j:plain
OpenLevelでボス戦のマップを読み込む

このイベントトラックは割といろんなことに使うことが出来て、アニメーションに合わせてエフェクトをスポーンしたり、音を出したりブループリントで出来ることがなんでも割り当てられます。 詳しい実装は公式ドキュメント見て…!
イベント トラック | Unreal Engine ドキュメント

完成

冒頭でも載せたましたが、完成したのがこちら。

 

まとめ

今回はムービー用のマップとボス戦のマップを別に作りましたが、サブレベルの読み込みを制御することでひとつのマップで行うことも可能だと思います。
(プレイヤーの入力、UIの表示、シーケンサーで変更したポストプロセスなどちゃんと戻す必要があります)
また、死んで再度訪れた際、ムービーを流してほしくない場合は、BP_LevelChangerで読み込むマップを変えておくといいでしょう。      

ボス前カットシーンを作る(1/2)ロジック編

大きな扉の前に近づくと「入る」と表示され、任意のボタンを押すと暗転してムービーが始まる。
そんなよくある演出、ボス前カットシーンを作ります。
※ムービーの撮り方・カット割りについては別記事で解説しました。
本記事ではロジックの解説になります。長くなったのでロジック編とシーケンサー編の全2回に分けました。

仕様

  1. ボス前の扉で任意のボタンを押す
  2. 画面が暗転(フェードアウト)
  3. ムービー開始・終了(フェードアウト)
  4. 戦闘開始  

マップ作成

まず、先に3つのマップを作っておきます。

現在いるマップを「Map01
ムービー中のマップを「Boss_Cutscene
ボス戦のマップを「Boss_Play

f:id:JackpotDevelop:20210413215946j:plain
作成した3つのマップ

※マップ、パーシスタントレベル、サブレベルについてはこちら参考に
UE4 パーシスタントレベルとサブレベル 凛(kagring)のUE4とUnityとQt勉強中のゲーム制作ブログ
複数のレベルを管理する | Unreal Engine ドキュメント

Map01

ボスの部屋に続く大きな扉を用意します。※別に扉じゃなくても何でもいいです。

アンリアルエンジン
シーン上に置いた扉
この扉の前で任意のボタンを押して中に入れるようにしましょう。

「トリガー内にプレイヤーがいる間、入力を受け付ける」アクタを作成します。
新しいアクタ、BP_LevelChangerを作成します。

f:id:JackpotDevelop:20210413213524j:plain
Actor型のBP_LevelChanger

ボックスコリジョンとマテリアルビルボードを追加

f:id:JackpotDevelop:20210413222452j:plain
BP_LevelChanger
マテリアルビルボードウィジェットでもいいです。
簡易的に説明するためマテリアルビルボードにしました。
デフォルトだとゲーム開始時から文字が表示されてしまうので、マテリアルビルボードのVisibilityはオフにしておきましょう。
f:id:JackpotDevelop:20210413231909j:plain
マテリアルビルボードのVisibilityはオフに

プレイヤーがコリジョンに重なった時の処理を作っていきます。
f:id:JackpotDevelop:20210413223439j:plain
プレイヤーがコリンジョンにオーバーラップしたときの処理
ボックスコリジョンにオーバーラップしたアクタにPlayerタグがついていれば、文字を表示する。
続けてEnableInputノードを置いて、このアクタがプレイヤーからの入力を受け付けるようにします。

プレイヤーのアクタにはPlayerタグをつけておきましょう。

次にプレイヤーが重なっているときだけ実行することが出来る処理を作ります。

f:id:JackpotDevelop:20210413225209j:plain
「中に入る」ボタンが押されたときの処理
今回、中に入るボタンはFキーを割り当てたので、Fインプットイベントに処理を作りました。
処理の解説としては、

  • マテリアルビルボードが表示されているかチェック
  • 入ることが確定されたら、PlayerPawnとBP_LevelChangerの入力を無効化
  • PlayerCameraManagerをつかったフェードアウトを実行
  • フェードアウトと同じ時間をDelayに入れて真っ暗になったらレベル読み込み

ここまで作ったら、シーンにBP_LevelChangerを置きましょう。
詳細パネルで、Boxコリジョンを選択し、シェイプを好きな大きさに調整します。

f:id:JackpotDevelop:20210413231302j:plain
扉の大きさに合わせてシェイプを調整

これでMap01は終わり。
ちなみに、実行してみると、オーバーラップで「中に入る」が表示され、その状態でFを押せば フェードが開始されます。画面が完全に黒くなった瞬間次のマップに遷移します。

続きはシーケンサー編へ

jackpot-lab.hateblo.jp

ムービーの構図・カット割りの例

f:id:JackpotDevelop:20210414115434j:plain
ボスパックンさん
いわゆるシネマティックムービーを作るときの参考になれば~。

ムービーってどう撮ればええねん問題

カメラの動かし方も分かった。シーケンサーも理解した。
で、この後ぶち当たるのがどんなムービーを取ればいいの?というところです。
映画技法、絵コンテ、漫画のネームあたりの知識があれば、ある程度の流れや良しあしの判断がつきますが、そんなもの持ち合わせていません。
そんなときは、例を集めて模倣するのが一番手っ取り早いです。

「なぜこのカメラワークなのか、なぜこれを映すのか」を例を挙げながら解説します。 挙げたポイントをかいつまんでムービーを構築すれば、それっぽくなるのではないでしょうか。

サンプル3つ

マリオサンシャイン ボスパックン

長さ:23秒
子供向けの作品なので、短めの尺になっています。

  • 2:00 印象的なシルエットを生かしたドアップからスタート
  • 2:03 咆哮のシーンは、巨大な頭身であることを伝えるためにマリオの目線の高さから見上げるようなカメラ
  • 2:08 床に亀裂が入ったことが分かり、パックンとマリオのリアクションも見えるカメラ位置
  • 2:18 落下のシーンで上からではなく、あえて真下から撮ったのは、空を丸く映すことで印象に残すため(撮れ高
  • 太陽をのぞかせる(マリオサンシャインだから!)
  • ゆっくりカメラをロールさせることでレンズフレアが動くようにしている。

短いながらも、印象に残るムービーとなっています。さすがN天堂。
メディチックではありますが、セリフが一切ない中でボスパックンのキャラクターを伝え、理屈の通ったカメラワークをしています。

Metal Gear Rising  LQ-84

長さ:1分(会話シーン除く)
開発はプラチナゲームズなだけあってこういう演出ムービーはお手の物。
目まぐるしく進む展開、でも何が起こっているかちゃんとわかるムービーになってます。

  • 0:07 異変に気付き、曲がり角の先を見つめる雷電、カメラごしのプレイヤーも同じように感じるよう雷電の背後へ
  • 0:12 しかし、突然背後からノコギリ。このノコギリを画面外から登場させるために上記のカメラワークになったと考えられます。
  • 0:15 アゴ火花(なくてもストーリーは伝わるが、美味しい演出)
  • 0:22 雷電の後頭部から、床を切るノコギリへズーム(プレイヤーへ状況説明と位置関係を把握させる)
  • 0:32 建物が壊れ、天地が狂うシーン。プレイヤーを混乱させないために、滑っている足を映してから重力で落下するコンテナを映す。
  • 0:35 着地する床が初めて見えるとき、一発で床と分かるようにタイルのテクスチャ。コンテナと雷電の落ち影が画面内に映るライト位置。
  • 0:39 1秒弱ほどですが、雷電が上を見上げます。間を作り、プレイヤーに「この後、重要なことが起こるぞ」と認識させる大切な1秒です。
  • 0:42 ぶった斬ったコンテナから姿を現すLQ、カッケェ。(最大の見せ場なので上記の間が重要だった)
  • 0:45 ノコギリを受け止めた後、爪で攻撃されます。ここで一瞬爪が光るのはプレイヤーに視線を映してほしいからですね。(視線誘導。ちなみに雷電を見ていたとしても彼の視線が爪に向くので2重の視線誘導になっている)
  • 1:00 ちょうど1分のタイミングでLQの全身カット。ここでムービーがトーンダウンします。緊張のシーンは長くても1分が良いのでしょうか。

アクションの激しいムービーはそれぞれのカットが一瞬で過ぎ去っていきます。
何が起こっているかプレイヤーに瞬時に理解してもらうために、あらゆる配慮がされていますね。

ゼルダの伝説 スカイフォードソード ギラヒム

長さ:2分
会話シーンのあるパターン。テキストを読ませるため、カメラワークがゆっくりだったりやキャラの動きは最小限だったりします。

  • ~0:20 ボスの姿は見えているが、顔を見せるまで20秒も焦らす
  • 0:42 最初は敵対していない状態なので普通のカメラワークですが、42秒の部分でカメラが傾き画面がななめになります。プレイヤーに少しずつ違和感を与えます。(漫画でもよく使われますよね)
  • 0:57 扉の正面にカメラが回ります。これは話の流れやキャラクター関係なくこの構図が撮れ高として使えるからです。
  • 1:00 カメラワークが絶妙ですね。かなりゆっくり寄っていき緊張を高めて、ゆっくりとしたカメラシェイクが恐怖感を出しています。
  • 1:48 ようやくボスっぽいムービーになりますが、画面がななめになってますね。最初に画面が傾いていた伏線がここで回収されるわけです。

会話のあるパターンは、プレイヤーが操作できない時間が長くなりダレてしまうことが多いです。 ゼルダのように作りこまれた世界観やDMCシリーズのようにキャラクターがジョークを言うことで体験が成り立つゲームで効果を発揮します。
もし会話シーンが長くなるようなムービーを作る必要があるなら、プレイヤーに「もっと見たい」という工夫が必要になります。(ギラ様の場合は不安定さがプレイヤーの見る動機になっていた)

まとめ

さて、3つ例を紹介しましたが、展開の原則としては以下のような感じでしょうか。

  • 日常パートから異変発生
  • そのボスの能力を示す演出・展開
  • 敵対状態を示す(咆哮など)
  • プレイヤーに与えたい体験の要素を入れる

また、カメラワークには必ず理由や意図があることがわかりました。

  • 状況がすぐわかる易しいカメラ
  • 盛り上げるためのカメラ
  • 撮れ高・画面構成のためのカメラ

カメラに意味があることが分かると、映画やアニメを見るときにもっと楽しめます。
3Dのゲームが発売された当初も映画を参考に作られたのではないでしょうか。

余談ですが、自分がいつも参考にしてるのはゲームではなくて映画のことが多いです。
ストーリーテリングのために何十年もノウハウを積み重ねて洗練されています。レンタルDVDには監督の音声解説から、カメラワーク・ライティングの意図を汲む事が出来ます。
あ~そうでした。ライティングの話もありましたね。
それはまた別の機会に。

ブログ開設しました。

はじめに

ツイッターだけで活動してましたが、「こんなマニアックなこと呟いても、誰も見ねーよな」という場面が何度かあったので放出用に作りました。
それに、現在自分がUE4を使いこなせているのは色んな方が書いた記事のおかげなので、自分も書くべきかなと。

それに技術ログ以外になにかコンテンツを補充するかもしれないので、雑多な場にしたいなという気持ちもあります。

誰向けのブログなの?

UE4で開発してる人の役に立てばいいな。

初心者向けの記事は、色んな方が既に沢山書かれているので一歩踏み込んだ実装方法をまとめたいですな。