Weekly Game Jam 1週目

一週間前に、

こんなTweetをしたので一週間でゲームを作りました。結果ですが、完成まではいけませんでした( ;∀;)
ですが、自分なりに学べたことがいくつかありましたので技術共有ということでブログにしたいと思います!

とりあえずこんなゲームを作りました。



お題
Game Jamなので、お題がなければ始まりません。そのお題ですが今回は FPS でした。
FPSの概念ですが、こちらを参照
ファーストパーソン・シューティングゲーム - Wikipedia


シンプルに、いつも作っているようなShooterでも良かったのですが、一週間も期間があるのでちょっと自分なりに挑戦してみたいと思い、いつもとは少し違うジャンルの制作に挑戦しました。
ちなみにいつも作ってたのはこういうのです。





それで、今回作ろうと思ったゲームはこんな感じのゲームです。
youtu.be

こちらのゲームはアーケードゲームとして有名な、The house of the dead シリーズです。
操作の概要ですが、移動は自動、プレイヤーが唯一しなければならないことと言えば
" 敵の弱点目掛けて鉛玉をぶち込む " だけです。このシンプルな操作 + 弾は無限というところから来る爽快感、また、敵は数で圧倒してくるので弱点をしっかりと狙い如何に非ダメージを減らすかという緊張感がウリのゲームとなっています。(僕はそう思ってますw)

では、The house of the dead (以下 HOD )をUE4で一週間で作るにあたって、どんなことを考えながらロジックを組み、HOD らしさを自分なりに出したのかを書いていきたいと思います。





プロトタイプ編
まずは、このゲームの面白さを確認するための " 土台 " を作らなければなりません。HOD の土台は一体何か?と考えた時、
私は「自動移動ロジック」だと考えました。
基本的にアーケードゲームとして遊ばれているこの HOD 。ゲームセンターに来るお客さんがシューティングゲームに慣れている方ばかりなんて想定はされていません。なので Input を削りに削って、プレイヤーがしなければいけないことが銃を撃つということだけになっているのだと思います。
ということは、" 敵を倒す為に銃を撃つ = 楽しい "が成立しなければ、ゲームとしては失敗です。
では、 " 敵を倒す為に銃を撃つ = 楽しい " をプロトタイプで達成する為にまず、必要な基本的なロジックは何か?
となると「自動移動ロジック」に落ち着くわけです
自動で移動してもらい、プレイヤーは射撃に集中する。この仕組みをプロトタイプで作ろうと決めました。


自動移動実装編①
このロジックを実装しようと考えた時、方法はいくつか浮かびましたが最善は Spline かなと確信していました。
docs.unrealengine.com
方法は浮かびましたが、このノードを使えば行けそう...とかは全く思い浮かびませんでした。
ですが Unreal Engine には、Content Example なるものが存在していることは知っていました。
docs.unrealengine.com
Content ExampleUE4 がアップデートされた際に追加された機能などをサンプルとして展示、使用例などを提示してくれるものです。
Spline 自体は4.7あたりから純粋な機能として入っていた気がしますので、サンプルは豊富だろう!と思い勉強させていただきました。




自動移動実装編②
この自動移動ロジックですが、大きく分けて三つに細分化出来ると考えました。

1 - 移動( Location )
2 - 回転( Rotation )
3 - イベント

です。一つずつ説明していきます。

1 - 移動
これは非常に単純で名前の通りですが、プレイヤーの移動です。
移動と言っても、何を、どれくらいの時間で、どれくらいの距離を移動させるかなど考えることは様々ですがこんな感じで取り合えず落ち着きました。
f:id:ikagamedev:20171204173533p:plain

1 - 移動はお馴染みの、SetActorLocation ノードを使っています。移動させる Target はシンプルに Player Pawn です。

2 - Get Location at Distance Along Spline ノードは Content Example の使用例で使われていたので使っています。恐らく、Enum Cordinate Spaceinput から、その座標系にある Spline の座標を返してくれるノードです。
今回の移動は Spline 上での移動を想定していますので、SplineLengthProgress Of Proceed 変数 ( 0 to 1 )を掛けています。

3 - Progress Of Proceed 変数ですが、ここの演算は元々 Timeline で行って、その 0 to 1 の返り値を Progress Of Proceed の代わりにしていたのですが、何秒で Spline の終点まで行くか。というパラメータが扱いにくかった為に変数での制御になっています。

4, 何秒で終点まで行くか。の変数ですが、GoalTime と名付けています。Progress Of ProceedGoalTime 秒で1になれば良いので毎フレーム、Progress Of Proceed = Progress Of Proceed + ( Delta / GoalTime ) をすることで何秒で終点に到達するか。を実現しています。


2 - 回転
回転は非常にシンプルです。Spline の直線方向を常に見させる...。というものだと思っています。(あまり理解せずに使ってましたw)
とりあえずこれで問題はなさそうだったのでOKということで
f:id:ikagamedev:20171204174636p:plain
ActorRotation を弄らず、ControlRotation を弄っているのは PlayerPawn のクラスが持つComponent の親がカメラだからです


3 - イベント
ここがそこそこ難しかったです。そして、あまり綺麗な処理を組めていません...。ですが、晒していきます。
の前に、試作を一つ作ったので先ずはこちらから
f:id:ikagamedev:20171204180054p:plain
左が EventGraph ,右が折りたたまれたノードです。
True になったら、移動を止め、何かしらの進展があり次第移動を再開する。というシンプルな構成になっています。

HasArrivedSplinePoint 内の処理について説明します。
Get Location at Spline Point ノードで Point Index を受け取り、その PointLocation を返り値として返します。その値 (Vector) とプレイヤーの Location(Vector) を比べ、誤差が1ユニット以下なら True に処理が流れます。
非常にシンプルです。これらの処理で出来たwipがこちら


何となくそれっぽくなってるのがわかりますね!細かく説明すると、赤いキューブを規定以上倒すことによって、移動のロジックが再開される仕組みになっています。
ここらへんからテンションが上がってきてます。



ではここで少し脱線して、敵の Spawner クラスについて説明していきます。(これも試作です)


ex - 敵スポナー
f:id:ikagamedev:20171204182459p:plain
変数、Component の説明をしていきます

Component

  • Box 敵をスポーンする際に使用、こいつの Extent(大きさ) 内にランダムでスポーンさせています
  • BillBoard 他の BP からスポイト参照する際に目立つ Component がなかった為追加
  • TextRender SplinePoint で移動が止まる。というロジックを組んでいた為に、どの Point で止まるかを明示する為に追加


変数

  • MaxEnemy 敵がスポーンできる最大数
  • CurrentNumOfEnemy 現在ワールドにいる敵の数
  • EnemiesRefe 敵のリファレンス配列
  • DestroyedCound 敵が破壊された場合にインクリメント
  • BoxExtent(Editable) BoxComponent の大きさ、エディタから編集できるようにしています
  • Text(Editable) SplinePoint の何番目かにいるかを明示する為のText、上に同じくエディタから...(以下略)



ロジック

Construction Script
f:id:ikagamedev:20171204210850p:plain

  • SetBoxExtent 名前のまんまですが、BoxCollisionComponentExtent を設定する関数です。Box Extent 変数で制御できます
  • SetText 大体上と同じ説明...。

Begin Play
f:id:ikagamedev:20171204210801p:plain

  • Max Enemy Set Max Enemy 変数に 3~7 の数値を入れてあげてます。この数値を元に、スポーンできる最大数が決まってきます。
  • SpawnEnemy 後で説明します。

Custom Event Spawn Enemy
f:id:ikagamedev:20171204212259p:plain

  • Delay このカスタムイベントは勝手に Max Enemy回 ループする仕様なので、Delay を挟んでいます。
  • Spawn AI From Class 敵キャラをスポーンします。Random Point in Bounding Boxノード で、BoxCollision内 でランダムにスポーンさせています。

