<前文>
コンテクストってどんな意味?
良く和訳されたビジネス書にコンテクストって出てくるじゃないですか。コンテンツじゃないですよ。コンテクスト。Googleで調べると「前後関係」とか「文脈」とか説明されています。しかし和訳された実際の文章でコンテクストを「前後関係」とか「文脈」で置き換えてもよく意味が分からないですよね。
コンテクストって勿論、contextのカタカナ読みです。そしてcontextを日本語に訳せば「前後関係」とか「文脈」になります。なりますが実際の文を読むと意味が分かりません。日本語と英語のある違いからcontextは日本語に訳すと全く理解出来なくなる珍しい単語なんです。
そのある違いとは、英語には一つの単語に沢山の意味がある場合が多いですが、日本語には極少数の例外を除いて一つの単語には一つの意味しかない事です。
分かり易い例を上げると、tipと言う単語です。レストランで使用する場合はチップと言う意味で、ウェイトレスらに渡す「お金」を意味します。しかし本にtipと書かれている場合は「豆知識」と言う意味です。お金と豆知識は全く意味が違います。だからアメリカ人にtipってどういう意味?と前提条件の無い状態で質問すると「それは状況によってです。」としか答えません。だって意味が全く違うんだから。
この「状況によって」と言う意味こそがcontextなんです。
- 「Implementってどういう意味?」「このコンテクスト(context)では、コードを書くって意味。」
- 「ジミーが 「a computerは好きだけとcomputersは嫌い。」って言ってたけど。どう意味が違うの?」「それはコンテクスト(context)が分からないと答えようがないよ。」
- 「fineって「罰金」と「調子が良い」と言う意味は知っているけど?」「このコンテクスト(context)では「非常に細かい(粒子)」と言う意味。」
みたいに基本的にコンテクスト(context)は、単語の意味を説明するのに使用される言葉なんです。
はい。
日本語にそんな全く違う意味を複数持つ単語はないですね。同音な単語は結構ありますが、漢字で書いても同じで意味は違う単語って思い付きますか?
私は「お盆」しか思いつきませんでした。
だから、日本語には基本的にコンテクスト(context)に対応する概念がないんです。無いからcontextを含む分を日本語に訳すと???な文になってしまうんです。
コンテクストってどんな意味?part 2
コンテクストの良い例文がないか調べていたら、コンテクストの意味として「空気を読む」と説明しているサイトがありました。これはかなりの超訳ですが、ある意味的を射ていてかなり感心しました。例えば、今、流行りの自粛ですが、本来の意味では強制的なニュアンスはありませんが、今回の場合は半ば強制です。
- 「自粛ってどういう意味?」「このコンテクストでは絶対やってはいけないって事。」
- 「押すなよって言ってるけど」「このコンテクストでは押せって事よ。」
うーん。完璧ですね。
それでは今週の勉強を始めます。
<本文>
今週は以下に示した先週のバグの直しをして、
- Hierarchical Instanced Static Meshの勉強のやり直し
- Pause画面を開く時は、このSet Game Pausedをオンにすべきなのか?
- スタート画面のキャラが動かせます。
- 半分くらいのNPCは会話が可能な状態でも(!!)マークが表示されていません。
- 現在使用していない機能を実装した箇所は消す。
その後で先週の作業の続きを行っていきます。
1.先週のバグの直し
1.1 Hierarchical Instanced Static Meshの勉強のやり直し
先週の大量の骸骨の表示ですが、
ActorBPクラスから派生したクラス、MyBackgroundSkullsクラス内にHierarchical Instanced Static MeshをComponentとして追加する事で作成しています。
Hierarchical Instanced Static MeshではStatic meshにSkeltonHeadを指定しています。
以下の方法で大量の骸骨を円周上に配置しています。
以下の変数で発生する骸骨の数、骸骨同士の距離、円周のサイズ、骸骨の位置のばらつきをコントロールしています。
骸骨の自転は、以下の方法で実装しました。
骸骨の公転は、Hierarchical Instanced Static MeshをレベルBPから呼び出し回転を追加する事で実現しました。
本来ならばUE4におけるActorクラスにComponentされたstatic meshの回転そのものを厳密に数学として調べたいです。
しかし今回はあくまでデザインが主なので、まず1000個の骸骨がHierarchical Instanced Static Meshを使用して実現する事でCPUに負担になっていない事を確認します。
やっぱり、前に見たこのtutorialが一番分かり易い説明していました。このvideo 内でFor Loopの説明を丁寧に始めたので、こんな初心者向けのTutorialを全部見る必要はない。と途中までしか見ないで作成を始めてしまったのですがやっぱり良く見るべきでした。
まず、このtutorialでは、AddInstance()関数をEvent BeginPlayで呼ばずに、Construction Scriptで呼んでいました。
Construction Scriptで呼ぶ事で、viewport内でHierarchical Instanced Static Meshの全体像が確認出来ます。
更に、それぞれの変数の目を開く事で
Viewportのeditorから値を変更出来る様にしていました。
この方法でやると、どんな風に骸骨が配置されるのかを確認しながら上記の値を変更出来ます。
Instanceの作成方法は、tutorialと全く同じだったのでactorを一個ずつ作成した時のような負担はcpuにはかかってないと思われます。
このビデオにはHierarchical Instanced Static Meshを使用した場合の優位性を測定する方法が紹介されていました。が今回はここまでにしておきます。
まあ、少しずつ勉強する事にします。
1.2 Pause画面を開く時はSet Game Pausedをオンにすべきなのか?
現状どうなっているのかの確認から始めます。
Pause画面を開いたら、幾らマウスを動かしてもカメラは移動しません。Keyboardからの入力も受け付けません。
更に、モンスターも追いかけて来ません。現状で理想の状態が出来ています。
コードを確認したら既にSet Game Pausedが使用されていました。
更に、戻るボタンで、Set Game Pausedが解除されていました。
これはPause画面に限らず、宿屋の会話でも何でもこれと同じにすべきですね。
確認します。
しました。全部なっていました。
1.3 スタート画面のキャラが動かせます。
Set Game Pausedを使用すれば動かなくなるはずです。
カメラも動かなくなりました。
Set Input Mode UI Onlyに変更しました。
今度は、カメラは動きますが、キャラはInputから操作出来ません。これで良いですね。
1.4 半分くらいのNPCは会話が可能な状態でも(!!)マークが表示されていません
今、確認したら、表示はされているのですが暗くて見えない時があるみたいです。
Point lightを追加してみました。
何となく、みんな光っています。
しかしそのお陰で夜は滅茶苦茶見やすいです。
(!!)マークも見やすいです。
町の人なんか特に見やすいです。
今回はPoint lightはそのままにしておきます。後で昼間は消えるようにします。
1.5 現在使用していない機能を実装した箇所は消す。
消しました。
正し、Show Mouse Cursor if Notノードを使用して
以下の箇所を入れ替えようとしましたが止めました。
更に、ワープして別のマップに移動する実装をどうやったのかもう分からなくなっているので、その辺りもいじりませんでした。
この辺は戦闘システムが完成した後で直します。
2. 先週の続き
先週と言うか先々週の続きをやっていきます。
2.1 タイプライター効果の追加
これは、今回のプロジェクトでは作成しません。このRPGの作成が終わった後、Visual NovelをUE4で作成します。その時にタイプライター効果も作成します。
2.2 攻撃ボタンと逃げるボタンしか表示されていない…
2.2.1 コメントの直し
まず簡単なコメントから直します。以下のようにコメントを変えました。
うーん。セリフが飛び出してしまいました。
直しましたが、まだ文章がおかしいです。
最終的にはこうなりました。
2.2.2 ボタンの直し
ボタンの隙間も直しました。
道具を持っていなくて魔法も使えない時の選択ボタンの表示です。
魔法が使用出来る様になった時の表示です。
道具のみが使用出来る様になった時の表示です。
魔法並びに道具が使用出来る時の表示です。
どの場合もボタン同士の隙間は無くなりました。
2.3 戦闘時のモンスターの行動をAIに担当させる
UE4のAIの作り方を完全に忘れてしまいました。
今週はUE4のAIの復習をします。
UE4のC++はUE4C++と書く事にしていますが、UE4のAIをUE4AIと書くと何の事なのか分からなくなりそうなので、UE4_AIと書く事にします。
2.3.1 UE4_AIの勉強した内容を確認する
まずは過去のblogを見て今まで何を勉強したのかを確認します。
確認しました。
2019年に初めてUE4のAIの勉強をしたのですが、その時期はデータサイエンス大流行で機械学習とか深層学習以外はAIじゃないみたいな風潮が凄かったので、必死こいて大学のAIの教科書を読んでいました。だってUE4のAIってIf節しかないんですもん。
そしたら結局、AIってif節の塊で機械学習とか深層学習もどうやってif節を作成するかが違うだけって言う事が分かりました。
それで安心してUE4のAIの勉強を始めたんです。
始めたんですが、結局やったのはこのtutorialの最初の2~3個とMathew Wadstein氏のAIのチュートリアルをこれまた2~3個見ただけでした。しかしそれでもモンスターが自動でプレイヤーのキャラを追いかけるAIが完成したのでそれで完成と言う事になりました。
以上です。
2.3.2 UE4_AIの復習
兎に角、UE4のAIの作り方を完全に忘れているのでtutorialでもう一回勉強します。適当に簡単なtutorialないのかなと探していたらこのtutorialが短くて分かり易そうだったのでこれで復習します。
大体思い出しました。流石に2回目の勉強になると理解も速いです。
Tutorial自体もかなり分かり易い説明でした。一か所だけ不満だったのは、一寸不自然にenumを使用しているように思えた所だけでした。
他のtutorialも見ましたが「コレしてアレして、ハイ出来ました。」的な説明に終始していて「基本」が理解しにくい作りだったので、以下に私が簡単にまとめてみました。
2.3.3 UE4_AIの私なりのまとめ
兎に角、UE4_AIはif節の塊です。普段、
と書いている事を別な方法で書くだけです。
その書き方を以下にまとめます。
2.3.3.1 UE4_AIに必要な4つのクラス
まず、UE4_AIをBPで使用するためには4つのBPクラスが必要です。
<Characterクラス>
Characterクラスから作成しました。
AIにコントロールされるキャラクターが必要なのは当然ですね。
<AIControllerクラス>
AIControllerクラスから作成しました。
以下に示したようにAIControllerクラスは、Controllerの派生クラスです。Player ControllerクラスのAIバーションです。
ユーザーのinput ―> Player Controller―> character
によって操作されるように、AIがコントロールするキャラは、
CPU―> AI Controller―> character
でコントロールされるわけです。
<Behavior Treeクラス>
UE4_AIにおけるIf節を書くための特別なクラスです。
書き方もちょっと特殊で、木構造を使用しています。最初見た時は、まるで子供だましと思いましたが、理解が深まると、こっちの書き方の方が、可視化されているため、複雑な分岐も理解しやすい事が分かりました。
<Blackboardクラス>
黒板クラスです。
さっき勉強したtutorialの説明によると、Behavior Treeクラスは何故か変数の値を保持する事が出来ないんだそうです。
そのためにBehavior Treeクラスで使用する変数の値は別なクラスで保持する必要があるそうです。それをするのがこの黒板クラスだそうです。
まあ、黒板に値を、ちょろっと書いておくと考えると分かり易いですね。
2.3.3.2 それぞれのクラスの繋げ方
クラスが単独で存在していても、それぞれを繋げないとお互いを認識出来ません。繋げ方を以下に示します。
<Characterクラス とAI Controllerクラス>
Characterクラス とAI Controllerクラスを繋げます。
CharacterクラスのBPを開いて、Pawnの所のAI Controller Classに作成したAl Controllerクラスをセットします。この場合はNPC_Controllerをセットしました。
<AI ControllerクラスとBehavior Treeクラス>
Al ControllerクラスのBPを開いて、Run Behavior Tree()関数を呼び出します。そのパラメーターに作成したBehavior Treeクラスをセットします。
上記の例では、NPCBTと言う名前のBehavior Treeクラスがセットされています。
<Behavior TreeクラスとBlack Boardクラス>
Behavior TreeクラスのBPを開くと、以下に示したように、BehaviorTree、BlackBoard Assetと表示されている箇所があります。
そこに作成したBlack Boardクラスをセットします。
これでUE4_AIに必要な4つのクラスが全部つながりました。
2.3.3.3 Behavior Treeクラス内でif節を作成する。
やっとここまで説明出来ました。ほとんどのtutorialでいきなりBehavior Treeの説明から始まるから読者は混乱するんです。少なくとも私は混乱しました。
このBehavior Treeクラスでやる事はただ一つです。「if節を作成する事」だけです。
そしてその具体的な方法はBehaviorTreeのTutorialを見た方が分かり易いと思いますので、私がメモしておきたい事を以下にまとめます。
<変数を作成する>
Behavior Treeクラスの右上のBlackboardをクリックしてNPCBBを開きます。
Blackboardクラス内でNew Keyを押すと変数が作成出来ます。
こんだけです。
<Selectorとsequenceについての考察>
Selectorがif節の大本で、その下に付くsequence達がそれぞれのif節、else if節に対応していると考えられます。
If節のconditionはDecoratorノードで指定する事が出来ます。Blackboardクラスの値を使用する時はBlackboard Based Conditionを選択します。
If節のconditionが正しい時に実行されるコードは、serviceに書かれます。更にその後にtaskを追加する事も可能です。
2.3.4 Game mode クラスの変数とUE4_AI
モンスターの戦闘中の行動をUE4_AIで決定するためには、Game mode内の変数の値をBehaviorTreeにパスして、BehaviorTree内でそのモンスターが取るべき選択をAIに決定させ、その決定した選択をまたGame Modeに伝達する必要があります。今までの復習で得た知識からこれが可能かどうか試してみます。
まず、新しいGame mode baseを作成します。
Attack、Magic、itemのenumeratorを持つEnumを作成します。その変数をCombat Stateと名付けてMy Game Mode内に作成します。
更に、Event DispatchersでAttack、Magic、Itemを作成します。
それぞれのEvent Dispatchersにcustom eventをbindします。
Behavior Treeは以下のように作成しました。
Selector内のsetCombatState内で以下の実装を行います。SetCombatStateはserviceクラスから派生して作成しました。
State KeyはsetCombatState内の変数でタイプは、Blackboard keyです。上記のやり方で、State Keyに値をセット出来るらしいです。
そして、BehaviorTreeに戻って、State KeyをCombatStateにセットします。
かなり手間ですが、これでGameMode内の変数の値が、Blackboardクラスの変数にセットされたはずです。
ここで特殊なのは、Service クラスからblackboardクラスへの値のパスの仕方ですね。Blackboard keyと言う名前の特別なオブジェクトが間に入っています。パスされた値はcopyされたのか単にreferenceされたのかも分かりません。分かりませんが、多分copyされたんでしょうね。住所を写すだけならこんな手の込んだ事をする必要ないですから。
Selectorの説明ですが「左から順に実行していきますが、子供のうちの一つでも成功するとそこで実行を中止する。」と書かれています。この成功すると言う意味が子供のノードのdecoratorの条件にマッチしたと同じ意味ならif節、elseif節と同じ意味になります。
ので、
CombatStateの値がAttackなら、Magicなら、そしてItemならの3つの条件を作成しました。
設定の詳細は以下の通りです。
Decoratorがマッチした時に実行される内容はServiceノードで実装します。
ServiceノードであるAtttack_serviceの内容を以下に示します。
テストします。
出来ていますね。
試しにMagicに変更してみます。
出来ています。
ただし、
Attack Eventは一回、発動すれば良いので、その辺の改良をするための勉強が必要です。
2.3.5 ServiceクラスとTaskクラス
ServiceクラスとTaskクラスの違いがまだ良く分かりません。
結局どっちもIf節のconditionが合致した時に実行される内容です。 TaskクラスにはServiceクラスにしか出来ない何かがあるのでしょうか?
UE4_AIの勉強は、今週はここまでにします。
2.4 戦闘時のキャラのアニメーション表示時でもUIは表示させる
以下に示したように、ユーザーが操作するキャラのアニメーションではUIは消えています。
一方で、モンスターのアニメーションでは
UIが表示されたままです。
UIが表示されたままの方が、見栄えが良いのでユーザーが操作するキャラのアニメーション中もUIが見える様に変更します。
当然ですが、アニメーションが終わった後にUIを再表示させる必要もないので、その部分のノードも外しました。
試してみます。
はい、ユーザーが操作するキャラのアニメーション中もUIが見える様になりました。
2.5 勝利のアニメーション時のカメラ位置を調整する。
カメラの位置を変えるコードを追加しました。
当然ですが、アニメーション終了後に、カメラの位置を戻すコードも追加しました。
テストします。
出来ています。
このスクリーンショットだとしょぼいですが、実際のアニメーションはかなりの迫力があります。
戦闘を速く終わらせるために今回は武器を持たせたのですが、
武器を持っても戦闘中のアニメーションが素手の時と一緒でした。後で直します。
2.6 モンスターの死亡アニメーションの追加
追加します。
Skelton_Swordman_Dieと言うアニメーションがありますのでこれを追加します。
追加しました。
テストします。
素晴らしい死に方です。
ですが、カーソルが消えてしまいました。
2.7 戦闘終了後のカーソルの表示
直します。
色々な個所にShow Mouse Cursor If Not ()関数を追加してみたのですが、戦闘が終了するとカーソルが消えてしまいます。
これはUE4C++のRPGGameModeBaseクラスで何かカーソルの設定をしたかも。と思い調べると、
やっぱりUE4C++のRPGGameModeBaseクラスのTick()関数内のif(FightIsOverIsRead){}内で以下のように設定されていました。コメントアウトしました。
そしたらずっとカーソルが表示されるようになってしまいました。
BPにCall出来る新しい関数を、このRPGGameModeBaseクラス内に作成して、if(FightIsOverIsRead){}内に実装して、その関数をBattleFieldレベルBP内で呼び出して、Show Mouse Cursor If Not ()関数をその後に実装すれば…。
面倒なので、今回はLevelBPのTick()関数に直接、Show Mouse Cursor If Not ()関数を付けちゃいました。
UE4C++はCompileする時間が面倒です。ちょっと直して試しにBuildする事が出来ません。「BPでいいや。」となってしまいます。
テストします。
出来ました。
カーソルはスクリーンショットが取れないのでイメージ図はないです。
3. まとめと感想
今週はこんだけです。AIの残りは後で考えます。先々週にチェックした戦闘システムのバグを直しました。細かい部分はまだ直していないのでそれは来週以降になります。