2015年7月31日金曜日

テンプレートのFirstPerson(4.8.2)を解析してみる C++編

先日はBP版となる、「テンプレートのFirstPerson(4.8.2)を解析してみる」を投稿しましたが、今度はC++版のサンプルプロジェクトを解析してみます。今回の投稿は、前回のBP版の投稿を読んだ後の方が、より分かりやすくて良いかもしれません。

Visual Studioの準備

まずはC++のソースコードをビルドする環境を整えましょう。数年前までは考えられませんでしたが、個人開発であればVisual Studio が無償で使えます。
2015年/07/22日時点、リリースされたばかりのVisual Studio 2015が利用できますが、こちらは未だUE4側が対応していない様でビルドできません。なので、Visual Studio 2013をインストールします。




それではC++版のFirstPersonプロジェクトを見て行きましょう。C++版でも処理を追っていく上で

  1. プロジェクト設定を確認する。
  2. レベルブループリント(LevelBP)を確認する。
  3. レベルに配置されたアクターを確認する。
この流れは変わらないはずなので、まずはここを見ていきます。はじめに、プロジェクト設定の「マップ&モード」のDefault Modesを見てみると、BP版と同様にFirstPerson用のクラスが割り当てられているのがわかります

GameModeがFirstPersonCPPGameModeとなっています。恐らくこれがMainの様なものです。そしてDefaultPawnHUDクラスがグレーアウトされ、設定出来ない様になっています。BP版と異なっていますね。そこで、まずPawnやHUDはどこから設定するの?となるのですが、FirstPersonCPPGameModeのソースコード内で設定されています。


フォルダ構成の違い


プロジェクトのフォルダ構成を比較してみます。がBP版、がC++版です。同じFirstPersonプロジェクトなのですが、C++版の方にはSourceフォルダとプロジェクトのソリューションファイルがあります。BinariesBuildフォルダがありますがこれらはビルドすると作成され中間ファイルなどが入る様になっています。




Sourceフォルダの中身はC++のソースコードが並んでいます。cppとhファイルが対になっていて、1クラス1ファイルになっているみたいです。この中に例のFirstPersonCPPGameModeがありましたので中身を見てみます。




ソースコードの解析



FirstPersonCPPGameMode.cppファイルを開くとコンストラクタの部分のみ実装されています。この中でやっていることが正に、プロジェクト設定でグレーアウトしていたPawnやHUDの設定となっています。DefaultPawnClass にはPlayerPawnClassが割り当てられ、HUDClassにはAFirstPersonCPPHUDクラスが割り当てられています。クラス名にAが付くのがUE4の特徴で、これはおそらくActorを継承しているからかな?なんて個人的には思っています。




さて、AFirstPersonCPPGameModeクラスでプレイヤーとHUDを生成したので、そこからたどってプレイヤークラスを見ていきます。FirstPersonCharacter.cppですね。コンストラクタを見てみます。


なんだか長々と初期化コードが並んでいますが、ひとつずつ見て行きましょう。何事も根気。はじめにGetCapsuleComponentでコリジョンのコンポーネントを取得し、プレイヤーのコリジョンサイズを設定しています。42.fは半径で、96.0fは高さです。

BaseTurnRateはカメラのYaw回転の速度を設定しています。この値を変更すると矢印キーでのカメラの回転速度が変わります。BaseLookUpRateはゲームパッドが接続されている時のみ有効なパラメータで、Pitch回転の速度を設定しています。360コントローラを持っている人は接続して確認してみましょう。

なんなのか分からない不明なパラメータは、恐れず極端な値や0を入れて動作確認すると手っ取り早く理解できます。

カメラコンポーネントの作成


CreateDefaultSubobjectでカメラコンポーネントを作成しています。カメラの名前はFirstPersonCameraとし、親コンポーネントはGetCapsuleComponentとしてプレイヤーのコリジョンコンポーネントを指定しています。RelativeLocationでカメラの位置プレイヤーの腕の辺りになる様調整しています。RelativeLocation親から見た相対的な座標を設定しています。

値が(0, 0, 64)でZが64となっていますが、UE4の座標は、横(X)奥行き(Y)高さ(Z)となっているので間違いではありません。ここは慣れるまで少し時間がかかるかもしれません。


次にbUsePawnControlRotationが有効になるとどうなるのか、試しにfalseに変更して実行してみると・・・変化がありません。というのも、バージョン4.8.2ではこのフラグ、推奨されない機能になっている様で意味の無いものになっています。この辺はVisual Studioでどんどん定義に飛んでいけば分かります。変数や関数の側にDEPRECATED(4.5, "hogehoge")なんて書いてあると「これは無くなる機能だから、使っちゃいけないんだな」と悟れば良いです。



発射する弾の設定


GunOffsetというVector型の変数が初期化されています。この値を変更すると弾の発射位置が変わります。

キャラモデルの設定


FirstPersonプロジェクトでは腕だけのスケルタルメッシュがプレイヤーモデルとなっています。まず「CharacterMesh1P」という名前でスケルタルメッシュクラスを生成します。この時にもCreateDefaultSubobjectが出てきましたね。コンポーネントを生成するにはCreateDefaultSubobjectを使うと覚えておけば良いと思います。次に、生成されたスケルタルメッシュMesh1Pの設定を見ていきます。カメラコンポーネントと同じ様に親や座標を設定しています。影は一人称で要らないので無効になっています。