Rotation は単純にプレイヤーの方向を向かせているだけです。で、最初は Spawn Actor From Classノード Spawn していた(赤い CubeActorクラス だった為)のですが、本番用の Mesh に切り替え、Characterクラス を継承した敵キャラを Spawn Actor From Classノード を使ってスポーンした際に、AI Controller がスポーンしていないことに気づき、急遽このノードに変えました。

  • Add EnemiesRefe配列 にスポーンされた Enemy のリファレンスを追加しているだけです
  • Current Num Of Enemy Increment スポーンする = 敵キャラが増える。ということなのでインクリメントしてます。
  • Branch 非常にシンプルで、Current Num Of Enemy <= Max Enemy の間はループです。False になったら別ロジックが1秒起きに動きます。


Enemy Check
f:id:ikagamedev:20171204212243p:plain

  • ForEachLoop この処理は、EnemiesRefe に入っている値が Null かどうか?を調べ、NULL であった場合は DestroyedCount変数 をインクリメントします。この処理を毎秒やるのは気が引けましたが、しょうがなく....。(For loopでIndex指定したループの方がパフォーマンス的にいいのでしょうか?教えてください!)

ループが終わった時の Current Num Of Enemy DestroyedCount が等しいのであれば、全ての敵が NULL 、すなわち倒されたということになるので True に処理が抜け、イベントディスパッチャーが呼ばれる仕組みです。そして、イベントディスパッチャーが呼ばれることで自動移動が再開されます。




ここまで書いてきましたが、この子たちはあくまで試作です。(僕は何度も作っては消して、作っては消してを繰り返すタイプです)
では本番用のロジックはどうなったのかを見ていきます。





3 - イベント
上の HasArrivedSplinePoint はそのままです。ですが、この Bool値 による分岐だけだと Spline Point に到達する度 " 必ず " 移動が止まり、何かしらをしなければならなくなります。
では、Spline Point を出来る限りなく少なくして、イベントが起きる位置にだけ Spline Point を置けばいい。
ということをすれば、レベルデザイナーは苦しみ、プログラマーを呪い、僕はその念によって体調を崩すかもしれません。
それは避けたいので、特定の Spline Point にだけ止まれるような仕組みを作らなければなりませんでした。


4 - イベント本実装編 BP_MovePath
特定の Spline Point で止まるということは、結局は Bool値制御 になるだろうと考えていました。
そこで思いついたのが、Map です。
docs.unrealengine.com
Spline PointInteger , そして、止まる、止まらないが Bool ということは Integer の中にBoolean を入れることができる Map を使えば実装できるのでは!?と考えたわけです。
そして実装してみたものがこちら。


Construction Script BP_MovePath

f:id:ikagamedev:20171204230745p:plain

ForLoop で、SplinePoint数 ループをします。Integer Boolean Map の要素数 = SplinePoint数 です。
そして、Bool値(止まるか止まらないか) ですが、Event Actors配列 の中に何かしらが入っている = イベントが起きる。ということなので is ValidノードTrue or False を指定しています。
そして、EventActorsの要素数 = SplineのPoint数 なので SplinePoint数Resize をしています。

Event Actors配列 ですが、リファレンスは BP_EventActor です。
f:id:ikagamedev:20171204222652p:plain


BP_EventActor を親として、BP_Event_Door , BP_Event_EnemySpawner が生み出されてるので、Construction Scriptis Valid に反応して True を返してくれる訳です。
f:id:ikagamedev:20171204222728p:plain



ということは、BP_EventActor を親とすれば、どんなイベントでも実現可能というわけです!!!!うおおおおおおお!!


そして、BP_EventActor で、Do Some Event Custom Event とイベントディスパッチャーの LogicHasFinishedSuccessfully を作成しました。
f:id:ikagamedev:20171204224559p:plain



この二つを子クラスで呼びだすことで、Spline のクラスと連携を取っているわけです。
フローで見ると

  • プレイヤーが EventActors配列 の要素が NULL でない Spline Point に到達し、移動が止まる
  • DoSomeEvent が呼ばれる
  • BP_EventActor を親とするクラス内で諸々の処理が実行される
  • 実行が終わり次第、イベントディスパッチャーである LogicHasFinishedSuccessfully が呼ばれる
  • 移動が再開される

というわけです。



例として、BP_Event_Door を見ていきます。
f:id:ikagamedev:20171204224906p:plain

Event Do Some Event が呼ばれると、bCanOpen 変数に True が入ります。
TickbCanOpen変数True であれば、ドアが開くロジックが実行されるわけです。


f:id:ikagamedev:20171204225319p:plain

そして、左のドア(この際、どちらのドアでもいい)の Relative Rotation と決め打ちされた値を比べ誤差が 0.1以下 なら(つまり、開ききったら) bCanOpenFalse を代入し、LogicHasFinishedSuccessfully が実行されるというわけです。




Event Graph BP_MovePath
f:id:ikagamedev:20171204220101p:plain

これは非常に単純で、Integer Boolean MapSpline Point Index要素Bool値True かつ、要素が存在するのであれば何かしらのイベントを起こさせる...。といったものです。
False であれば、素通りしてくれます。動画で取ってみましたので、どうぞ。
youtu.be

これで自動移動編は終わりです。最後にオマケで AI編 をどうぞ




AI
上の HOD のプレイ動画を見ていただいたら分かる通り、単純にプレイヤーに向かってくる AI ではありません。
何らかのパラメーターにより重みづけをされ、ウェイトが一定値を超えると攻撃してくる仕組みになっています。
流石に AI に着手したのが遅すぎたので、これらの動きを出来る限り近似して表現することになりました。

最初に敵 AI の特徴を見つけるとしたら、綺麗にプレイヤーの正面に立ち、一定の間隔で攻撃してくるということだと思います。ここで AI をそれっぽい動きにさせる為にプレイヤーの正面に BillBoard を三つ置き、EnemyStayPos と名付けました。
ここに三体の AI を移動させようと企てたわけです。
f:id:ikagamedev:20171204233404p:plain

まあそこそこ上手くいって、それっぽく見えるのでOKということにしました。三体以外の AIプレイヤーの前方向ベクトル * Random Float( 200 ~ 500) + プレイヤーの横方向ベクトル * Random Float( -200 ~ 200) 付近で待機してもらっています。攻撃してくる三体がやられ次第、適当なところにいる AI が正面にまた来るような形です。また、AI 同士の衝突を避ける為に RVOAvoidance を設定しています。Avoidance Consideration Radius 100 , Avoidance Weight 0.5です。



攻撃当たり判定
Trace の進み具合はこんな感じです。
www.youtube.com

f:id:ikagamedev:20171204235622p:plain
AttackTrace という名の関数を作成しました。
ForLoop で複数回実行されます。今回は、NumOfAttackTraceLoop回 実行されます。(今回は30回にしてます)
では左から解説していきます。まず、今回の思想として、AI から見て 180°方向 にだけ Trace をしたいと考えていました。なので先ず、180 / NumOfAttackTraceLoop をし、それと ForLoopのIndexを掛けたもの自身のRotationに足している だけです。途中で -90° しているのは、MeshRelative Rotation-90° だからです。

だらだら書いていたら長くなってしまいました。最後までお付き合いいただきありがとうございました。

UE4で返り血の表現

こんにちは。記事名が物騒ですみません。書いていきたいと思います。
今回やる処理はこんな感じ。
www.youtube.com
では、やっていきましょう。


1 : 血のマテリアルの作成

f:id:ikagamedev:20170223033901p:plain

マテリアルはとても簡単です。血のテクスチャですが、

youtu.be
こちらの方が配布してくださっているので、ありがたく頂戴しましょう。


2 : 適当な StaticMesh にマテリアルを割り当てる
f:id:ikagamedev:20170223034233p:plain


3 : BP_BloodDecal クラスの作成
ComponentStaticMesh を追加し、Pitch 方向に +90します。Blueprint ですが、今回はこれしか書きません。
f:id:ikagamedev:20170223035610p:plain

X, YScale変数ですが、Editable, Expose on Spawn チェックを入れるのを忘れないようにしましょう。
f:id:ikagamedev:20170223035725p:plain

最後に、Component BP_BloodDecal をクリックし Initial Life Span をいい感じの値にしましょう。
f:id:ikagamedev:20170223040133p:plain


