<前文>
またカルロス・ゴーンが逮捕されたのですがFox Business Newsがどのようにこのニュースを伝えているのか見てみました。前回の逮捕の時はたまたま見たのですがその偏向的な内容に大変な怒りを覚えてブログの前文にわざわざその内容と何故私が怒っているかを簡潔に述べたのを覚えています。今回も内容的には同じでした。日本人は外人を差別する。その一例が今回のカルロス・ゴーンの不当な再逮捕である。と紹介されていました。私はカルロス・ゴーンが有罪か無罪であるかに関わらず、日本の検察の捜査は違法であると思っています。思っていますが、それは外人に対する日本人の差別とは思いません。何故なら日本人も同じように捜査されるからです。Fox Business Newsはその事実を敢えて無視して日本人をあえて一塊の存在にしてその日本人は外人を差別していると報道しています。
しかし今回は少しだけ違いがありました。総理大臣が検察の操作方法に対して疑問を持っているが現場の責任者は全く聞く気がない。との新しい情報が付け加えられました。ただしこれは結構な文化の違いから来る誤解を含んでいる問題があり、この新しい情報を付け加えるFox Business News側の理由と目的は、日本人がこの新しい情報から得られる印象とはかなり違います。日本人がこの総理大臣の見解を文字通り理解すれば、せいぜい「善処します。」と同程度の反応で、実際は何もしないと理解します。しかしアメリカ人はそうは捉えません。この現場の捜査官が行政から独立した権限を持っていて総理大臣が何をしようが関係がない。しかし総理大臣の全ての権限を使用してカルロス・ゴーンを助けます。と言う総理大臣側からの検察に対する宣戦布告ぐらいに解釈されます。
ここで最も大切な個所は「現場の捜査官が行政から独立した権限を持っていて総理大臣が何をしようが関係がない。」なんです。これはアメリカと同じなんです。このニュースを聞いたアメリカ人は今まで、Fox Business Newsが日本人は野蛮でルールを守らないと言っていたのに、突然、日本には、アメリカと同じような法律があり、しかもその法律は(世界中の独裁者と違い)総理大臣であろうとも守らないといけないアメリカと同じような文明国であると言い始めたと感じるはずです。
これってアニメで言えば遊戯王Arc-v並みに設定が破たんしていますよ。世の中には遊戯王Arc-vを見て遊戯王カードやってみたいと思う稀有な人もいるかもしれませんが、極一部でしょう。ほとんどの人は遊戯王シリーズもここまで堕ちたのかと見捨て去るだけです。同じ事がこのニュースに関してだけはFox Business Newsにも言える訳です。
もうこれで、日本人は野蛮でルールを守らない存在とレッテルを貼る事は無理だと思われます。と言うわけで安心して作業に戻れます。今週は3章をやります。
<本文>
<目的>
今週は、「Unreal Engine4.xを使用してRPGを作成する」の三章を勉強します。参照のイントロによると、
- プレイヤーのポーン(pawn)を作成します。
- キャラクター、クラスそして敵を定義します。
- アクティブなパーティのメンバーの記録を付けます。
- 基礎的なターン制の戦闘エンジンを作成します。
- スクリーン上でのゲームを起こします。
を勉強するとありました。更に、
あなたがプログラマーでこの本を買った理由が、RPGのためのフレームワーク作成のためのバックグランドをもっと知りたいからなら、この章はあなたのためにあると言えます。
とありました。まさに私が勉強したい内容が書かれているようです。
<方法と結果>
この教科書で勉強するのは初めてなので、どのようにまとめるのがベストかまだ分かりません。ので、だらだらとやった事を書いて行きます。
サンプルコードを解凍して、UE4で開いて見ます。UE4のVersionは4.12.5です。
普通に開けました。
<Step.0>
プロジェクトを作成しようと思ったのですがChapter3のサンプルコードを見てみるとChapter2で作成したコードがそのまま使用されていますので、先週のプロジェクトをそのまま使用します。UE4のバージョンは4.22.0です。UE4.22.0ではvisual studio 2019が使用出来るらしいのですがvisual studio 2017で行きます。
1. プレイヤーポーン(player pawn)の作成
まず最初にプレイヤーポーン(player pawn)を作成するそうです。ただしこの本が書かれた当時はCharacterクラスがまだなかった可能性がありますのでその辺は後で調整が必要になると考えられますが、まずは教科書通りに作成します。
かなり重要な内容が書かれていたのでそれをまずまとめます。
Pawnはキャラクター表現、物理、そしてキャラクターのレンダリングを扱います。
プレイヤーコントローラー(Player Controller)はプレイヤーのインプットを変換してプレイヤーが望むポーンの行いを作成します。
教科書によると、プレイヤーポーン(player pawn)の役割は二つあります。キャラクターの作成とプレイヤーがそのキャラクターの操作が出来る事です。それぞれをPawnクラスとプレイヤーコントローラー(Player Controller)が担当するそうです。
こんな役割分担がそれぞれのクラスにあったとは!BPでよくPlayerControllerノードを良く分からないまま繋げていましたが上記の役割分担があったからだったのか!
1.1 Pawnの作成
<Step.1 RPGCharacterクラスの作成>
C++ウィザードを開きCharacterクラスから派生してRPGCharacterクラスを作成します。やっぱりCharacterクラスを使用するのですね。教科書には「Characterクラスにはフィールドプレイヤーのために我々が使用出来る沢山のビルドイン関数があるから。」と説明されていました。
<Step.2 RPGCharacter.hファイルにコードを追加>
教科書には「以下に示すコードに基づいてRPGCharacterを書き直してください。」とありましたので書き足していきます。
RPGCharacterクラスはプライベートでした。念のためにサンプルコードも見たのですが、
こっちもプライベートでした。ウーン…作り直しますか。
UE4C++を作り直す時は我流で直していましたが、最適なやり方を常に実行したが楽なので調べてみます。このサイトにやり方が載っていました。これでやってみます。
このやり方で出来たみたいです。
Privateで作成し直しました。
以下に示すように今度はPrivateでRPBCharacterをCharacterクラスから作成しました。
それでは教科書のコードを追加していきます。
クラスのspecifierについてはあまり知らないので調べます。UE4の公式のClass Specifiersでは、
- このクラスはconfiguration file(.ini)内にデータを保持する事を許可されている事を示します。
- もしconfigもしくはglobalconfigのspecifierを宣言したクラスのプロパティがあればこのSpecifierのせいでこれらのプロパティがその名のconfigurationファイルに保存されます。
- このSpecifierは全ての子クラスに伝播し無効果する事は出来ません。しかし子クラスはconfig speciferを再宣言し違うconfigNameを提供する事でconfigを変える事は出来ます。
- 一般的なconfigNameの値は”Enigne”、”Editor”、”Input”、そして”Game”です。
とありました。
次に以下の変数を宣言しました。
USpringArmComponentクラスはプレイヤーポーンとカメラを繋ぐスプリングの役割を果たすクラスだったと思います。一応調べて見るとUE4C++のUSpringArmComponentには、
- このコンポーネントはその子が親から固定された距離を維持するように試行します。
- しかしもし衝突があったらその子を後退させて、衝突がなくなってからスプリングを元に戻します。
- 例:その世界で衝突するプレイヤーのための従属するカメラを維持するために”Camera boom”を使用します。
とありました。まあ、合っています。
ちょっと気になる事がこのオブジェクトの宣言にはあります。それはPublicかPrivateか宣言していない事です。今までのUE4C++で宣言した変数は全てPublicかPrivateだったのですが、ちょっと気に成ります。
次に以下の変数を追加します。
UCameraComponentクラスはカメラを作成するためのクラスなはずですが一応、UE4C++のAPIは読んでおきましょう。
- カメラのビューポートとセッティングを表現します。例えばプロジェクションのタイプ、フィールドのビュー、そしてポストプロセスのオーバーライドです。
- カメラのビューのターゲットとして使用されるアクターのためのデファルトの振る舞いでは、付属のカメラのコンポーネントとその位置、回転、そしてセッティングを探します。
プロジェクションのタイプ、フィールドのビューなどは3Dグラフィックのプログラミングを勉強した人にはお馴染みのコンセプトです。ポストプロセスをこのクラスが担当しているのは知りませんでしたが考えてみればそれは効率的です。
このオブジェクトもPublicかPrivateか宣言していません。うーん。
次の変数を追加します。
このクラスはpublicに追加されています。そして単なるfloatタイプですので特に調べる事はないです。ただターンレイトが実際に何を示すのかはまだ分かりませんが。
次にいくつかのメンバー関数が追加されています。
ターンレイトとは、プレイヤーがコントロールするキャラクターのターンするスピードの事みたいですね。
Hファイルの最後は、以下のゲッターを追加しました。
<Step.3 RPGCharacter.cppファイルにコードを追加>
<Step.3.1 コンストラクターの実装>
教科書に書かれているようにまずGetCapsuleComponent()関数をコンストラクターに追加します。
エラーが表示されていますね。直しましょう。
取りあえず試しにビルドしてみると
こんなエラーが出ています。ので、
を追加しました。
エラーが消えて、ビルドも成功しました。
次に、変数BaseTurnRateを初期化しました。
次に以下に示すカメラの設定をします。
うーん。この関数C++ Cookbookで勉強したような気がしますが調べたらなかったです。
UE4C++のAPawnのページに解説がありました。
bUseControllerRotationPitch:
もしTrueなら、もしplayerControllerによってコントロールされているのなら、このPawnのピッチ(pitch)はcontrollerのControlRotationのピッチ(pitch)と一致するためにアップデートします。
残りは省略
こういうのは実際に動かさないと分からない部分があるので完成してから試してみます。
次のコードを足します。
UE4C++のAPIによるとGetCharacterMovemnet()関数は、
UCharacterMovementComponentクラスのオブジェクトを返すそうです。
そこでUE4C++のAPIのUCharacterMovementComponentクラスを調べて見ると、
Remark:
CharacterMovementComponentは、付随したキャラクターのオーナーのための動作のロジックを扱う。歩く、落ちる、泳ぐ、飛ぶ、カスタム化などを含む色々な動作のモードをサポートします。
動作は今の速度と加速度に最も影響されます。加速度はこれまでに蓄積されたインプットされたベクトルに基づいてそれぞれのフレーム毎に加速されます。(UPawnMovementComponent::GetPendingInputVector()を参照)
ネットワークは完全に実装されています(サーバークライアントの結合と予測を含む)。
これを読むとUCharacterMovementComponentクラスは付随しているキャラクターオブジェクトの動作を担当するクラスのようです。
そのクラスの変数であるbOrientRotationToMovementは
Remark:
- もしtrueならば、RotationRateを回転の変化の割合として使用してそのキャラクターを加速している方向へ回転せよ。UseControllerDesiredRotationをオーバーライドせよ。
- 一般的に言えば、貴方は他の設定(例えばキャラクターのbUseControllerRotationYaw)がクリアされているか確認すべきである。
とありました。つまりキャラクターの動作を決定するためには、そのキャラクターが付随するUCharacterMovementComponentクラスから作成されたオブジェクトにアクセスしてその変数であるbOrientRotationToMovementなどの設定を変更する必要があると述べています。
RotationRateの方は、
Remark:
- 一秒毎の回転を変化します。
- UseControllerDesiredRotatiaonかOritentRotationToMovementがTrueの時に使用されます。
- 無限の回転割合とすぐの回転をするなら負の値をセットしてください。
こちらの変数は回転率を決定するためのものみたいです。
次のコードはカメラが衝突した時の挙動を決めているようです。
最初のコードはお馴染みのCreateDefaultSubobject関数ですね。CameraBoomの初期化をしています。
次のコードはCameraBoomをRootComponentに追加してます。
3番目のコード、TargetArmLength()関数は、カメラとキャラクターの距離を指定する変数のようです。USpringArmComponentのAPIでは以下の様な解説がありました。
衝突がない時のスプリングアームの長さ
4番目のコード、RelativeLocationを設定しています。この変数、USpringArmComponentのAPIに載ってないんですね。USceneComponentの変数なんですね。
コンポーネントのその親からの位置
最後のコードbUsePawnControlRotation変数はこのアームをコントローラーに沿って回転させるかどうかを決定してるそうです。カメラが回転しないのにアームは回転させるそうです。
ここまで書いて思い出したんですが、BP 3rd Person Game: Character BP Components | 12 | v4.8 Tutorial Series | Unreal Engineの10分ぐらいの所でBPでカメラをプレイヤーポーンの後ろにセットする方法が説明されていますがこれと同じ事をUE4C++で行っているだけですね。
ウーン。何かBPで出来る事と全く同じ事だけをC++でやるのは…この所は考察でもっと検証したいです。
次はカメラの作成についてのコードです。
正直、まだ、あるのかよ!と驚きましたが、以下に示すコードはコンストラクター内では最後のコードですがその後に更にメンバー関数の実装が続いています。フレームワークとしてこの書き方をして下さいと言うにしてももうちょっと違う書き方はなかったのかな?と思います。例えば、先に挙げたBPの場合の例のBP 3rd Person Game: Character BP Components | 12 | v4.8 Tutorial Series | Unreal Engineみたくキャラクターを作成します。それにカメラが付随出来るようにします。そのためにまずカメラとキャラクターを繋げるスプリングを作成します。みたいに3つに分けてそれぞれ節を作成してそれぞれの節で宣言方法と実装方法を説明した方が断然分かり易いですよね。
最初のコードでCreateDefaultSubobject関数でカメラを初期化して、次のSetupAttachment関数でカメラをアームに接続しています。3行目のコードでカメラの回転とアームの回転を分離しています。最後のコードはカメラのアームに対する角度を指定しています。
<Step.3.2 SetupPlayerInputComponent関数の 実装>
4.22.0のバージョンには上に示したように、Characterクラスから派生したRPGCharacterクラスのSetupAttachment関数内にSuperが使用されていますが、サンプルコード(バージョンは4.12)では使用されていません。Superは親クラスの関数を呼ぶ機能のはずですが、一応調べて見ます。
私が確認したい内容は、
- 一般のC++ではSuperが採用されていない理由
- あえてUE4C++でsuperが採用された理由
- このSetupPlayerInputComponent()関数で親クラスのSetupPlayerInputComponent()関数を呼ばなければならない理由。(昔のバージョンでは呼ばなくてよかった理由)
まず1.ですがこのサイトに答えが載っていました。
めんどくさいので訳しません。要するに使っても良かったのですが、別なので代用出来てしまったので使わなくなったと言うのが真相のようです。C++は多重継承があるのでSuperを使用するとどのBaseからの関数か曖昧になるからSuperを使用しないと思っていたのですがそうではなかったようです。となるとUE4C++で採用しても問題がないので2.の疑問も解決です。
3.の答えと言うか一般的にsuperを使用しなければならない場合についての答えはこのサイトにありました。
もしその関数が値を返して、そして貴方はその元の値にアクセスしたいならSuperを呼ぶ必要があります。
もし貴方がoverrideした関数がvoidなら多分、幾つかの状態が変化するからsuperを呼んでおいた方が賢明です。
分かり易すぎる!言われてみれば当たり前ですが、言われるまでは全く気が付きませんでした。Superを普段から使う他の言語を使用する人には当たり前すぎて疑問にすらならないのかもしれませんね。うーん。
次のコードは以下に示すようにcheck()です。
サンプルコード並びに教科書のSetupPlayerInputComponent()関数のパラメ―ターが以下に示すようにInputComponentですが
バージョン4.22.0の場合は、SetupPlayerInputComponent()関数のパラメ―ターは以下に示すようにPlayerInputComponentなので、
PlayerInputComponentに変更します。
サンプルコード並びに教科書のSetupPlayerInputComponent()関数のパラメ―ターが以下に示すようにInputComponentですが
PlayerInputComponentに変更します。
Checkについては、このサイトに以下の説明がありました。
Check(expression)
- このマクロはこのExpressionを実行します。もしその結果がFalseの宣言だった場合、実行を止めます。
- このExpressionはもしマクロがビルト内(DO_CHECK=1)でコンパイルした場合のみ実行します。
まあ色々難しい事を述べていますが、この変数(expression)が実際に存在しているのかをチェックしていると考えられます。
Checkの上のコメント//Set up gameplay key bindingsはcheckについて述べているのではなくその下のコードを解説していると考えられます。
次のコードをサンプルコードからこぴーしました。ただしInputComponentはPlayerInputComponentに変更しています。
ARPGCharacterがエラーに成ってますね。うーん。理由が分からない。
ARPGCharacterクラスの名前がARPBCharacterになっていた。
流石にこの間違いは恥ずかしい。絶対に直さなければ。
まず、C++ファイルのARPBCharacterの名前をARPGCharacterに直しました。
今度は、ARPGCharacter.hとARPGCharacter.cppファイル内のARPBCharacterをARPGCharacterに置換しました。
ARPBCharacter.hをインクルードしているクラスはまだないので、これでコードの直しはいいはずです。そしてセーブした後にすべて閉じました。Visual studioとUE4エディターの両方の事です。
後はいつもの通りにプロジェクトが保存してあるファイルを開いて、.vs、Binaries、Intermediate、Saved、そしてCh2.slnを消してuprojectを開きます。
UEエディターが再生されたら今度はuprojectからvisual studioを開きます。
これで、直ったみたいです。
エラーが消えています。エディターの方もRPGCharacterと正しく表示されています。
これで大丈夫でしょう。アー、びっくりしました。
コードそのものについては馴染みのコードなので解説は省略します。そういえばこれもBPで出来る事をUEC++で行っている例の一つですね。
ただ、project Settings のinput欄にある以下に示した箇所の設定の一部をここで行っているのか、
それともRPGCharacterクラスBPのイベントグラフ内での設定をここで行っているのかが良く分かりません。これも勉強しておく必要がありますね。
次のコードに行きます。
次のコードを追加しました。これもBindAxis()関数ですね。勿論InputComponentはPlayerInputComponentに変換しています。
TurnはAPawnクラスでTurnRateはARPGCharacterクラスなんですね。ま、いいや。ここは掘り下げないで行きましょう。
ビルドも成功しました。次の関数の実装に行きます。
<Step.3.3 TurnAtRate関数の 実装>
次はTurnRate関数の実装です。
これだけです。
UE4C++のAPIにAddControllerYawInput()関数は、
- コントローラーのコントロールの回転にインプット(Yawに影響)を追加します。
- もし、それはローカルのプレイヤーコントローラーならば、この値はプレイヤーコントローラーのInputYawScaleの値を掛けます。
と紹介されています。まずYawの方向ですがこれは散々勉強したUnreal Engine 4 Scripting with C++ Cookbookの12章、Core/ Math API – Rotation using FRotatorで以下の様に説明されています。.
ヨー:そのZ軸はまっすぐ上から下に伸びています。(ヨーは左右にそれを動かします。)
2018-10-14に書かれた私のブログ「12章3節 Core/Math FRotatorを使用した回転」をみると
と書かれていました。
つまり、ヨー(yaw)は、キャラクターの左右の向きを変化させる軸と考えていいようです。
次にAddControllerYawInput()関数のパラメーターについても見てみます。
Val:ヨー(yaw)に追加する量。PlayerControllerのInputYawScaleの値をこの値に掛けます。
つまり、このパラメーターの値がそのままキャラクターの左右への方法変換への変化値になるのではなく、この値×(PlayerControllerのInputYawScaleの値)が、キャラクターの左右への方法変換への変化値になるようです。
ここまで来て大変大切な事に気が付きました。それを説明するのに、まず以下に示したコードの違いをみてください。
- 違う種類の装置を違うように扱うための2つのバージョンの回転のバインドがあります。
- “turn”は絶対的なデルタを提供する装置、例えばマウス、を扱います。
- “turnrate”は変化の割合を扱う装置、例えばアナログのジョイスティック、のためです。
Turnはマウスの操作で使用される回転です。そこでは単純にAddControllerYawInput関数が使用されています。TurnRateはアナログのジョイスティックで使用される回転でそれには今勉強しているTurnAtRate()関数が使用されています。しかしよく見るとこのTurnAtRate()関数内で
単に、AddControllerYawInput関数が使用されているだけでマウスなどでキャラクターを回転させる時に使用するturnとの違いはAddControllerYawInput関数にパスするパラメ―ターのみです。
「これって、単純にturnの場合はキャラクター自身の回転を表して、turnrateの場合は一フレームか一秒間におけるキャラクターの回転を表しているだけでしょう。」
とコードを表面上だけで理解しているとここに潜む大変大きな問題が分からなくなってしまいます。
分かり易く解説します。
もし、turnの場合はキャラクター自身の回転を表して、turnrateの場合は一フレームか一秒間におけるキャラクターの回転を表しているならば、キャラクターの回転は以下のようになるはずです。
上記の図は、鳥瞰図で、キャラクターを上から見ています。Turnの場合はその場でキャラクターが回転するので、キャラクターの位置が変わらないです。Turnrateは違います。キャラクターが前進しながら曲がるためキャラクターの位置も弧を描くように変わっていきます。
これって全く違う回転ですよね。
まず、この解釈が正しいのかどうかを確認しないといけません。
そのためには、このturnとturnrateの違いをはっきりさせないといけません。そしてその違いが何故、rateとRate * BaseTurnRate * GetWorld()->GetDeltaSeconds()なのかについても理解しないといけないです。
と言うわけで今週はここまでにして、来週この部分と、カメラとキャラクターを繋ぐスプリングの設定方法のUE4C++とBPの比較を行いたいと思います。
<考察>
来週やります。