これでコンストラクタ内の処理は終わりです。




入力処理のセットアップ


そういえば、C++版の入力処理については以前Wikiの方で簡単にですがメモしてありました。宜しければ目を通してみてください。
SetupPlayerInputComponentでは予め設定した仮想キーに対応するイベント(関数)を登録しています。例えばJumpキーを押した(IE_Pressed)時、ACharacter::Jumpが呼ばれる様になります。また、Jumpキーを離した(IE_Released)時にはACharacter::StopJumpingが呼ばれる様になっています。弾はEnableTouchscreenMovementでマウス操作時やモバイル端末などで表示する仮想コントローラが有効な場合に撃てる様になっています。

こんな風に、登録したイベントを事前に登録しておいて、起動すると共にイベントを待機し、起こったイベントに従って処理を実行するのをイベントドリブン型と呼びます。ゲームプログラムではよくある手法かなと思います。




プレイヤーの移動


プレイヤーの移動はMoveForwardで前進後退、MoveRightで左右に移動します。引数のValueには仮想キーに割り当てられたScaleが入っています。



これですね。MoveForwardではGetActorFowardVectorでプレイヤーキャラの向きから見て前方向ベクトルを取得して、Value分移動しています。MoveRightではGetActorRightVectorで右方向ベクトルを取得してValue分移動しています。ラジコン操作と言ってしまえば分かりやすいかもしれません。




視点切り替え


TurnAtRateAddControllerYawInputを使ってPlayerControllerクラスのYaw要素を更新してプレイヤーの視点を横回転させています。

同じ様にLookUpAtRateではAddControllerPitchInputを使ってPlayerControllerクラスのPitch要素を更新して視点を縦回転させています。




弾の発射

弾の発射はOnFire()でに実装されています。弾の発射はProjectileClassが割り当てられている場合に動作する様です。このProjectileClasspublicになっていて、データ専用BPで外部から設定されています。コンテンツブラウザのBlueprintsフォルダにFirstPersonCharacterBPがあるので開いてみます。


弾クラスの設定は、開いたFirstPersonCharacterの詳細を見てみると、Projectileという項目があり、その中にProjectile Classが設定できる様になっています。ここに弾のデータ専用BPであるFirstPersonProjectileが割り当てられているのがわかります。



この割り当てられたアクターをSpawnActorで生成しています。弾を生成した後は、UGameplayStatics::PlaySoundAtLocationで発射音を鳴らし、Montage_Playで弾の発射時の反動アニメーションを再生しています。

この一連の処理は、BP版でいうところのSpawn projectile 処理と同じであることが分かります。見比べてみるのも面白いと思います。BPという新しい言葉を使うにあたって、はじめは戸惑いも生じるかと思いますが、視覚化してわかりやすくしたスクリプトであると考えれば理解しやすいかと思います。例えばBPの赤いノードは処理の開始位置なんですが、これってほぼ関数みたいなものです。




弾の挙動

弾の発射後の挙動はAFirstPersonCPPProjectileクラスにまとめられています。SpawnActorするときにクラスを割り当てているかと思います。データBPのFirstPersonProjectileアクターにAFirstPersonCPPProjectileクラスが割り当てられているのが分かります。


ではAFirstPersonCPPProjectileの中を追っていきましょう。ここはコンストラクタと、OnHit関数があるだけなのでとってもシンプルです。


コンストラクタでは、CreateDefaultSubobjectでコリジョン用のSphereを生成してコリジョンサイズ
や名前、そして衝突時の関数を登録しています。その後、UProjectileMovementComponentで移動用のクラスを生成し各種設定をして、InitialLifeSpanで弾の生存時間を設定したりしています。


弾が地面だったり、キューブだったり、何かに衝突した時はコンストラクタで登録したOnHit関数が呼ばれる様になっています。


引数のOtherActorOtherCompは衝突したアクターとコリジョンが入っています。NormalImpulseは法線の方向に働いた垂直抵抗の大きさです。Hitに名前のFHitResultと名前の通り衝突時の情報が入っています。

処理は、IsSimulatingPhysics()で当たったメッシュの物理が有効か確認して、有効であればAddImpulseAtLocationでメッシュに力を加えて移動しています。最後にDestroyで弾を消して終了です。

照準の表示



弾の照準はFirstPersonCPPHUD.cppファイルのAFirstPersonCPPHUDクラスで処理しています。コンストラクタで照準用のテクスチャFirstPersonCrosshairを読み込んでおいて、DrawHUD関数ではCanvasの中心にDrawItemで照準を描画して終わりです。C++でもここはとってもシンプルですね。

最後に

C++版といっても、BPと似たような記述が沢山あって変わりないことが分かります。UE4はBPだけでもゲームが作れますが、こうやって一度生のソースコードを追うのも良いですね。C++のプロジェクトを解析することでBPの理解が深まるので気が向いた時に試してみると楽しいですよ。







0 件のコメント:

コメントを投稿