4 : BP_BloodDecal を Spawn したい Character クラスでの処理
倒れる時に BP_BloodDecalSpawn したいと思います。(なので今回は Event AnyDamage のあとに呼び出しています。ここら辺はお好みでどうぞ。)

(1) 壁に返り血が付く処理について
f:id:ikagamedev:20170223041127p:plain
まず、LineTraceStart, End を求めていきます。

Start
自分自身の Location をゲットしたいので、GetActorLocation します。それを分解しZ軸方向にだけ加減算しています。これによって、LineTraceStart するZ位置に変化を持たせています。


End
For loopNumOfLoops 数ループさせます。(360 / NumOfLoops) * IndexMake Rotator することで、For Loop が一度終わる度 Index が増えていくので 360°になるまで NumOfLoops 数ループしてくれます。 そのあと、GetRotationXVector でX方向ベクトルを返し、300を掛けています。この300ですが、LineTraceが届く距離です。こうして出てきたベクトルの値と、Start ベクトルを足すことで End ベクトルが導かれます。(Actors ignore は気にしなくていいです。)

(2) BP_BloodDecal を Spawn させる処理について
f:id:ikagamedev:20170223041131p:plain
まず、当たったかどうかの判定の為に Branch を挟みます。次に、Spawn Actor From Class のパラメーターについて説明します。

Class

これは紛れもなく、BP_BloodDecalSpawn して下さい。

Collision Handling Override
こちらは、Collision は無視して毎回 Spawn させたいので Always Spawn, Ignore Collisions を選択します。

X, YScale
BP_BloodDecalExpose on Spawn にチェックを付けたので出てきたパラメーターです。こちらはランダムな値で出てほしいので Random Float in Range ノードを使っています。

Spawn Transform
Location
Impact Point 直繋ぎでもよさそうですが、Z-Fight が起きてしまうので Impact Normal だけ足しています。
www.youtube.com


Rotation
まず、HitResult 構造体を分解します。その中にある情報から今回使うのは Impact Point と、Impact Normal のみです。
Find Look at Rotation ノードで、角度を求めていきます。Start は、当たったポイントなので、Impact PointTargetImpact Point + (Impact Normal * 90) で求められます。(Impact Normal, Normal正規化 されているので返ってくる値は-1 ~ 1です。)

Scale
こちらは設定する必要はありません。

以上で壁への返り血は完了です。次は、地面への返り血です。


(1) Spawn Transform についての処理

f:id:ikagamedev:20170223051921p:plain
Location
まず、Character クラス前提の話なのですが、Capsule ComponentWorld Location をゲットします。恐らくですが、GetActorLocation でも同じ結果が見込まれます。(Capsule Component WorldLocationCapsule の中央 = Root = GetActorLocation...?)
ゲットした Capsule World Location から Capsule の大きさを引くことで、BP_BloodDecalSpawn する位置を地面にオフセットします。(つまりZ軸は0。なので地面に Spawn します。)
そして、そのベクトルを分解します。このまま Location に入力してもいいのですが、せっかく For loop で複数回 Spawn させたいと思っているので X, Y 軸方向にランダムなフロート値を足したいと思います。これは単純で、For loopIndex と ランダムなフロート値を掛けて、それぞれ X, Y に足しています。そして、Make Vector して Location は終了です。


Rotation
Find Look at RotationStart から説明します。Start は 先ほどの処理だと Trace が当たった位置でした。ですが、今回の処理は毎回地面なので Capsule World Location(Z) - Capsule Half HeightOKです。
Target ですが、ImpactPoint の代わりが Capsule World Location(Z) - Capsule Half Height でしたので、その通りにつなぎます。では、Impact Normal はどうするかですが要するに、地面の法線ベクトルを取れればいいです。地面の法線ベクトル = Character Up Vector なので90をかけるベクトルは Capsule Up Vector です。(今回、敵が死亡した時メッシュに物理シミュレーションを適用していますので Capsule ComponentUp Vector を取っています。)

以上で完了です。お疲れさまでした。プレイして試してみましょう。
最後までご覧いただきありがとうございました。良い、Unreal Engine ライフを!

AimOffsetに合わせてAnimMontageをプレイさせる

お久しぶりです。備忘録ということで記事を書きたいと思います。
記事の題名ですが、一体どういうことかというとこういうことです。

www.youtube.com

下から上に向けてAimしながら発砲しています。発砲の際に AnimMontage を再生しているのですが、どの方向を向きながらでも同じ強度でプレイされています。
今回はこの処理についての記事になります。


用意編
1 : AnimMontage を用意する
今回は銃を発砲した際の反動の AnimMontage を作成します。AnimMontage 作成方法ですが、alweiさんが詳しく解説してくださってますのでこちらの説明は丸投げします。
unrealengine.hatenablog.com


2 : Blueprint に AnimMontage をプレイさせる処理を組み込む
まず、Event BeginPlay やなんやらで meshAssign されている AnimationBP にキャストし、それを変数にします。こんな感じ。
f:id:ikagamedev:20170223010416p:plain

そうしたら AnimInstance を引数に取る、 MontagePlay 関数で AnimMontage がプレイできるようになります。
f:id:ikagamedev:20170223010232p:plain


3 : AnimationBP で Blend を行う
f:id:ikagamedev:20170223012225p:plain


簡単に説明させていただきます。 BS_Walk_Jog_Run と書かれているものは、BlendSpace といって複数の Animation を一つの Animation にまとめ、X, Yのパラメーターで遷移させるといったものです。Editor自体はこんな感じ。
www.youtube.com

なお、BlendSpace の作成方法ですがこちらの公式ドキュメントをご覧ください。
docs.unrealengine.com


もうひとつの AO_AimSpace_Iron は、今回の主役である AimOffset です。こちらのEditorは先ほどの BlendSpace のEditorとあまり変わらないので割愛させていただきます。
AimOffset の作り方ですが、かなり面倒なので今回は割愛。代わりに、こちらをご覧いただければ仕組みは把握できるかと思います。
UE4 #5 Add rifle and create aim offset from AimSpace Animation - YouTube
こちらの動画の、3:50あたりから説明されています。

Slot
こちらが AnimMontage を再生するのに必要なものです。


Layered blend per bone

こちらも重要です。 Bone 毎に独立した Animation をプレイさせたいときに使用するものです。こちらを参照。
f:id:ikagamedev:20170223014551j:plain

今回は spine_01 という Bone から上だけに銃の発砲の AnimMontage を適用したかったので、こんな感じになっています。
f:id:ikagamedev:20170223014820p:plain

ここまで実装できれば AnimMontage は再生されます。しかし、問題点が浮上してきます。
www.youtube.com

ふざけている訳ではありません。これが現実です...。元となっている Animation が下のポーズのままのせいで、しっかりと AimOffset に合わせた AnimMontage のプレイができないのです。
f:id:ikagamedev:20170223015552p:plain

実は、これの解決方法はいたってシンプルです。
AnimMontage の元になっている Animation で設定を行っていきます。

1 : Asset Details のAdditive Settings の Additive Anim Type を変更する
f:id:ikagamedev:20170223021002p:plain
No AdditiveMesh Space に変更してください。


2 : 同項目内の Base Pose Type を変更する
f:id:ikagamedev:20170223021012p:plain
NoneSelected Animatin Frame に変更してください。


3 : Base Animation を追加する
f:id:ikagamedev:20170223021020p:plain
こちらを今回は、 Aim_Space_IronSight に変更しています。


以上で完了です。プレイして試してみてください('ω')
お疲れさまでした。 良い Unreal Engine ライフをお過ごしください。

第一次 Add Force / Add Impulse 戦争

f:id:ikagamedev:20170126183530p:plain

みなさんこんにちは。よしだです。
いつかは来るかと思っていましたが来てしまいました。戦争です。
今回は Add Force関数と Add Impulse関数の相違点、それぞれの特徴、使いどころなどについてまとめていきます。


Add Force

f:id:ikagamedev:20170126183633p:plain

おなじみのAdd Force関数です。先ずは、各種パラメーターについて説明していきます。

Target
Add Force関数を実行したいPimitive Componentを入力してあげるためのパラメーターです。(あとのTargetピンの説明は省きます。)


Force
適用させたいForceの値を入力してあげます。ノードの説明だと、オブジェクトを"押す"感じのときに使えばいいらしいです。
Add Forceは断続的(毎フレームなど...)な力を与えることに使われます。


Bone Name
押すTargetがSkeletal Meshだった場合、Boneの名前を指定してあげることでそのBoneにAdd Forceをすることが可能になります。


Accel Change
チェックを入れると、物理的な力(Force)ではなくPrimitive Componentに加速度(Acceleration)を与えることができます。


ex)CharacterのForward VectorからTraceをして、当たったComponentにTick処理でAdd Forceする場合
f:id:ikagamedev:20170126192951p:plain

(FirstPersonCharacter内での処理です。)
ActorのForwardVectorとForce変数を掛け合わせた値をForceに入力しています。この時のForceの値ですが、動かしたいオブジェクトが今回は400Kgあるので...

f:id:ikagamedev:20170126193459p:plain

f:id:ikagamedev:20170126193850p:plain
大体Forceが 400000[N]手前あたりでオブジェクトが動くかと思います。

f:id:ikagamedev:20170126194653g:plain

質量2000kgのオブジェクトは動かせません。。。(Force2000000[N]あたりで動き出します。)

f:id:ikagamedev:20170126195128g:plain


ex2)先ほどとほぼ同様の動きを別処理で

f:id:ikagamedev:20170126195643p:plain

当たったオブジェクトの面法線ベクトルに -1 を掛けてやることで同じような処理が可能です。





Add Force at Location

f:id:ikagamedev:20170126202455p:plain
Add Force関数にLocationパラメーターが追加されただけのものだと思っていいです。ですが、Locationパラメーターが増えたおかげでオブジェクトに対する部分的なForceを与えることが可能になります。

ex)LocationにHitResultのLocationを入力

f:id:ikagamedev:20170126202734p:plain

f:id:ikagamedev:20170126203405g:plain

明らかにAdd Force関数より動きが激しくなっていることがわかるかと思います。

ex2)LocationにHitResultのImpactPointを入力

f:id:ikagamedev:20170128193911p:plain

f:id:ikagamedev:20170128193931g:plain

なんとなくAdd Force < Add Force at Location(Location) < Add Force at Location(Impact Point)の順でForceの量が大きくなる気がします。
なぜLocationよりImpactPointのほうが与える力が大きく見えるかですが、LocationがオブジェクトのRoot(オブジェクト座標系の中心)に対してForceを適用させているのに対して、ImpactPointはTraceがHitしたPointに直接Forceを与えているからだと思います。あまり使い分けが必要なことはなさそうですが、自分がどっちの挙動が今目指しているものなのかを考えて使えばいいと思います。


Add Radial Force

f:id:ikagamedev:20170128200101p:plain

こちらのノードはイメージするなら爆発物などが爆発した際にその近くにいた敵や、物理オブジェクトにRadial(放射状)な力を与えるノードです。

Radius
Forceを適用する範囲

Strength
Forceの大きさ。



f:id:ikagamedev:20170128200048p:plain

マウスのクリックでTraceを飛ばし、当たったオブジェクトに3000000[N]のパワーを与えています。Tick処理で毎フレームパワーを与えているわけではないのでForceの値は大きめです。

f:id:ikagamedev:20170128195952g:plain

ですが上の処理だと、Traceが直接当たらないとオブジェクトにForceを与えることができないので、下のような処理を組むことで爆発物が爆発し、その近くにいた敵に対しForceを与える、といった処理が可能になります。

f:id:ikagamedev:20170128202325p:plain
まず、Traceが当たったpositionにSphere CollisionをAddし、そのCollisionにOverlapしているComponentすべてに対してAdd Radial Force関数を実行してあげてます。

f:id:ikagamedev:20170128202506g:plain



Add Impulse
f:id:ikagamedev:20170201192048p:plain

オブジェクトに単一かつ瞬時な力を与えるノードです。Add Forceが毎フレーム力を与えるのに用いられるのに対し、Add Impulseは一フレームに力を集中させる感じです。
(テストの前日に勉強を急いでやるか、一週間前から勉強をやるかみたいな感じです。(ちなみに私は前者。なのでAdd Impulseです。))
各種パラメーターについて説明します。


Impulse
AddするImpulse(衝撃)の大きさです。Add ForceのForceパラメーターが大きさだけなのに対し、Add Impulseは向きも与えます。


Vel Change
もし、TrueならばImpulseパラメーターに入力された値がImpulseではなく、オブジェクトの直接的な速度(Velocity)になります。
また、オブジェクト固有のmass(質量)は無視されます。


ex)マウス右クリックでRayを飛ばしAdd Impulse(vel change parameter false)
f:id:ikagamedev:20170201193035p:plain
こちらの処理はVel Changeパラメーターがfalseなので質量ベースもとい、物理ベースなImpulseをオブジェクトに与えます。石のブロックは重いので動きません。

f:id:ikagamedev:20170201192839g:plain


ex)vel change parameter true
f:id:ikagamedev:20170201193407p:plain

質量が無視され、直接的な速度になるのでForceの値を1/30にしています。
f:id:ikagamedev:20170201193511g:plain

使いどころですが、ゴルフ、サッカー、野球などの球技系などのゲームを作るときは相性がよさそうです。



Add Impuse at Location
説明割愛.....(Add Force at LocationのImpulse版...)

Add Radial Impulse
割愛...


Add Angular Impulse
f:id:ikagamedev:20170201195630p:plain

簡単に説明すると、ある軸に対する角運動です。詳しくはこちらを参照。
角運動量 - Wikipedia

ちょっとだけ複雑なので詳しく説明します。

Characterの前方向ベクトル(Forward Vector)に対するAngular Impulse
f:id:ikagamedev:20170201201100p:plain

プレイするとこんな感じ。
f:id:ikagamedev:20170201201158g:plain

なぜこんな動きをするんだ?って方のために図で説明すると、
f:id:ikagamedev:20170201201928p:plain
こんな感じになりますw(Y-Upにしています。)

Forwardを軸としての回転なので、実質Roll回転をしているのとほぼほぼ同じです。言い換えると物理ベースで、オブジェクトに回転運動をさせている形です。



Characterの横方向ベクトル(Right Vector)に対するAngular Impulse
f:id:ikagamedev:20170201202529p:plain

プレイするとこんな感じ。
f:id:ikagamedev:20170201202642g:plain
先ほどの図の通りの動きをしますね!いい感じです。
実質的には、Pitch回転をしているように見えます。



Characterの縦方向ベクトル(Up Vector)に対するAngular Impulse
f:id:ikagamedev:20170201202838p:plain

プレイするとこんな感じ。
f:id:ikagamedev:20170201203052g:plain
図の通りですね!
実質的にはYaw回転ですね。


使いどころですがコマ回しとかですかねぇw





まとめ

Add Force断続的な力をオブジェクトに与える。毎フレーム力を与えたりする際に用いる。
Add Impulse単一かつ瞬時な力をオブジェクトに与える。一フレームで力を与えたいときに用いる。
Radial 系は爆発物を視覚化する際に相性がいい。
at Location 系は Impact Point を用いることで、よりリアルな挙動を視覚化することが可能。
Angular は、ある軸に対する Impulse を与える。雪ころがしなど。


以上です。最後まで見ていただきありがとうございました。良い、Unreal Engine Lifeを!

よく使われる処理をUnrealC++で書いてみようの巻。

こんにちくわ。
吉田です。初めましての方が多そうなので自己紹介をさせていただきます。

 年齢は3歳。(18歳です...)
 一応高校生やらせてもらってます。UE4に触れ始めたのは去年の今頃だった記憶が...
 Twitterやってます。更新多めでもかまわないよって方、フォローミー('ω')
Profile : Kai Yoshida

twitter.com

今回は
qiita.comに参加させてもらいましたので、
自分なりに書きたいものを書きたいと思います('ω')
なお、本記事ではUE4 [4.14.0],Visual Studio Community 2015を使用しています。
Visual Studio Community 2015のダウンロードはこちらから。
www.visualstudio.com



今回のテーマはブログの題の通りです。
ごちゃごちゃ言っていても仕方がないので本題に入りましょう。


BeginOverlap,EndOverlap
こちらはBPでもおなじみCollisionの衝突判定のイベントです。
BPだとこんな感じ。

BP :
f:id:ikagamedev:20161211013731p:plain
これを使わずしてゲームは作れないって程重要なノードです。まずはこいつを実装してみましょうか。


まず、自分は今こんな状況にいる!ってシチュエーションを作りましょう。
今回は、ドアの開閉スクリプトをBPじゃなくUnrealC++で作りたくて夜も朝までしか寝れない!ってシチュエーションにしましょう。

1 : DoorClassを作る。
はい。
f:id:ikagamedev:20161211020832p:plain
f:id:ikagamedev:20161211020834p:plain
ActorClassを継承したDoorClassを作りましょう。

コードを書きます。

まずは基本となる、StaticMeshをアサインする処理から。

Door.h

UPROPERTY(EditAnywhere,BlueprintReadOnly,Category = "Door", meta = (AllowPrivateAccess = "true"))
class UStaticMeshComponent* DoorMesh;

UPROPERTY(EditAnywhere,BlueprintReadOnly,Category = "Door", meta = (AllowPrivateAccess = "true"))
class UStaticMeshComponent* DoorFrameMesh;

UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category = "Door", meta = (AllowPrivateAccess = "true"))
class UBoxComponent* DoorTrigger;

ひとつずつ解説していきます。

UPROPERTY([specifier, specifier, ...], [meta=(key=value, key=value, ...)])
 Type VariableName;

UPROPERTY :
UPROPERTYを簡単に説明すると、エンジン内に存在するUPROPERTYマクロを使用した
変数の宣言です。こいつを使うことによってBPClassとの連携が取れます。


specifier :
specifier(指定子)はUPROPERTYマクロを宣言する際に記述することによって、宣言子に指定子を追加しさまざまな振る舞いを持たせることができるものです。
公式ドキュメントにくわーーーしく載っています。
docs.unrealengine.com

meta :
metaを一言で説明すると、パラメータの編集を制御できるようにするためのもの。です。
Dv7Pavilionさんが詳しく説明してくれてますので、詳しくはリンクをクリック
qiita.com


2 : ソースファイルのコンストラクタにComponentを追加し、階層を設定

Door.cpp

ADoor::ADoor()
{
        DoorMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Door"));
	DoorFrameMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorFrame"));
	DoorTrigger = CreateDefaultSubobject<UBoxComponent>(TEXT("DoorTrigger"));

	//	アタッチ
	RootComponent = DoorFrameMesh;
	DoorMesh->SetupAttachment(DoorFrameMesh);
	DoorTrigger->SetupAttachment(DoorMesh);

}

Componentを追加はCreateDefaultSubobject,Attachに関してはSetupAttachmentです。
詳しく説明すると、今回の主題とはかけ離れてしまうので各自よろしくお願いします...


3 : Begin,EndOverlap関数の定義

Door.h

        // BeginOverlap
	UFUNCTION(BlueprintCallable, Category = "Door", meta = (AllowPrivateAccess = "true"))
	void OnDoorTriggerBeginOverlap(class UPrimitiveComponent* HitComp, class AActor* OtherActor,
		 class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, 
			 const FHitResult & SweepResult);

	// EndOverlap
	UFUNCTION(BlueprintCallable, Category = "Door", meta = (AllowPrivateAccess = "true"))
	void OnDoorTriggerEndOverlap(class UPrimitiveComponent* HitComp, class AActor* OtherActor,
	class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

なお、関数の引数ですがこれからのバージョンアップで増えたり減ったりすると思うので、あくまで動作を保証するバージョンは4.14.0
ということにさせてください。
UFUNCTIONマクロですが、BPClassと連携が取れる(関数Ver)とでも思っておいてください。

Door.cpp

        // OverlapEvent Override
	DoorTrigger->OnComponentBeginOverlap.AddDynamic(this, &ADoor::OnDoorTriggerBeginOverlap);
	DoorTrigger->OnComponentEndOverlap.AddDynamic(this, &ADoor::OnDoorTriggerEndOverlap);

AddDynamicヘルパーマクロというものを用いることで、自動的に関数名の文字列を生成してくれるそうです。
これによってOverlapEventのBindingは完了です。
あとは関数を呼び出して好きなタイミングで使えばOK

Door.cpp

void ADoor::OnDoorTriggerBeginOverlap(class UPrimitiveComponent* HitComp, class AActor* OtherActor,
class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep,
	const FHitResult & SweepResult)
{
        // 好きな処理を書いてください。
}

void ADoor::OnDoorTriggerEndOverlap(class UPrimitiveComponent* HitComp, class AActor* OtherActor,
class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	// 好きな処理を書いてください。
}

Full Code

Door.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

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

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

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

	//	BeginOverlap
	UFUNCTION(BlueprintCallable, Category = "Door", meta = (AllowPrivateAccess = "true"))
	void OnDoorTriggerBeginOverlap(class UPrimitiveComponent* HItComp, class AActor* OtherActor,
		 class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, 
			 const FHitResult & SweepResult);

	//	EndOverlap
	UFUNCTION(BlueprintCallable, Category = "Door", meta = (AllowPrivateAccess = "true"))
	void OnDoorTriggerEndOverlap(class UPrimitiveComponent* HItComp, class AActor* OtherActor,
	class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

	//	Add primitive
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Door")
	class UStaticMeshComponent* DoorMesh;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Door")
	class UStaticMeshComponent* DoorFrameMesh;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Door")
	class UBoxComponent* DoorTrigger;

};


Door.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "SideShooterCpp.h"
#include "Door.h"


// Sets default values
ADoor::ADoor()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	//	Create the Object
	DoorMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Door"));
	DoorFrameMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorFrame"));
	DoorTrigger = CreateDefaultSubobject<UBoxComponent>(TEXT("DoorTrigger"));

	//	Attach process
	RootComponent = DoorFrameMesh;
	DoorMesh->SetupAttachment(DoorFrameMesh);
	DoorTrigger->SetupAttachment(DoorMesh);

	//	OverlapEvent Override
	DoorTrigger->OnComponentBeginOverlap.AddDynamic(this, &ADoor::OnDoorTriggerBeginOverlap);
	DoorTrigger->OnComponentEndOverlap.AddDynamic(this, &ADoor::OnDoorTriggerEndOverlap);

}

// Called when the game starts or when spawned
void ADoor::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called every frame
void ADoor::Tick( float DeltaTime )
{
         // 扉が開く処理
}

void ADoor::OnDoorTriggerBeginOverlap(class UPrimitiveComponent* HItComp, class AActor* OtherActor,
class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep,
	const FHitResult & SweepResult)
{
        // 好きな処理を書いてください。
}

void ADoor::OnDoorTriggerEndOverlap(class UPrimitiveComponent* HItComp, class AActor* OtherActor,
class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
        // 好きな処理を書いてください。
}

・おまけ
BeginOverlap,EndOverlapをイベントにしBPへ公開したい場合は

Door.h

 // BeginOverlap
 UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "Door", meta = (AllowPrivateAccess = "true"))
 void OnDoorTriggerBeginOverlap(class UPrimitiveComponent* HItComp, class AActor* OtherActor,
 class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, 
 const FHitResult & SweepResult);

 // EndOverlap
 UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "Door", meta = (AllowPrivateAccess = "true"))
 void OnDoorTriggerEndOverlap(class UPrimitiveComponent* HItComp, class AActor* OtherActor,
 class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

Door.hのBegin,EndOverlap関数のUFUNCTIONにBlueprintImplementableEventを追加し、Door.cppの関数の定義を消せばOKです。
コンパイルしてDoorClassを継承したDoorBPClassを作り、

f:id:ikagamedev:20161218000642p:plain
自分だけのイベントを作っちゃいましょう('ω')

これでOverlapEventに関しては終わり!
お次はAnimBPをC++から制御する処理についてです。

Animation
仕様 :
マウスの右クリックでADS(銃のサイトを覗き、安定させること)のAnimationをさせる

1 : AnimBPを作成する。
f:id:ikagamedev:20161211150739p:plain
ここでひとつ注意です。必ず AnimInstanceClass を継承したAnimBPClassを作成してください。
名前はなんでもOkです。


2 : AnimInstanceClassを継承したC++Classを作る
f:id:ikagamedev:20161211152654p:plain


f:id:ikagamedev:20161211152805p:plain
わかりやすい名前で作成の後、コードを書いていきましょう。
今回は TPSAnimInstance という名前で作成しました。



3 : 変数宣言

TPSAnimInsatnce.h

UCLASS(transient,Blueprintable,hideCategories=AnimInstance,BlueprintType)
class SGRAYSHOOTER_API UTPSAnimInstance : public UAnimInstance
{
GENERATED_UCLASS_BODY()

public:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Combat")
bool bIsAiming;
};


4 : コンストラクタで値の初期化処理

TPSAnimInstance.cpp

UTPSAnimInstance::UTPSAnimInstance(const FObjectInitializer& ObjectInitializer)
  : Super(ObjectInitializer)
{
  // ここに変数の初期値を設定します。
  bIsAiming = false;
}

Full Code :

TPSAnimInstance.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "Animation/AnimInstance.h"
#include "TPSAnimInstance.generated.h"

/**
 * 
 */
UCLASS(transient,Blueprintable,hideCategories=AnimInstance,BlueprintType)
class SIDESHOOTERCPP_API UTPSAnimInstance : public UAnimInstance
{
  GENERATED_UCLASS_BODY()
	
public:
  UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Combat")
  bool bIsAiming;
};

TPSAnimInsatnce.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "プロジェクトの名前.h"
#include "TPSAnimInstance.h"

UTPSAnimInstance::UTPSAnimInstance(const FObjectInitializer& ObjectInitializer)
  : Super(ObjectInitializer)
{
  // ここに変数の初期値を設定します。
  bIsAiming = false;
}


5 : 使用しているCharacterClassで関数、変数を制作

ThirdPersonCharacter.h

UCLASS(config=Game)
class ThirdPersonCharacter : public ACharacter
{
   GENERATED_BODY()

   UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category = "Combat")
   class USkeletalMeshComponent* myMesh;

protected:
   void Aim();
   void StopAim();
};


6 : CharacterClassのコンストラクタで初期化処理

AThirdPersonCharacter::AThirdPersonCharacter()
{
   // 上にごちゃごちゃ書いてあると思いますが気にせず。
   myMesh = GetMesh();
}


7 : PlayerInputComponentを設定

f:id:ikagamedev:20161211171954p:plain
BPからバインドされたインプットイベントを呼ぶにはノード一つで対応できますがC++だと少しだけ手間が必要になります。

(1) : ThirdPersonCharacterのソースファイルにコードを書きます。
ThirdPersonCharacter.cpp

void AThirdPersonCharacter::SetupInputComponent(class UInputComponent* PlayerInputComponent)
{
   PlayerInputComponent->BindAction("ADS",IE_Pressed, this, &AThirdPersonCharacter::Aim);
   PlayerInputComponent->BindAction("ADS",IE_Released, this, &AThirdPersonCharacter::StopAim);

PlayerInputComponentのBindAction,BindAxis関数(今回使っていませんが皆さんのCharacterClassにあると思うので説明させていただきます。)ですが、
引数はこのようになっています。

void AYourCharacterClassName::SetupInputComponent(class UInputComponent* PlayerInputComponent)
{
   PlayerInputComponent->BindAction(FName ActionName, EInput KeyEvent, AYourCharacterClassName *Object, void (AYourCharacterClassName:: *Func));
   PlayerInputComponent->BindAxis(FName AxisName, AYourCharacterClassName *Object, void (AYourCharacterClassName:: *Func));
}

BindAction :
FName ActionName : Project Settings->
Inputに設定したイベントネームのことです。今回は "ADS"という名前のインプットアクションです。

f:id:ikagamedev:20161211171940p:plain

EInput KeyEvent : 列挙型でKeyEventが6つ定義されています。
IE_Pressed : キーが押された時実行
IE_Released : キーが離された時実行
IE_Repeat : キーが押されている限り実行(ですが、他のキーの割り込みが入ると中断されるそうです。)
IE_DoubleClick : キーがダブルクリックされた時実行
IE_Axis : キーのAxisValueを送る
IE_MAX : すみません、不明です...。


AYourCharacterClassName *Object :
現在、使用しているCharacterClassを参照してあげてください。今回は自分自身なので this です。

void(AYourCharacterClassName:: *Func) :
キーを押したとき,離した時に呼ぶイベントを参照してください。今回はAim,StopAim関数を呼んでいます。


8 : Aim,StopAim関数を実装

ThirdPersonCharacter.cpp

void AThirdPersonCharacter::Aim()
{
   // もしmyMeshがnullならreturn
   if(!myMesh) return;

   // UTPSAnimInstanceのAnimationを定義し、myMeshにアサインされているAnimBPにCast
   UTPSAnimInstance *Animation = 
     Cast<UTPSAnimInstance>(myMesh->GetAnimInstance());

   // もしCastに失敗したらreturn
   if(!Animation) return;
   
   // UTPSAnimInstanceのbIsAimingにtrueを代入
   Animation->bIsAiming = true;
}

void AThirdPersonCharacter::StopAim()
{
   // もしmyMeshがnullならreturn
   if(!myMesh) return;

   // UTPSAnimInstanceのAnimationを定義し、myMeshにアサインされているAnimBPにCast
   UTPSAnimInstance *Animation = 
     Cast<UTPSAnimInstance>(myMesh->GetAnimInstance());

   // もしCastに失敗したらreturn
   if(!Animation) return;
   
   // UTPSAnimInstanceのbIsAimingにfalseを代入
   Animation->bIsAiming = false;
}

Full Code :

ThirdPersonCharacter.h

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GameFramework/Character.h"
#include "ThirdPersonCharacter.generated.h"

UCLASS(config=Game)
class AThirdPersonCharacter : public ACharacter
{
   GENERATED_BODY()
        
   UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category = "SkeletalMesh",meta = (AllowPrivateAccess = "true"))
   class USkeletalMeshComponent* myMesh;

protected:
   void Aim();
   void StopAim();
};


ThirdPersonCharacter.cpp

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

#include "YourProjectName.h"
#include "ThirdPersonCharacter.h"
#include "TPSAnimInstance.h"

AThirdPersonCharacter::AThirdPersonCharacter()
{
   myMesh = GetMesh();
}

//////////////////////////////////////////////////////////////////////////
// Input

void AThirdPersonCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
   // set up gameplay key bindings
   PlayerInputComponent->BindAction("ADS", IE_Pressed, this, &AThirdPersonCharacter::Aim);
   PlayerInputComponent->BindAction("ADS", IE_Released, this, &AThirdPersonCharacter::StopAim);

}

void AThirdPersonCharacter::Aim()
{
   if (!myMesh) return;
   UTPSAnimInstance *Animation =
	Cast<UTPSAnimInstance>(myMesh->GetAnimInstance());

   if (!Animation) return;
   Animation->bIsAiming = true;
}

void AThirdPersonCharacter::StopAim()
{
   if (!myMesh) return;
   UTPSAnimInstance *Animation =
	Cast<UTPSAnimInstance>(myMesh->GetAnimInstance());

   if (!Animation) return;
   Animation->bIsAiming = false;
}

ここまできたらAnimBPに飛びましょう。(コンパイルを忘れずに!)


9 : AnimBPでParentClassを再定義

f:id:ikagamedev:20161211201720p:plain

f:id:ikagamedev:20161211201729p:plain

変更が完了したらBPをコンパイルして下さい。

ちゃんと動作してるかテストしてみましょうか。

f:id:ikagamedev:20161217225035p:plain

画面にtrueと表示されていればOKです。


10 : 実際にプレイヤーにAnimationさせる

・まずはStateMachineを用意します。なお、StateMachineの説明は本題からそれてしまうので、
docs.unrealengine.com

こちらをご覧ください('ω')

f:id:ikagamedev:20161217225707p:plain

・StateEditorに飛んだら新しいStateを用意しましょう。
f:id:ikagamedev:20161217230032p:plain
名前はなんでもいいと思いますが、今回は " idle " という名前のStateと " Aiming " という名前のStateを作ります。


・idle内に " Idle_Rifle_Hip " を追加してあげます。
f:id:ikagamedev:20161217230338p:plain


・お次はAimingStateを追加します。
f:id:ikagamedev:20161217230439p:plain

Aiming内に " Idle_Rifle_Ironsights " を追加します。
f:id:ikagamedev:20161217230609p:plain


・遷移ルールを設定します
"idle" - "Aiming"
f:id:ikagamedev:20161217231547p:plain


"Aiming" - "idle"
f:id:ikagamedev:20161217231633p:plain


ちなみにUPROPERTYのCategoryはこんな感じで活躍できます。(便利!)
f:id:ikagamedev:20161217231915p:plain


・Test!!
f:id:ikagamedev:20161217233133p:plain

Characterがこんな感じでAnimationしていればOKです('ω')

Animation編・・・END!!




[LINETRACE]

おなじみのLineTraceです。
f:id:ikagamedev:20161217233717p:plain


銃の発射処理やikなどにも使われているこの処理をUnrealC++でやってみましょう。

1 : ObjectClassを継承したClassを作る

f:id:ikagamedev:20161220224223p:plain

今回は MyStaticLibrary という名前のClassを作りました。


2 : LineTrace関数を定義する

MyStaticLibrary.h

public:
	/** LineTrace */
	static FORCEINLINE bool Trace(
		UWorld* World,
		AActor* ActorToIgnore,
		const FVector& Start,
		const FVector& End,
		FHitResult& HitOut,
		ECollisionChannel CollisionChannel = ECC_Pawn,
		bool ReturnPhysMat = false)

FORCEINLINEマクロ
コードを強制的にインライン化するものです。(inlineの強いVerかな...?)
inlineについてはこちらを参照。
インライン関数 - Wikipedia


3 : 関数の中身を書いていく

MyStaticLibrary.h

public:
	/** LineTrace */
	static FORCEINLINE bool Trace(
		UWorld* World,
		AActor* ActorToIgnore,
		const FVector& Start,
		const FVector& End,
		FHitResult& HitOut,
		ECollisionChannel CollisionChannel = ECC_Pawn,
		bool ReturnPhysMat = false)
        {

        if (!World)
	{
	    return false;
	}

	FCollisionQueryParams TraceParams(FName(TEXT("VictoreCore Trace")), true, ActorToIgnore);
	TraceParams.bTraceComplex = true;
	//TraceParams.bTraceAsyncScene = true;
	TraceParams.bReturnPhysicalMaterial = ReturnPhysMat;

	//Ignore Actors
	TraceParams.AddIgnoredActor(ActorToIgnore);

	//Re-initialize hit info
	HitOut = FHitResult(ForceInit);

	//Trace!
	World->LineTraceSingleByChannel(
		HitOut,
		Start,
		End,
		CollisionChannel,
		TraceParams
	);
	return (HitOut.GetActor() != NULL);
	}


・FCollisionQueryParams
Collisionの衝突判定に関する情報がいろいろと入っています。
今回の処理で言えばbTraceComplex(trueなら元のmeshのCollisionから衝突判定を取り、falseならmeshに割り当てられているcollisionから衝突判定を取る),
bReturnPhysicalMaterial(PhysicalMatを返すか否か),IgnoreActor(Traceを無視するActorを指定できる)などです。便利...


・FHitResult
おなじみのヒットリザルトです。


4 : コンストラクタの定義

MyStaticLibrary.cpp

UMyStaticLibrary::UMyStaticLibrary(const FObjectInitializer& ObjectInitializer)
  : Super(ObjectInitializer)
{
    //変数の初期化とかしてね!!
}


ではCharacterClassに戻りLineTraceを呼び出す関数を定義し、PlayerInputComponentのBindActionでInputActionをBindしましょう!


5 : CharacterClassにLineTrace関数を定義

※ThirdPersonCharacterを使っていると仮定してのコードです。

ThirdPersonCharacter.h

UCLASS(config=Game)
class AThirdPersonCharacter : public ACharacter
{
   GENERATED_BODY()

protected:
   void LineTrace();

}

他にいろいろ書かれていると思いますが気にしなくて大丈夫です。


6 : InputActionを設定
一度UE4のEditorに戻りInputActionを設定しましょう。
今回は "Fire" という名前のActionをマウスの左ボタンに割り当てました。こんな感じ
f:id:ikagamedev:20161221000741p:plain



7 : cppにMyStaticLibraryをincludeし関数の呼び出し

ThirdPersonCharacter.cpp

#include "MyStaticLibrary.h"

AThirdPersonCharacter::AThirdPersonCharacter()
{

void AThirdPersonCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
   PlayerInputComponent->BindAction("Fire",IE_Pressed, this, &AThirdPersonCharacter::LineTrace);
}

void AThirdPersonCharacter::LineTrace()
{
   ACharacter* myCharacter = UGameplayStatics::GetPlayerCharacter(AActor::GetWorld(),0);
   const FVector Start = myCharacter->GetActorLocation();
   const FVector End = Start + myCharacter->GetActorRotation().Vector() * お好きな距離(5000とか);
   
   FHitResult HidData(ForceInit);

   if(UMyStaticLibrary::Trace(AActor::GetWorld(), myCharacter, Start, End, HitData) && HitData.GetActor())
   {
      //お好きな処理をどぞ!!
   }
}

・おまけ
Traceが当たった時にBlueprintでEventを呼び出すものを "実装" してみましょう。

ThirdPersonCharacter.h

UCLASS(config=Game)
class AThirdPersonCharacter : public ACharacter
{
   GENERATED_BODY()


public:
   UFUNCTION(Category = "Trace", BlueprintImplementableEvent, BlueprintCallable)
   void OnTraceHit(FVector ImpactPoint, AActor* HitActor);


・次に先ほどの //お好きな処理をどぞ! の部分に先ほどの関数を呼び出してあげるだけで....
ThirdPersonCharacter.cpp

{
   HitPoint = HitData.ImpactPoint;
   AActor* HitActor = HitData.GetActor();
   OnTraceHit(HitPoint,HItActor);
}

f:id:ikagamedev:20161221002128p:plain

f:id:ikagamedev:20161221002134p:plain

こんなことが可能に....素晴らしい。


以上で本ブログは終了になります。張り切ってかなり長く、見にくくなってしまい申し訳ありません...。
間違っている箇所がありましたら修正依頼をお願いします早急に直します。

いよいよ明日はクリスマスイブ、ぼっちを決め込む皆さんに demuyan さんの 「 何か裏っぽいネタを書きます 」が癒してくれることを願って終わりとさせていただきます。

Widget Interactionを使ってみよう

みなさまどうもどうもお久しぶりでございます。

本日は要望がありましたので、Widget Interactionの簡単な使い方と概要について話していきたいと思います。

 

今回やること....

          Buttonにカーソルが合っている状態で、クリックするとJump,FireEventを呼ぶ...といったものです。うん、シンプル。まずは動画を見てください。


[UnrealEngine4] Widget Interaction

 

 

1 : まずは何も考えずに指定されたクラスを作ります

(1)WidgetBlueprintClassを3DWidgetという名前で作成してください。

f:id:ikagamedev:20161031232040p:plainf:id:ikagamedev:20161031232059p:plain

 

(2)ActorBlueprintClassをWidgetActorという名前で作成してください。

f:id:ikagamedev:20161031232000p:plain

 

とりあえず、制作するクラスは以上になります。処理を組んでいきましょう。

 

2 : WidgetBlueprintClass内での処理から組んでいきます。

(1)WidgetEditorに画像の用にComponentを配置してください。(画像と同じように、Renameした方が後々救われるかもしれません。)

f:id:ikagamedev:20161031233620p:plain

 

(2)なにも考えずに画像の通りにノードを配置して下さい。

f:id:ikagamedev:20161031233413p:plain

 

では一つ一つ解説をしていきたいと思います。

Event Construct

このEventはこのWidgetがActiveになったとき、すなわちLevel上に配置されたまたはAdd to ViewPortなどで呼ばれた際に呼ばれるイベントです。ここに変数の初期化処理、セット処理などを書くのがいいと思います。

OnPressed

名の通り、Buttonが押されたときに呼ばれるEventです。Button1が押された時はJump、Button2が押された時はFireという関数を呼び出しています。

OnHovered , OnUnHovered

名の通り、Buttonにカーソルなどが重なった時に呼ばれるEventです。この処理では見栄えのための処理となっているので処理内容は割愛します。

 

3 : ActorBlueprintClass内でやること

f:id:ikagamedev:20161031234507p:plain

ComponentにWidgetを追加します。

次に、Detailsの値を少しだけ弄ります。UserInterfaceのWidgetClassに先ほど作ったWidgetClassを指定し、DrawSizeに1920,1080と入力して下さい。

f:id:ikagamedev:20161101000356p:plain

そして、InputのAuto Receive InputをPlayer0にしておいてください。

 

4 : 使用しているCharacter,PawnClassでやること

f:id:ikagamedev:20161031235440p:plain

ComponentにWidgetInteraction,今回の主役を追加してあげます。追加したのちCameraにアタッチしてあげてください。(CameraにWidgetInteractionComponentを追従させてあげるためです。)

 

f:id:ikagamedev:20161031235629p:plain

DetailのInteraction項目を弄ります。今回弄るのは、Interaction DistanceInteraction Sourceです。画像の通りに編集して下さい。

 

Interaction Distance

Rayを飛ばす距離です。はい。

 

Interaction Source

どこからRayを飛ばすかを選択できます。DefaultではWorldですが、FirstPersonCharacterではCenter Screenが一番しっくり来たのでこれで。

 

f:id:ikagamedev:20161101000721p:plain

そしてこちらもAuto Receive InputはPlayer 0で。

 

処理を組んでいきます。

f:id:ikagamedev:20161101000126p:plain

.....................End!!!!!

単純です。Left Mouse Buttonがクリックされたら、Press Pointer Key関数を呼び、リリースされたらRelease Pointer Keyを呼ぶだけです。

 

(あっFire関数(custom event)は作ってください。)

f:id:ikagamedev:20161101001006p:plain

 

あとはWidgetActorをLevelに配置したら、プレイしてみましょう。

お疲れさまでした。

ゲームプレイレビュー in TGS

みなさまどうもお久しぶりです。9月の18日に東京の幕張メッセで行われた東京ゲームショーにいってまいりました。

そこでプレイさせていただいたゲームのレビューをつらつらと書いていきたいと思います。かなり個人的な意見ですので、一意見として受け取っていただけたら幸いです。

 

 

{ゲームスクールコーナー};

こちらのコーナーは全国の専門学校さんや大学さんの生徒さんの作品が展示されているコーナーでした。今流行りの...とでもいいますか、VRの作品が多かった印象でした。親子連れの方が多かった気がしました。

 

1.からくり忍者

こちらの作品はZKOOというデバイスを用いて、手の動きを感知するシステムをうまく使えていたと感じました。ゲームの内容は手を握るとパワーを溜め、離すと溜めた分だけの時間攻撃ができるといったものです。非常に直感的な操作でほんの数分で操作をマスターできるほどです。敵を倒した時に出るエフェクト、サウンドも爽快感あふれるもので何回もプレイしたくなる面白さを秘めている作品だな、と感じました。

Scoreを競うことで、お友達とプレイした際ではさらに楽しめるかと思います。(実際私も友達とスコア競いで盛り上がりましたw)

f:id:ikagamedev:20160920234911j:plain

 

2.HALLOWEEN SHOOTING

こちらの作品はOculusRift DK2とXbox360コントローラーを使って、お化けをどれほど退治できるか..というものを競う作品です。

視点の移動をDK2側で取るのではなく、純粋にコントローラーのパッドで取っているためあまり酔う、といったことはなさそうでした。ただ一つ、残念な点としましてはフレームレートが低く、下手したら酔う方もいるかもしれないという点です。グラフィックはデフォルメされてる感じであまりパフォーマンスをもっていかなそうな感じだったのでPCのスペック不足だとは思いますが少し残念でした。ですが、それを忘れさせてくれるほどにはお化けのモデルがかわいかった印象を受けましたw

f:id:ikagamedev:20160920235040j:plain

 

3.SPACE FIGHTER

こちらはXbox360のコントローラーを使っての画面分割マルチプレイが可能なシューティングゲームです。最大で4人同時プレイが可能なので4人でプレイしてみましたが、それはもう白熱してゲーム会場ということを忘れて楽しんでしまいました...w(周りの方、うるさくてすみません。)

一位になると周りのプレイヤーがポイントがほしいがために私を狙いに来るので逃げ回るのに必死でしたw操作もいたってシンプルで分かりやすかったです。

f:id:ikagamedev:20160920235119j:plain

 

4.Connect

こちらのゲームはスマートフォンでのゲームでした。ゲームのシステムは画面に表示された魔法陣にいくつかの模様を描き、描いた模様に応じてさまざまなアクションが起きる、といった感じです。

スタッフの方に丁寧に操作を教えていただいた後に本編なのですが、このゲーム意外と頭を使う...w出てくる敵によって攻撃パターンが違うのでどのタイミングで攻撃するのか防御するのかなど数ターンで覚える必要があるためやりごたえは抜群。

魔法陣を描くのは慣れてしまえば直感的に描けるので慣れたら楽です。個人的にいろいろとアイデアをもらえた作品でした。

 

f:id:ikagamedev:20160920235147j:plain

f:id:ikagamedev:20160920235212j:plain

 

5.ELEC HEAD

こちらの作品は日本ゲーム大賞2016アマチュア部門の最終審査に進出したというだけあってかなりゲームとしてのできはすごく、プレイしている間は時間を忘れてプレイしてしまいました。ELECという電気ロボットを操作してゴールを目指すといったシンプルな内容なのですが、パズル要素もありアクション要素もありと贅沢な作品でした。サウンド、グラフィックもともにとても凝っているため市販で売ってくれたら買うレベルです。()

f:id:ikagamedev:20160920235238j:plain

 

f:id:ikagamedev:20160920235248j:plain

 

 

6.4D FOLLOWERS

こちらのゲームも日本ゲーム大賞2015を受賞した作品です。作戦フェイズと行動フェイズがあり作戦フェイズでやったことが記憶され、行動フェイズの時に障害物などをよけてゴールを目指すといった感じなのですが、シンプルに面白い。製品版がほしいです。グラフィックもかわいらしい感じでとても好印象でした。

f:id:ikagamedev:20160920235324j:plain

 

{インディーコーナー};

インディーでゲーム開発をしている方々のコーナーでした。どこの作品もクオリティは高い...。

 

1.LAST STANDARD

こちらの作品はサイコダイブというものを用いて、プレイヤーに最適な武器を選びプレイしてもらう、といった内容のゲームでした。最初に10問ほどの質問に答えるのですが、私が真面目に10問答えたところプレイヤーキャラクターがう○ちになってしまい一緒に来ていた知人には笑われ、集中できませんでした。ぜひ、開発者の方にどのような性格の人がう○ちになるのか聞きたいものです。w

グラフィックですが、UnrealEngine4を使用しているため非常にリッチな印象を受けました。操作の面もいい感じでした。

f:id:ikagamedev:20160920235356j:plain

 

 

以上、超個人的なプレイレビューでした。写真を撮れなかった作品もあるので次回、TGSに行く際はなるべく撮れるようつとめたいと思います。