UE4の勉強記録

UE4の勉強の記録です。個人用です。

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する 魔法の習得システムの改良

f:id:kazuhironagai77:20201101215749p:plain

<前文>

WebGPU

最近、3D graphicsの業界に疎くなってしまったんですが、その理由は3つあります。

  1. 最新の3D graphics APIは、低レベル化が進んで大学院や大企業で研究開発する人以外勉強しても益がない。
  2. Vulkan、MantleそしてDirectX12の3つが同時に存在しているのは、multi-threadに対応した3D graphics APIの最適解がまだ見つかっていないからと言う気がしている。
  3. 個人が勉強するならばUE4やUnityなどのゲームエンジンの使用方法を勉強した方が役に立つ。

それでWebGPUも名前しか知らなくて「どうせWebGLの後継APIなんでしょ。」と勝手に思っていました。しかし最近WebGPUについてまとめた動画を見たら、単なるWebGLの後継APIと言うよりはVulkan、MantleそしてDirectX12の3つの良いとこ取りをした最新の3D Graphics APIらしいです。

それで突然興味が湧いてきました。しかしこれを勉強するとなるとそれなりに時間と労力が取られるので、理解出来ました以上の成果がないと継続が困難になる事は確実です。(あるいは10倍の簡単さで理解出来るような天才的な教科書が有れば、取られる時間や労力が10分の1になるので知的好奇心を満足させるだけでもいいかもしれません。)

うーん。やっぱり二の足を踏んでしまいますね。

ただ、益はなくても勉強しない事によって害が生まれる可能性もあります。

今回のVtuberの中国における炎上は、私にとって中国人が味方ではなく敵になる可能性について考えさせられました。アメリカにいた時は、同じアジア人ですから中国人は基本的に味方なんです。大学で勉強していても解けない宿題を中国人の友達に聞きに行ったりしたりしていました。今の日本人のソフトエンジニアだって自分で分からなかった時は中国人の同僚に聞けばいいや。と軽く考えている人はいっぱいいると思います。

もしその中国人から明日からお前には何も教えない。と言われたら結構窮地に陥ります。その時に交渉出来る材料としてWebGPUの専門家になっておく。と言うのは有りかなと思います。

うーん。でもそれなら私の場合はUE4の専門家でもいいですね。

それでは、今週の勉強を始めます。

<本文>

前に予定していた作業内容の内、魔法の拾得の改良を行います。

f:id:kazuhironagai77:20201101215847p:plain

具体的に言うと以下の事を考えています。

  1. Talent [Skill] Tree Builderについての勉強 
  2. レベルアップのテストと改良
  3. 神官NPCか巫女NPCの作成
  4. 神殿の作成

をやって行きます。

1. Talent [Skill] Tree Builderについての勉強

f:id:kazuhironagai77:20201101220004p:plain

が無料で配布されていたので、これの使い方を勉強してみます。もし魔法の拾得に使えそうならば使用します。

初っ端からですが、Create Projectになっていてます。既にあるProjectに追加する事は出来るんでしょうか?

1.1 解説を読む

f:id:kazuhironagai77:20201101220029p:plain

Talent/Skill Tree systemってどんなシステムなんでしょうか? Tree diagramの事なんでしょうか?ゲームを普段しない私はこういうゲーム関係の専門用語が良く分かりません。

以下の事が出来ると書かれています。

  1. キャラクターの属性の調整
  2. 名前によるアイテムの解除
  3. タイプによるあるグループのアイテムの解除

キャラクターの属性って何なんでしょうね。ポケモンの火属性とか、水属性みたいなのを指しているんでしょうか?名前によるアイテムの解除も良く分かりませんね。キャラクターの名前によって使用出来るアイテムが変わるんでしょうか?タイプによるあるグループのアイテムの解除は、キャラのタイプ(例えば職業とか?)によって使用出来るアイテムが違うと言う事なんでしょうか?

更に4つのPresetが提供されているとあります。それは、

  1. 伝統的な垂直木
  2. 武器(人工的な?)の才能?
  3. 才能のスイッチャー?
  4. 水平木

垂直木と水平木(vertical tree and Horizontal tree)は、多分、Tree diagramの事を指していると思われます。2と3の才能(talent)は技術(skill)の事を言っているのでしょうか?

中身を見てから検討します。

まず、Tutorialが紹介されています。

f:id:kazuhironagai77:20201101220118p:plain

動画なので後で見ます。

Descriptionの所をみると木の作成方法が説明されています。

f:id:kazuhironagai77:20201101220141p:plain

これだけでは分かりませんね。

まずクラス(例:メイジ)を作成せよとありますが、普通のUE4のクラスにはMageはありません。このPlugInにMage Classが付いているのでしょうか?それともCharacterクラスから派生してMageクラスを作成するのでしょうか?

次に特技(specialization)を作成せよ。とあります。うん。これから推測すると、このPlugInには特別なクラスが付属されていそうですね。

最後に才能(技術?)のdata tableを作成せよ。とあります。これはサンプルを見れば作れそうです。

ここまで読んだだけですが、結構簡単に作成出来そうですね。続きを読んでいきます。

出来る事の一覧が載っています。ここは後で読みます。

以下のBPが含まれているそうです。

f:id:kazuhironagai77:20201101220237p:plain

Tutorialが2つありますね。これらも見てみましょう。

次は、動画を見てみます。

f:id:kazuhironagai77:20201101220302p:plain

この動画は上の説明を映像を交えて説明しているみたいですね。操作の仕方が分からない時に参考にしましょう。

1.2 実際にやってみる

それではTalent [Skill] Tree Builderからprojectを作成してみます。

f:id:kazuhironagai77:20201101220337p:plain

Ch4_3が4.24なので同じVersionで作成しました。

Playボタンを押すと以下の画面になります。

f:id:kazuhironagai77:20201101220358p:plain

30分程度いじってみました。理解した所をまとめます。

f:id:kazuhironagai77:20201101220422p:plain

右のWidgetに書かれていますがTまたはNを押すとTalentsウィジェットが開きます。

f:id:kazuhironagai77:20201101220443p:plain

動画で説明されていたのと同じです。

f:id:kazuhironagai77:20201101220500p:plain

動画ではそれ以外に以下に示したウィジェットが表示されています。それぞれ

Switcher,

f:id:kazuhironagai77:20201101220519p:plain

Horizontal,

f:id:kazuhironagai77:20201101220542p:plain

weapon,

f:id:kazuhironagai77:20201101220602p:plain

と紹介されています。

これらの画面が見たいのですが開けません。

Projectに付属のTutorialなどをやって分かったのですが、以下に示したDT_ClassesデータテーブルのAssaultが今の実装だと選ばれているようです。

f:id:kazuhironagai77:20201101220623p:plain

これをSwitchSampleClassに変更すれば

f:id:kazuhironagai77:20201101220700p:plain

この画面が表示されるはずです。

調べて見るとW_MainWindowウィジェットの以下の部分、GetHeroClass関数がAssaultを返していると分かりました。

f:id:kazuhironagai77:20201101220742p:plain

GetHeroClass関数の実装を見てみると

f:id:kazuhironagai77:20201101220758p:plain

Assaultに固定されていて変更出来ません。以下に示した方法でSwitchSampleClassに変更しました。

f:id:kazuhironagai77:20201101220824p:plain

f:id:kazuhironagai77:20201101220833p:plain

テストします。

f:id:kazuhironagai77:20201101220854p:plain

惜しい。Class Specializaionがありませんと出て来ました。

だったらAssaultのClassTreesを

f:id:kazuhironagai77:20201101220910p:plain

から

f:id:kazuhironagai77:20201101220931p:plain

に変えてみました。

f:id:kazuhironagai77:20201101220948p:plain

うん。ClassTreeだけなら呼び出せますね。同様にHorizontal TreeやWeapon Treeも呼び出せました。

f:id:kazuhironagai77:20201101221034p:plain

f:id:kazuhironagai77:20201101221042p:plain

うーん。ここまであるならなんで表示出来ないですかね。

1.3 色々なTreeを表示させる

ありました。Sample CharacterのDefaultの項目にClassがありました。これをSwitcher Sample Classに変更します。

f:id:kazuhironagai77:20201101221107p:plain

f:id:kazuhironagai77:20201101221122p:plain

他のも試してみます。

f:id:kazuhironagai77:20201101221153p:plain

f:id:kazuhironagai77:20201101221204p:plain

うん。なるほど。大体分かりました。

f:id:kazuhironagai77:20201101221229p:plain

最初に出て来たコレらは、このTree diagramらの事を指していました。調べたらRPGでは、これらをskill tree systemと呼ぶそうです。

今のRPGはレベルが上がると魔法が使えるのではなくて、レベルが上がるとTalent pointが貰えてそれに応じて使用出来る魔法を自分で選んで開放するんですね。

こっちの事実の方が驚きでした。

せっかくここまで使用方法が分かったので、もう少し遊んでみようと思います。

1.4 自分でTreeを作成してみる

TutorialでMageクラスを作成する方法が説明されていたのでやって見ました。

f:id:kazuhironagai77:20201101221257p:plain

何も表示されていませんね。もう少し改良してみます。

f:id:kazuhironagai77:20201101221318p:plain

多少は見やすくなったでしょうか?

まずは、矢印を表示させたいですね。後、クリックしても使用可能になりませんね。

1.4.1クリックして使用可能にするには?

DT_Talentの

f:id:kazuhironagai77:20201101221347p:plain

Test_FireのNeed_SpendPointsを0にする必要がありました。

f:id:kazuhironagai77:20201101221416p:plain

出来ました。

1.4.2 矢印を表示させるには?

f:id:kazuhironagai77:20201101221442p:plain

DT_TalentsのNeed_Parentsにelementを追加するとArrowElementsの項目が出現します。そこで適切な矢印を選択します。

f:id:kazuhironagai77:20201101221503p:plain

これで矢印が表示出来ます。

1.5 他の機能を試してみる

Descriptionには以下の事が出来ると書かれています。全部試すのは無理ですが、幾つか試してみます。

f:id:kazuhironagai77:20201101221532p:plain

1.5.1 Can modify stats [+/-/+%/-%/*]

これのやり方はTutorialに書かれていました。

f:id:kazuhironagai77:20201101221602p:plain

Test_Fireを開放するとHealthが10上がる様にしました。

f:id:kazuhironagai77:20201101221638p:plain

100から110に変化しました。

[-/+%/-%/*]の条件はStatModifier_ModOPで選ぶ事が出来ます。

f:id:kazuhironagai77:20201101221658p:plain

1.5.2 Can unlock abilities [3 sample abilities included]

まず、提供されている3つのサンプル能力とは以下の

f:id:kazuhironagai77:20201101221721p:plain

Heal, Immune, そしてAoEの事です。HealとImmuneはいいですが、AoEって何の事なんでしょうか?

これが、Healですね。

f:id:kazuhironagai77:20201101221742p:plain

これがimmuneです。炎に突っ込んでもHealthが減らなくなりました。

f:id:kazuhironagai77:20201101221806p:plain

最後にAoEです。

Area Of Effectの事だそうです。

f:id:kazuhironagai77:20201101221828p:plain

この範囲で魔法の影響が出ると言う事でしょうか?

作成方法ですがHealを見て推測しましょう。

f:id:kazuhironagai77:20201101221848p:plain

Assaultの場合、上記のtalentを開放するとHealが使用出来るようになります。

DT_Talentを見るとHealing AbilityがAssult_Gunmanの所にありました。

f:id:kazuhironagai77:20201101222049p:plain

そしてA_GM_HealingのTalentBornusesを見るとBonusTypeがUnlockSkillName、UnlockNameがHealになっています。

f:id:kazuhironagai77:20201101222122p:plain

うん。これをそのままコピってみましょう。

M_Fire_testのTalentBornusesをA_GM_Healingと同じにしました。

f:id:kazuhironagai77:20201101222154p:plain

Mageに変更してTalent Tree画面を開き、TestFireを開放します。

f:id:kazuhironagai77:20201101222219p:plain

1を押してHealが使用出来るか試します。

f:id:kazuhironagai77:20201101222240p:plain

出来ました。

今度はBPを見てどのようにSkillが管理されているか調べましょう。

SimpleCharacterのBPではUnlocked Abilities変数が使用出来るskillを管理しているみたいです。

f:id:kazuhironagai77:20201101222326p:plain

正し、このUnlocked Abilities変数はNameの配列なので、どんなTextでも保持出来ますね。

f:id:kazuhironagai77:20201101222419p:plain

Healを発動させるために、1を押す必要がありますが、そこでUnlocked Abilities変数内にHealがあるかチェックしていました。

f:id:kazuhironagai77:20201101222450p:plain

種も仕掛けもない非常に愚直な方法でskillを発動させてますね。

その後でParticleを発動させています。

f:id:kazuhironagai77:20201101222521p:plain

一応、Particle Systemもチェックしておきます。

f:id:kazuhironagai77:20201101222611p:plain

f:id:kazuhironagai77:20201101222620p:plain

もう分かりました。

SkillだけじゃなくてこのPluginの仕組みも大体理解出来ました。

1.6 Talent [Skill] Tree Builderは他のprojectに移植出来るのか?

今までの理解の範囲で語るのならTalentTreeBuilderのフォルダー丸ごとmigrateして

f:id:kazuhironagai77:20201101222654p:plain

SampleCharacterのBPをThirdPersonCharacterに丸コピーすれば出来そうです。

f:id:kazuhironagai77:20201101222741p:plain

f:id:kazuhironagai77:20201101222750p:plain

多少は継承関係を直したり他の関数をコピーしたりする必要はあるかもしれせんが、それで出来そうです。

1.7 今のRPGゲームに使用するのか?

しません。これ完全にオーバーキルな機能です。しかも今から使用するとなると今まで作成した部分に大幅な変更が必要になる可能性があります。

1.8 Talent [Skill] Tree Builder1の総括

以下の事を学びました。

  1. 経験値を自分で振り分けて使用出来るアイテムや魔法を決定出来るというアイデア
  2. UIの表現方法
  3. データの整理の仕方

2と3についてはこれからもTalent [Skill] Tree Builder1を何回も見て参考にしていこうと思います。

2. レベルアップのテストと改良

流石に、Pluginの使い方を見るだけでは物足りないのでもう少しだけやります。

2.1 レベルアップの復習

どうやってキャラクターがレベルアップしているのか覚えていません。その復習からやります。

RPGGameModeBaseクラスのTick()関数内で戦闘勝利後に、敵のモンスターの金貨と経験値を獲得するコードがあります。

その直ぐ後に、獲得した経験値が次のレベルにふさわしいかどうかをチェックするコードが書かれています。

最初に戦士のレベルを管理するデータテーブルSoldierLevelUpを読み込みます。

f:id:kazuhironagai77:20201101222908p:plain

その直ぐ後に、獲得した経験値が次のレベルにふさわしいかどうかをチェックするコードが書かれています。

最初に戦士のレベルを管理するデータテーブルSoldierLevelUpを読み込みます。

f:id:kazuhironagai77:20201101222929p:plain

SoldierLevelUpを開いて見てみると、MagicNameの項目がありました。このデータテーブルを作成した時は、レベルが上がったら即、魔法が覚えられる設定だったのでMagicNameの項目を作成したんでした。

f:id:kazuhironagai77:20201101223004p:plain

そしたら、まだコードは残っていました。

f:id:kazuhironagai77:20201101223041p:plain

思い出してきました。

魔法の名前は日本語にしたいのですが、UE4C++のFStringに直接日本語を読み込ませると、文字化けしてしまったんでした。

因みに、魔法の種類を管理しているデータテーブルは以下の様になっています。

f:id:kazuhironagai77:20201101223109p:plain

うーん。ここに新しい項目、local nameでも作成してRow Name にはfire1, fire2 としてlocal nameには炎(小)、炎(大)とすれば別に神殿云々しなくても出来そうですね。

それはともかくとして、レベルアップに関しては良く出来ていました。特に直す必要はなさそうです。

2.2 魔法の習得の確認

魔法の名前を英語名にすれば、レベルアップ時に魔法も覚えられるはずです。それを確認します。

f:id:kazuhironagai77:20201101223204p:plain

f:id:kazuhironagai77:20201101223212p:plain

レベル2に上がった時にfire1が覚えられるようにセットしました。

テストします。

エラーが出ました。プレイヤーの操作するキャラが、炎(小)の消費魔力以上の魔力があるかどうかをチェックする箇所でした。

f:id:kazuhironagai77:20201101223240p:plain

Fire1を新しくセットしました。

出来ました。レベル2になったら魔法Fire1が使用出来る様になりました。

f:id:kazuhironagai77:20201101223306p:plain

この方法で魔法が習得出来る様になると神殿とか考える必要なくなりますね。こっちで魔法は習得出来るようにします。

3. レベルアップと魔法の習得

FMagicsDataにFStringタイプの変数、MagicLocalNameを追加します。

f:id:kazuhironagai77:20201101223412p:plain

ビルドしました。

あれ、いつもここで一回だけクラッシュしたと思ったんですがしませんね。念のために一回UE4Editorを閉じてやり直します。

データテーブル、MagicsのRow NameとMagic Local Nameに新しい名前を追加しました。

f:id:kazuhironagai77:20201101223437p:plain

テストします。

エラーは起きませんでしたが、魔法の名前がfire1, fire2になっていました。

f:id:kazuhironagai77:20201101223455p:plain

炎(小)、炎(大)に変えます。

f:id:kazuhironagai77:20201101223515p:plain

RowNameをFStringで保存していたので、FString->FText->FNameに変化させる必要がありました。正直この3つのタイプの違いが良く分かっていません。ここでちょっと勉強します。

このサイトに丁寧な説明がありました。

f:id:kazuhironagai77:20201101223534p:plain

理解した範囲で以下にまとめると、

  • FStringが一番コストが掛かるがsearchなどの機能が使用出来る。
  • FTextText Localizationが可能。
  • FNameは何も出来ないがコストが最も低い。

となると単純な結論を言えば、FStringは他のタイプの上位交換と言う事でしょうか。Text Localizationについてはちょっと勉強しないと理解出来ない部分がありそうですが、後で暇なときに勉強します。

テストします。

f:id:kazuhironagai77:20201101223608p:plain

はい。今度は炎(小)、炎(大)が表示されました。

コメント欄を見てみると、Fire1が選択されたと表示されています。これを直します。

f:id:kazuhironagai77:20201101223638p:plain

ウィジェットMagicのBPでデータテーブルのmagicsのmagic Local Nameが何回も使用されるので、FStringタイプの変数、LocalNameOfMagicにデータテーブルのmagicsのmagic Local Nameを保持する事にしました。

f:id:kazuhironagai77:20201101223658p:plain

その変数を適切な個所で使用します。

f:id:kazuhironagai77:20201101223718p:plain

f:id:kazuhironagai77:20201101223726p:plain

テストします。

f:id:kazuhironagai77:20201101223745p:plain

f:id:kazuhironagai77:20201101223753p:plain

出来ました。

4. 今後の課題

4.1 UIとそこに表示されるコメントについて

一応、レベルアップにおける魔法の習得が可能になりました。しかし課題が多く見つかりました。まず戦闘中のセリフはそれぞれのコードの中で管理されていてどこでどんなセリフが呼ばれているのか全く分からないです。一括で管理するシステムを作らないと今日みたいに見直した時に訳が分からなくなります。

コメントの表示方法については、もっと適切なUIがある気がします。戦闘中の画面とポーズ画面、更に普段の会話画面のコメントの表示方法が全部バラバラです。統一した方がユーザーに分かり易いはずです。

コメントの内容についても、改善しなければならない点が沢山あります。まず報連相が必要です。ユーザーはゲームの内部で何が起きているのか全く分かりません。適切な報連相がなければゲームをプレイする事がつまらなくなって継続しなくなるでしょう。

これらの課題については来週、考えてみます。

5. まとめと感想

今週はここまです。来週はLandScapeについてもう少し勉強して、村の地面がデコボコするようにします。

 

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する 村人の会話を管理する3

f:id:kazuhironagai77:20201025213654p:plain

<前文>

やっとvtuberと中国の炎上も落ち着いたみたいです。Redditでこの話題を追いかけていた人にとっては、中国側が土下座して謝る以外の決着はない事は最初から明白でした。しかし結構な数の日本人が右往左往していたのを見ると、それらの人々は普段の口ぶりとは違い、全然英語が出来ない(少なくともRedditを読む事が出来ない。)事が分かり、私はかなり憤慨しました。

ただその英語の情報が全くない状態でvtuberの味方をし続けた人は尊敬に値します。英語の情報が全くない状態では中国からの脅しはそれなりに怖かったと思います。

個人的な感想を言えば、今回の炎上は完全に仕組まれていて、

日本のある政治家 -> (中国に強い商社) -> 中国共産党のとても偉い人 -> あのvtuberを叩け

のラインで発生したと思っています。中国側から仕掛けたならあまりに稚拙すぎるからです。今回の炎上で、あんなに英語が得意な中国人スパイの存在を10人もアメリカ側にバラシしてしまったんですから、中国側からすれば大損です。二度と関わりたくないでしょう。

今回の件で唯一私が納得出来なかったのが、三週間のクールダウンを入れる事で炎上が収まるのを待つと言う考え方でした。これはケガをしたら、病院で治療しないで家で寝てるのと同じで、肉離れ程度なら良いですが骨折だったらもっと悪くなる最悪の選択だったと思います。

今回はReddit民が敵対的な中国人を片っ端から潰して、一週間経った時にはもう敵対的な発言をすると徹底的に叩かれる、という事実が中国側に完全に浸透していました。日本語しか出来ない人には、一見三週間のクールダウンに効果があったように思えますが、このReddit民の行動が無かったら、三週間たっても炎上し続けた可能性は高かったと私は思っています。

更にこの三週間のクールダウンのせいで、一つの中国を肯定し「台湾は国である。」と言う事は言ってはいけないと言う風潮が、日本のvtuber界に何となく受け入れられてしまいました。これも最悪だと思います。

―以下の内容は、私が知り合いから聞いた話を、元に書いてるので細かい部分は、間違っているかもしれません。台湾、歴史で検索すれば正確な歴史を記した本が、沢山ではないですが結構あります。正確な歴史はそれらの本を読んでください。-

ほとんどの日本人は知らないと思いますが、第二次世界大戦の後アメリカの支援を受けた中華民国の元である国民党は、ソ連(ロシア)の支援を受けた中国人民共和国の元である共産党との戦争に敗れて台湾に逃げ込みました。そこには日本の教育を受けた(お金持ちは帝国大学まで出ていた。)台湾人が住んでいたのですが、国民党はその現地人たちを虐殺につぐ虐殺で殺しまくったんです。その悲劇から海外に逃げ延びた台湾人が掲げたのが台湾独立運動なんです。

だから中華民国(台湾)と言う表記は元々おかしいんです。台湾と言う国は中華民国から独立して初めて存在するんです。

そして中国(中華人民共和国)と日本の条約ってのは「中華民国が中国の代表ではなく中華人民共和国が唯一の中国の代表で、それ以外の国が中国の代表である事は認めない。」と言う意味なんです。この唯一の中国の代表であると言う事の意味は、中華人民共和国が国連の常任理事国であり、中華民国は中国の代表じゃないから国連の常任理事国じゃないよ。と言う意味でそれ以上でも以下でもないんです。

もし台湾人が中華民国から独立して、台湾と言う国を宣言したとしても、台湾が「我々こそが、中国の代表であり国連の常任理事国の権利をよこせ。」と言わない限り、それを国として承認するかどうかは、100%日本の自由なんです。

では、何でこんなに、中国が台湾を国と表記した事に激怒しているのかと言うと、国民党の幹部と共産党の幹部が親戚同士だからなんです。

昔の中国の金持ちは国民党と共産党のどっちが勝つか分からなかったから、子供を兄は国民党、弟は共産党に入れてどっちが勝っても家族が生き残れるようにしたんです。中華民国から台湾が独立してしまうと国民党が台湾に築いた富と権力がなくなってしまうので(一説によると国民党は世界で一番金を持っている政党だそうです。)親戚の共産党の幹部にお願いして、台湾が絶対独立出来ないように圧をかけてもらっているんです。また自分たちでも中華民国(台湾)のような表記を積極的に使用して台湾と言う言い方も中華民国と同じ意味であるような印象を回りに与えているんです。

あれ、でも台湾って選挙してたよね。と気づく人も居ると思います。そうなんです。国民党は中華人民共和国と戦争中であるから戦争が終わるまでは選挙はしない。と言っていたのですが、それだと台湾人がまた反乱を起こすので、名ばかりの副総統に台湾人をつけていたんです。しかしこの副総統、ある特別な権限だけは持っていたんです。それは、総統が在職中に亡くなった場合、副総統が総統に就任する。と言う権限なんです。そう、在職中に総統が何故か亡くなった、その隙をついて総統に就任したその台湾人が、すかさず総統の権限を使用して選挙を行ったんです。

それで、慌てた国民党の幹部が親戚の共産党の幹部に頼んで、台湾が中華民国から独立しないように中華人民共和国から圧をかけまくっているのが現在の台湾の政治なんです。

これは、非常にsensitiveな内容で台湾人もあまり触れたがらない歴史と政治なんです。日本で原爆の話をする時は、非常に気を使って話さないといけないのと同じような難しさが存在する話題なんです。

でも日本人がこういう話を全く知らないでいて良い訳ではありません。知った上で空気を呼んで知らないフリをするのは、賢いですが知らないのは単なるアホです。

今回の台湾を国と言ってはいけないと言う風潮は、多くの日本人がvtuberを通じてこういう事実に触れる機会を奪ってしまったんです。だから私はこの三週間のクールオフは最悪だったと思っています。

しかし今回の炎上で多くの日本人は気付いませんが、大きな宝を得る事が出来た事はここに記しておきたいと思います。

今のアメリカの若い世代は日本のアニメや漫画を見て育っています。彼らにとって一番衝撃だったのが日本のアニメや漫画における友情の強さでした。今のアメリカの若者がベビーブーマー世代に対して自分の事しか考えていないと批判するのは、日本のアニメや漫画における友情が潜在的な基準になっているからなんです。しかし彼らは本当に日本人がアニメや漫画の主人公の様に友情を大切にするのか疑問に思ってもいます。

今回の炎上の初期に炎上したvtuberが所属する会社や同僚たちがそのvtuberを見捨てて中国におもねる様な行動を取った時、Reddit民を代表とするアメリカの若い世代は大激怒しました。彼らは、日本のアニメや漫画における友情の強さを信じていたのにあっけなく裏切られたからです。所がその後、vtuberが所属する会社が中国におもねる様な行動を取ったのは本意ではなかった事や、同僚のvtuberはこの炎上に触れない事で炎上のクールダウンを狙っている事が知れ渡ると、アメリカの若い世代は、やっぱり日本のアニメや漫画のように彼らは強い友情で助け合っていると確信するようになりました。

そして今回無事に復活出来た事でその確信は信用に昇華しました。大人だろうが子供だろうが他人から信用してもらうのは大変です。それが違う文化に属している人達ならなおさら大変です。別にアメリカに限りませんが別な文化に属している人達からそういう信用を得られた事は大変な宝です。そしてこの信用はvtuberを通じて全ての日本人に対する信用に拡張する可能性にもつながります。

それでは今週の勉強を始めます。

<本文>

1. バグの直し

前回は村人NPCを沢山作成した所で終わりました。今週は先週のバグの直しからやろうと思います。

1.二人の村人NPCが近い距離で立っていると、一方の村人としか話せない。

2.UE4C++widgetを作成した場合は、その派生クラスであるBPのデザインはどうなるのか?

3.NPC村人のアクターを別々に作成しているが、一種類に出来ないのか?

それぞれ直していきます。

1.1 二人の村人NPCが近い距離で立っていると、一方の村人としか話せない。

これは以下に示したように、それぞれのNPCがボックスを保持していてそのボックス内にプレイヤーが操作するキャラが侵入した時に、そのNPCと会話出来るようになっているからです。

f:id:kazuhironagai77:20201025215031p:plain

これを、プレイヤーが操作するキャラにボックスを付けてその中に居るアクターを調べる方式でやれば、こんな問題は起きませんでした。

ここまで制作しておいて今更ですが、ゲームの制作は結局どうやってアルゴリズム(algorithm)に落とすかにかかっていたと言う事です。最初に優れたアルゴリズムに変換出来ていれば、こんな問題は今頃になって発生する事はありませんでした。

とってもこれからやり方を変えるわけにもいかないので、対症療法で解決します。

まず。以下に示したような印を、NPCの上に表示出来るようにします。

f:id:kazuhironagai77:20201025215050p:plain

プレイヤーが操作するキャラが会話出来るNPCは上の印が表示されます。

表示されないNPCとはどんなに距離が近くても会話する事は出来ません。一端離れてからもう一度近づく必要があります。

この印が表示されるには、RPGGameModebaseクラスのPlaceForEvent変数の値が、このNPCの種類と同じである必要があります。

以下のコードで確認します。

f:id:kazuhironagai77:20201025215152p:plain

テストします。

以下の状態でテストします。

f:id:kazuhironagai77:20201025215210p:plain

印が表示されているので右側のNPCと会話出来る事が分かります。

f:id:kazuhironagai77:20201025215228p:plain

一端離れた後、近づき方を変えて左側のNPCと会話出来るようにしました。

f:id:kazuhironagai77:20201025215248p:plain

これでNPCのボックスが重なったとしても、プレイヤーはキャラを操作する事で望みのNPCと会話出来るはずです。

1.2 UE4C++widgetを作成した場合はその派生クラスであるBPのデザインはどうなるのか?

要するに前回出来なかったVertical boxを追加したUUserWidgetの親クラスの作成をUE4C++から出来ないのかと言う事です。

f:id:kazuhironagai77:20201025215312p:plain

Google先生で調べた限りではやっている人は見当たらないです。諦めても良かったのですがちょっとだけ試してみます。

うん。

2時間ぐらい色々試したのですが、良く分かりません。なんていうかUUserWidgetとかSlotの周りを全部忘れてしまっていてその辺を復習しているだけで時間が経ってしまいました。これは今の私にはちょっと難しすぎました。

パスします。

1.3 NPC村人のアクターを別々に作成しているが、一種類に出来ないのか?

f:id:kazuhironagai77:20201025215343p:plain

これを一種類のActorで代用します。

f:id:kazuhironagai77:20201025215402p:plain

NPC_Oldmanからduplicateして作成しました。

f:id:kazuhironagai77:20201025215420p:plain

変数にOccupationを作成します。タイプはPlaceForEventでInstanceEditableとします。

更にコードを変更します。

f:id:kazuhironagai77:20201025215449p:plain

RPGGameModebaseのMyPlaceForEvents変数にはこのOccupationの値をセットします。

更に、印を表示するための判定の部分もOccupationを使用します。

f:id:kazuhironagai77:20201025215511p:plain

これで完成です。滅茶苦茶簡単でした。

テストします。

2体、instanceを作成しました。

f:id:kazuhironagai77:20201025215556p:plain

f:id:kazuhironagai77:20201025215604p:plain

一体目はOldmanにセットしました。

f:id:kazuhironagai77:20201025215632p:plain

2体目はHenchmanにセットしました。

f:id:kazuhironagai77:20201025215651p:plain

一体目に話しかけます。

f:id:kazuhironagai77:20201025215707p:plain

普通にOldmanとの会話が出来ました。

2体目は、村長の腰巾着が出て来ました。

f:id:kazuhironagai77:20201025215729p:plain

こっちも普通に会話出来ます。

出来ました。

これで要らない以下のアクター全部消せます。

f:id:kazuhironagai77:20201025215746p:plain

消してはいけない以下の3つのアクターだけ別のフォルダーに移して

f:id:kazuhironagai77:20201025215805p:plain

残りは全部消しました。

f:id:kazuhironagai77:20201025215824p:plain

相当すっきりしました。

並べると以下のような感じです。

f:id:kazuhironagai77:20201025215844p:plain

こうなるとWelcomeウィジェットも一つに統一したいです。

f:id:kazuhironagai77:20201025215903p:plain

うーん。来週もう一回考えます。

. 残りの村人の作成

以下の村人NPCを作成します。

f:id:kazuhironagai77:20201025215940p:plain

村長の娘です。

f:id:kazuhironagai77:20201025220015p:plain

保安官です。

f:id:kazuhironagai77:20201025220040p:plain

旅人です。

ムーミンスナフキンみたいにしたかったのですが、難しいですね。

f:id:kazuhironagai77:20201025220058p:plain

ハンターです。

f:id:kazuhironagai77:20201025220117p:plain

これで村人NPC全部完成しました。

こんな感じで配置しました。

f:id:kazuhironagai77:20201025220137p:plain

アニメーションは来週作成します。

. 残りは何をするか

先々週は、以下の事をすると予定を立てました。

f:id:kazuhironagai77:20201025220200p:plain

この中で村人との会話は完成しました。

今週は、もう時間が無くなってしまったので以下に来週以降どんな事をやるのかの計画だけを書いておきます。

3.1 魔法の拾得方法について

神殿のような建物を作成して、そこに行くとレベルに応じて魔法が覚えられるようにします。

これは、GameCharacterクラスで使用出来る魔法を管理しているから

f:id:kazuhironagai77:20201025220231p:plain

f:id:kazuhironagai77:20201025220247p:plain

f:id:kazuhironagai77:20201025220255p:plain

魔法の拾得に使用出来る可能性があるのでこれも勉強してみます。

神殿のデザインについては、

f:id:kazuhironagai77:20201025220322p:plain

f:id:kazuhironagai77:20201025220332p:plain

らのアセットを使用して作成しようと思います。

3.2 戦闘システムのボタンの直し

これは今すぐでも出来る事なのでやってしまいます。

f:id:kazuhironagai77:20201025220417p:plain

以下の様に変更しました。

f:id:kazuhironagai77:20201025220434p:plain

攻撃する対象は以下の様に変更しました。

f:id:kazuhironagai77:20201025220451p:plain

魔法の選択画面も変更しました。

f:id:kazuhironagai77:20201025220512p:plain

その後で、攻撃対象を選択するためのボタンが以下の形だったのでそれも直しました。

f:id:kazuhironagai77:20201025220529p:plain

以上です。

3.3 実際のゲーム内の町の地面をデコボコにする

前のテストで、Landscapeの使い方は一応勉強したのですが、数値の最適化がまだ良く分かっていませんので、来週以降に何回かLandscapeを練習作成してから考えます。

3.4 夜と昼の作成の続き

良く考えてみると、時間で昼と夜が変わるよりも、歩いた歩数で昼と夜が変化した方が分かり易い気がします。後、宿屋に止まった後は、必ず朝になるようにします。

. まとめと感想

今週はこれでお終いです。

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する 村人の会話を管理する2

f:id:kazuhironagai77:20201018215720p:plain

<前文>

手塚治虫鉄腕アトム宮崎駿風の谷のナウシカ

現在、失われた30年と言われ日本経済が全く発展しないで30年が経ちました。ほとんどの日本企業は画期的な技術の開発や科学の発見が出来なくなって何十年もゾンビのような生ける屍と化しています。私はその原因は、日本人の科学技術に対する考え方が、手塚治虫鉄腕アトムのそれから宮崎駿風の谷のナウシカのそれに変わったからだと思っています。

これを言うと大抵の人は私の事を馬鹿にしますが、私はかなり強力にこの事を信じていて、いつか文章にまとめようと思っていました。

正し、最初にはっきりと言っておきますが、宮崎駿風の谷のナウシカや他のジブリの作品が駄目と言っている訳ではありません。アニメとしての質とその作品内における特定のモノに対する創作者の思想は全く別モノだからです。ハリウッド映画に例えると分かり易いかもしれませんが、ある監督が作る映画に必ず日本人が出て来て、必ず一番最初に殺されるとしたらどう思いますか?その映画の面白さや内容の質とは別に「この監督、日本人嫌いなんじゃねえ。」と思いますよね。

それと同じ事を宮崎駿風の谷のナウシカにおける科学技術の取り扱い(特に手塚治虫鉄腕アトムにおける科学技術の取り扱いと対比しての)に対して私は言いたい訳です。

宮崎駿風の谷のナウシカでは、科学技術に関して2つの点が強調して書かれています。

一つ目は、科学技術は純粋な悪であると言う事です。風の谷のナウシカで科学技術が人類や主人公に良い結果をもたらす事は全くありません。人類を滅ぼすほどの環境汚染を引き起こしたりとかしてます。

二つ目は、科学技術は簡単に収得出来るものである。と言う事です。ナウシカは自作の稚拙な実験器具から腐海の秘密を解き明かします。特別、化学や薬学の勉強をしたと言う説明もありません。小さい頃から本を読むのが好きとか、実験に失敗して爆発を起こしたとか、一般人には不要でも、科学や技術が好きな人間なら共感できるような逸話もありません。にもかかわらずナウシカ腐海の秘密を解き明かします。

これに対して手塚治虫鉄腕アトムでは、科学技術は人々に大きな幸せをもたらす物であるが、扱い方を間違うと大変な事になる。と描かれています。更に科学者のモラルが科学技術の発達の方向性に大きな影響を与え、人類の幸せに直結する事も描かれています。読者は鉄腕アトムを通じて、どのように科学技術と向き合っていくべきなのかを漫画を読みながら学べます。

更に、手塚治虫鉄腕アトムでは科学者や技術者は簡単には成れない憧れの存在として描かれています。登場する科学者や技術者は、人生の辛苦とそこから真理を学んだ高僧のような表情をしていたり、天才を具現化したような厚顔無恥な態度を取ったりと人によって千差万別ですが、一貫して特別な存在として描かれています。

手塚治虫鉄腕アトムにおける科学技術は、火です。火は扱いを間違えると火事を起こしたり火傷をしたりと大きな害をもたらしますが、正しく用いれば暖かい食事を作成出来たり、暖を取る事が出来たりします。宮崎駿風の谷のナウシカにおける科学技術は、悪です。片手間に収得出来き、表面的には人に益するように見えるが、結局は害にしかならない存在です。

宮崎駿風の谷のナウシカの世界観が支配する今の日本で、誰が科学者や技術者に成りたいと思うでしょうか?また、科学や技術を収得するのは簡単であるとの間違った見解は、必要な勉強時間や実習時間(更に言えばそれにかかる費用も)を大幅に少なく見積もる可能性が高いです。よく科学技術の研究のための予算が貰えないから良い研究が出来ないと言われますが、十分な予算が貰えない時点で、宮崎駿風の谷のナウシカの世界観が日本全体を支配している事の証明だと思います。科学技術そのものを純粋な悪と見なす思想の呪縛が日本人全体を覆っている事について考えてみて欲しいです。

でも読者の中には「宮崎駿風の谷のナウシカにおける科学技術への捉え方の方が本質をついているんじゃね。」と思う人もいるかもしれません。

確かにテレビを見ていると、今の日本の科学者や技術者は専門家の地位を利用してとんでもない嘘をついているとしか思えない人は沢山います。しかしこれも科学技術に対する扱いに長けていないから生じる害なんです。科学や技術は専門家の意見が良く間違っている事を自らよく知っていて必ず2重盲検法というテストでその効果を客観的に判断します。科学技術自体が専門家の意見を何の検証もしないで聞くのは「たき火に手を突っ込むようなもんだ。」と言っているんです。

もう一つの問題があります。科学や技術を悪と断じたとしても、その原理に律されている事には変わりがない事です。原始時代に例えると分かり易いかもしれません。貴方がある日、火の扱いに失敗して火傷してしまったとします。それから貴方が火は悪であると断じて一切火を使わないで生活したとするでしょう。しかし火を扱う事を貴方が止めても火が無くなる訳ではないです。貴方が火を扱うのを止めたとしても山火事が起きなくなる訳ではありません。また食事も火を使わなければ全て生で食べなければなりません。食中毒に罹る割合も段違いに高くなるでしょう。

宮崎駿風の谷のナウシカや他のジブリ作品の呪いから外れ、手塚治虫鉄腕アトムが示したように、科学技術を賢く運用出来た時に日本の停滞が終わる日がくると信じています。

それでは今週の勉強を始めます。

<本文>

今週は先週、終わらなかった村人NPCの会話システムのうちの単純な会話の部分を完成させます。

具体的には先週、設定だけ作った以下の村人NPCを実際に作成します。

1. 詩を読む村人

  • 詩人になりたくていつも詩を作成している人です。
  • 話しかけると作成した詩を披露してくれます。
  • 更に主人公に詩の評価を聞いてきます。高評価なら喜び、低評価だと悲しみます。

2. 占い師

  • この世界では霊感が備わった人は守護精霊からの警告が占いの形で聞き取れるとの信仰があります。それを実践する人達です。
  • 話しかけると主人公の未来を占ってくれます。

3. 科学者の卵

  • 再現性を重んじ、繰り返し再現出来る事に価値を見出します。この世界では、魔法が強力なため科学技術はあまり発展していません。
  • 話しかけると、科学の基礎に発展しそうな考察を披露してくれます。

4. 天気予報の達人

  • 村人は基本的に農民なので天気の予測は非常に大切です。
  • 話しかけると今年の雨量、暑さ、寒さなどの予測を教えてくれます。

5. 政治評論家

  • この世界の王たちも政争に明け暮れています。
  • 話しかけると今の政治状況について解説してくれます。

6. 山の達人

  • この世界の村人は、春は山菜、秋はきのこ狩りをするのが普通です。山の達人はそれらを採る達人です。
  • 話かけると、山の情報について教えてくれます。

7. ビジネスマンの卵

  • どうやったらお金持ちになれるのかいつも考えています。
  • 話しかけると、今考えているビジネスについて説明してくれます

8. 退役した戦士

  • 退役した戦士も農民になって暮らしています。
  • 話しかけると、戦闘に有利な情報や退役後にのどかに暮らす方法などについて教えてくれます。

9. 魔法研究家

  • 魔法について研究している村人もいます。
  • 話しかけると、魔法についての情報を教えてくれます。

10. 村長

  • 村のリーダーです。村の食料、治安、水などを管理しています。
  • 話しかけると、村のビジョンについて語ってくれます

11. 村長の腰ぎんちゃく

  • 村長の機嫌を良くする事だけが生きがいです。
  • 話しかけるとどうして腰ぎんちゃくとして生きているのか教えてくれます。

12. 村長の娘

  • 村人から姫と呼ばれています。理想の女の子を演じるのに疲れています。
  • 話しかけると本音を教えてくれます。

13. 保安官

  • 村の治安を守っています。村で武器の使用が許可されている唯一の人です。
  • 話しかけると仕事を頼まれます。保安官に頼まれた仕事は村にいる限りやらなければいけません。因みに料金はもらえません。

14. 旅人

  • 旅人は主人公だけではありません。色々な理由で旅をしている人も村に宿泊しています。
  • 話しかけると、世界の旅の仕方について教えてくれます

15. ハンター

  • 村の外には凶悪なモンスターが出現する事があります。その時には、村人はハンターを呼んで退治してもらいます。ハンターは高額な報酬を貰えるのでみんなの憧れの職業です。
  • 話しかけると、バカにされます

1.NPC村人(詩人)の作成

1.1 DataTableの作成

DataTableを使用して以下に示したように最初の村の詩人のセリフを作成しました。

f:id:kazuhironagai77:20201018220512p:plain

f:id:kazuhironagai77:20201018220520p:plain

f:id:kazuhironagai77:20201018220529p:plain

1.2 詩人のCharacterクラスの作成

NPCの老人をDuplicateして作成しました。

f:id:kazuhironagai77:20201018220554p:plain

以下に示したように変数MyPlaceForEventsの値がPE_TalkingNPC_Oldmanに成っています。

f:id:kazuhironagai77:20201018220613p:plain

Enum、EPlaceForEventsにPE_TalkingNPC_Poetを追加します。

f:id:kazuhironagai77:20201018220632p:plain

Buildし直します。

以下に示したように、UEC++のGameModeBaseのコードを変更した後は、全てのBP内のGameModeBaseのcastがエラーになります。

f:id:kazuhironagai77:20201018220649p:plain

これを直します。

一回、UE4Editorを閉じて開いたら直りました。

f:id:kazuhironagai77:20201018220720p:plain

1.3 ThirdPersonCharacterクラスにPoetのための機能を追加しPoet会話用のwidgetを作成する。

以下のコードをNPC_Poetのために追加します。

f:id:kazuhironagai77:20201018220744p:plain

OldmanWelcomeウィジェットの代わりにPoet_Welcomeを作成します。

OldmanWelcomeからduplicateして名前を変えただけです。

f:id:kazuhironagai77:20201018220802p:plain

イメージとコードを少しだけ変えました。

f:id:kazuhironagai77:20201018220819p:plain

f:id:kazuhironagai77:20201018220827p:plain

テストします。

f:id:kazuhironagai77:20201018220852p:plain

はいを選択します。

あれ、変化しません。エラーになっていました。

f:id:kazuhironagai77:20201018220908p:plain

調べると、AnswerButtonウィジェットのParentWidget変数のタイプはOldmanWelcomeウィジェットなので

f:id:kazuhironagai77:20201018220934p:plain

f:id:kazuhironagai77:20201018220941p:plain

このAnswerButtonウィジェットはOldmanWelcomeしか使用出来ませんでした。新しいボタンウィジェットを作成します。

f:id:kazuhironagai77:20201018220958p:plain

テストします。

はい。を押すと

f:id:kazuhironagai77:20201018221014p:plain

時数が多すぎました。

調整します。

f:id:kazuhironagai77:20201018221033p:plain

細かい調整は必要ですが、大体はOKですね。

2.NPC村人の作成についての考察と効率化

2.1前節で作成した welcome ウィジェットを統一出来ないか?

Buttonウィジェットにwelcomeウィジェットをパスするのですが、welcomeウィジェットはそれぞれwelcome_oldmanウィジェットとwelcome_poetウィジェットと違うウィジェットなのでButtonウィジェットもAnswerButtonウィジェットとAnswerButtonPoetウィジェットの2つを作成しました。共通の親ウィジェットからwelcome_oldmanウィジェットとwelcome_poetウィジェットを派生すれば、AnswerButtonウィジェット1個足りるはずです。

試してみます。

出来ませんでした。

Widgetで親子関係が成り立つのは純粋な変数と関数だけみたいで、親ウィジェットのdesignパートで作成したTextなどのウィジェットは、子クラスのウィジェット内には作成されませんでした。うーん。もしかしたら出来るのかもしれませんが、今回はやり方が分からないのでpassします。

ここにやり方が載っていました。

f:id:kazuhironagai77:20201018221106p:plain

こっちのやり方なら親ウィジェットのdesignパートで作成した全てのウィジェットが表示されるみたいです。

出来ました。出来ましたがこのやり方だと、親クラスのウィジェットで作成したデザインに足したり、変化したりする事は出来ません。

うーん。今回はwelcome ウィジェットの統一は諦めて別々に作成する事にします。

3.その他のNPC村人の作成

これ以上整理出来る事はないので、別のNPC村人を作成していきます。やり方は1. NPC村人(詩人)の作成と同じなので結果だけ載せていきます。

3.1 占い師

WelcomeウィジェットのFormatは詩人と同じです。イラストだけ変更しています。後でWelcomeウィジェットも改造する予定です。

f:id:kazuhironagai77:20201018221150p:plain

3.2科学者の卵

詩人を元に絵を描きました。

f:id:kazuhironagai77:20201018221210p:plain

3.3 天気予報の達人

老人を元に作成しました。これで5種類の村人NPCを作成しましたが、今になってwelcomeウィジェットは統一しても良かったかもと思い始めています。

f:id:kazuhironagai77:20201018221233p:plain

後で、統一するにしてもそれぞれの村人NPCのセリフと絵は必要なのでこのまま残りのNPCも作成します。

3.4 政治評論家

知ってます。が2行になっているのが気になります。

f:id:kazuhironagai77:20201018221300p:plain

3.5 山の達人

流石に同じ事を繰り返すのは飽きて来ました。関係ないですが、NPCの村人のデザイン私が描いているですが、上手くないですか。こんなシンプルな絵にも著作権って付くんでしょうか?

f:id:kazuhironagai77:20201018221344p:plain

3.6ビジネスマンの卵

ビジネスマンのやつです。

f:id:kazuhironagai77:20201018221406p:plain

3.7 退役した戦士

今までのキャラは一人を除いて男性でしたので、このキャラは女性にしました。

f:id:kazuhironagai77:20201018221434p:plain

3.8 法研究家

このキャラも女性にしました。

段々疲れて来て成果デザインが前ほど良く無くなって来ました。

f:id:kazuhironagai77:20201018221458p:plain

以下の絵に変えました。

f:id:kazuhironagai77:20201018221516p:plain

一つデザインで気が付いたんですが、体は基本的な図形を使用して表した方が美しく見えると言う事です。

3.9 村長

村長と言うより王様です。もうデザインのアイデアが無くなりました。

f:id:kazuhironagai77:20201018221601p:plain

3.10 村長の腰巾着

デザインは前よりかなりマシになりました。

f:id:kazuhironagai77:20201018221620p:plain

文字の表示ですが、タイプライター効果は欲しいですね。読み切る前に選択肢のボタンが表示されるのは、混乱します。文字が全部表示されてから選択ボタンが示されると分かり易いです。

流石にもう疲れました。残りは別な機会に作成する事にします。

4.NPC村人の問題点

4.1 NPC村人が2人以上重なった時に、片方の人と会話が出来なくなります。

f:id:kazuhironagai77:20201018221655p:plain

最初のNPC村人のボックスを出た後に、既に次のNPC村人のボックス内に入っていると、

f:id:kazuhironagai77:20201018221720p:plain

MyPlaceForEvents変数が、PE_Noneにセットされたままに成ります。

f:id:kazuhironagai77:20201018221738p:plain

その場合は一端、ボックスを飛び出してもう一度そのNPC村人のボックスに入り直す必要があります。

このボックスを飛び出してもう一度そのNPC村人のボックスに入り直すと言う行為はプレイヤーはボックスそのものを見る事は出来ないので、プレイヤーにとっては、基本的には不可能な行為です。早急に解決する必要があります。

この問題は来週考えます。

5.まとめと感想

今週はここまでです。

ここまでやって思い出したんですが、UE4C++でウィジェットを作成してそれの派生クラスをBPで作成する場合は試していなかったです。更に

f:id:kazuhironagai77:20201018221809p:plain

レベル内に配置するためのアクターをNPC毎に別に作成しているので今の時点でも15種類になっています。これも最適化する事で減らせないか検討します。

 

 

Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する 村人の会話を管理する

f:id:kazuhironagai77:20201011202512p:plain

<前文>

I was worry about…は間違いなのか?

何処かの総理大臣が英語でツイートした時の文言らしいですが、ネットで英語が間違っていると叩かれているそうです。この文章の何処が間違っているのかも二転三転していて、最初私が聞いたのは「be + worriedじゃなくてworryが正しい。」と言う意見だったんですが、それが間違いでworryでもbe worriedでも正しい事が分かると今度はwasが間違っていると言う事になりました。

私は、この総理大臣を庇いたい気持ちは全くないんですが「C-何とかに罹ったと聞いて本当に心配しました。」と素直に解釈しましたし、英文法のサイトで調べてもそういう意味に取れると思います。

何で人の英文にケチつける人は自分では調べもしないんですかね。それと、ここで大切なのは自分の気持ちを伝える事で100%正しい英文を書く事じゃないですよね。日本語が分からない人に日本語で大丈夫ですか?と言っても通じないから英語をしゃべる事が大事なんでしょう。文法的にちょっとくらい間違っていたとしても気持ちが伝わる事の方が大切じゃないんでしょうかね。

私も10年アメリカに住んでいたので普段の英語には問題ないですが、論文の英語はアメリカ人の教授がキッチリ直してくれました。アメリカ人とまったく同じ英文が書けるようにはとうとう成らなかったです。

後、英文法盲信君達は勘違いしているけど、受験英語の文法を幾ら勉強してもアメリカ人から見て文法に間違いがない英文は書けるようにはなりませんよ。平均的な知性があれば英文法極めた大先生達がちっとも英語でTwitterしてないのを見れば気が付くんですが。英語でtwitterすればfollower数だって何倍になりますよね。更にアメリカ人の英語の文法の間違いなんか指摘出来たら、日本人のfollower数も爆増しますよ。でもしないと言う事は…、そう言う事なんですよ。

じゃあ、幾ら勉強しても英語をアメリカ人のように書くのは不可能なのかと言うと、そうでもないみたいなんです。先週のvtuberの炎上で英語圏で有名なvtuberの切り抜き翻訳サイトが中国人が運営していた事が判明したんですが、その翻訳、私はアメリカ人が書いたとずっと思っていました。その後、あまりに中国寄りな意見のため、アメリカ人のファンからそのサイトは総攻撃されたんですが、英語の間違いを攻撃している人は一人もいませんでした。それから推測するとその翻訳を担当していた中国人グループは完璧な英語を取得していたと思われます。

中国は科学技術で世界の最先端に追いついただけじゃなくて、語学などの文系の分野でも日本がまだ知らない特別な学習方法を編み出した可能性があります。更にその学習方法に基づいて勉強すればネイティブと同じレベルまで上達可能みたいです。

先週、vtuberの中国での炎上が収まらなかったらもっと深い考察をやると言いましたが、まだ考えがまとまっていないのと別な内容でまとめたい事があるのでそっちを先にする事にしますが、必ずまとめます。

それでは今週の勉強を始めます。

<本文>

先週やり残したデザインパートからやって行きます。

Design Part

  1. 村人と会話出来る様にする
  2. 魔法の拾得方法について
  3. 戦闘システムのボタンのデザインを統一する
  4. 実際のゲーム内の町の地面をデコボコにする

Programming Part

  1. 夜と昼の作成の続き
  2. 村人の会話システムの構築

1. Design Part

1.1 村人と会話出来る様にする

ゲームデザインの観点から考えると例え村人と会話出来たとしても面白くなければ意味がないと思います。多くのRPGで村人は単なる愚か者として描かれています。そんな人達と会話する意味があるんでしょうか?

私が作成するゲームでは村人は主人公と比較して知性が劣っている訳ではなく主人公とは人生の目的が違う人達です。

以下のタイプを考えています。

1. 詩を読む村人

  • 詩人になりたくていつも詩を作成している人です。
  • 話しかけると作成した詩を披露してくれます。
  • 更に主人公に詩の評価を聞いてきます。高評価なら喜び、低評価だと悲しみます。

2. 占い師

  • この世界では霊感が備わった人は守護精霊からの警告が占いの形で聞き取れるとの信仰があります。それを実践する人達です。
  • 話しかけると主人公の未来を占ってくれます。

3. 科学者の卵

  • 再現性を重んじ、繰り返し再現出来る事に価値を見出します。この世界では、魔法が強力なため科学技術はあまり発展していません。
  • 話しかけると、科学の基礎に発展しそうな考察を披露してくれます。

4. 天気予報の達人

  • 村人は基本的に農民なので天気の予測は非常に大切です。
  • 話しかけると今年の雨量、暑さ、寒さなどの予測を教えてくれます。

5. 政治評論家

  • この世界の王たちも政争に明け暮れています。
  • 話しかけると今の政治状況について解説してくれます。

6. 山の達人

  • この世界の村人は、春は山菜、秋はきのこ狩りをするのが普通です。山の達人はそれらを採る達人です。
  • 話かけると、山の情報について教えてくれます。

7. ビジネスマンの卵

  • どうやったらお金持ちになれるのかいつも考えています。
  • 話しかけると、今考えているビジネスについて説明してくれます。

8. 退役した戦士

  • 退役した戦士も農民になって暮らしています。
  • 話しかけると、戦闘に有利な情報や退役後にのどかに暮らす方法などについて教えてくれます。

9. 魔法研究家

  • 魔法について研究している村人もいます。
  • 話しかけると、魔法についての情報を教えてくれます。

10. 村長

  • 村のリーダーです。村の食料、治安、水などを管理しています。
  • 話しかけると、村のビジョンについて語ってくれます。

11. 村長の腰ぎんちゃく

  • 村長の機嫌を良くする事だけが生きがいです。
  • 話しかけるとどうして腰ぎんちゃくとして生きているのか教えてくれます。

12. 村長の娘

  • 村人から姫と呼ばれています。理想の女の子を演じるのに疲れています。
  • 話しかけると本音を教えてくれます。

13. 保安官

  • 村の治安を守っています。村で武器の使用が許可されている唯一の人です。
  • 話しかけると仕事を頼まれます。保安官に頼まれた仕事は村にいる限りやらなければいけません。因みに料金はもらえません。

14. 旅人

  • 旅人は主人公だけではありません。色々な理由で旅をしている人も村に宿泊しています。
  • 話しかけると、世界の旅の仕方について教えてくれます。

15. ハンター

  • 村の外には凶悪なモンスターが出現する事があります。その時には、村人はハンターを呼んで退治してもらいます。ハンターは高額な報酬を貰えるのでみんなの憧れの職業です。
  • 話しかけると、バカにされます

村人の会話の内容はこんなもので言いと思います。しかしどうやって管理しましょうか。以下に問題点を書きます。

  • 村は一か所ではありません。○○村の詩人。の様に管理すべきでしょうか?それとも…
  • 村人の会話も単一では面白くありません。色々な条件が変化する事で会話の内容が変化するようにしたいです。

このゲームは色々なレベルを移動するので、特性はGameInstanceクラスで管理する必要があります。しかし一々GameInstanceに変数を作成していたら管理出来ません。

以下に現在全てのNPCに使用しているNPC_Oldmanを示します。

f:id:kazuhironagai77:20201011203428p:plain

中のBPを見てみると既に変数NPC_OldmanをRPGGameInstanceBP内に作成しています。

f:id:kazuhironagai77:20201011203517p:plain

f:id:kazuhironagai77:20201011203534p:plain

何でこのクラスのための変数は、GameInstanceクラスのUE4C++側じゃなくてBP側に作成したんですかね。覚えていませんね。

NPCの会話のシステムの実装をどうやったのか忘れていました。それをまず復習します。

f:id:kazuhironagai77:20201011203607p:plain

Keyboardのeを押す事でDoSomething Eventが発生します。

Event DoSomething はThirdPersonCharacter内で実装されていました。

f:id:kazuhironagai77:20201011203800p:plain

あれ、これ見るとRPGGameInstanceBP内の変数NPC_Oldmanは必要無いですね。

f:id:kazuhironagai77:20201011203818p:plain

NPC_Oldmanがどこに使用されているか調べたら昔作成して今は使用していない箇所のみで使用されていました。確かに要らない変数です。

外します。

f:id:kazuhironagai77:20201011203835p:plain

テストします。

きちんと動きました。

f:id:kazuhironagai77:20201011203903p:plain

セリフの管理状況を見てみます。

単にNPC_Oldmanクラス内の変数で管理しているだけでした。

f:id:kazuhironagai77:20201011203924p:plain

うーん。実は以下のPlugInを購入してセリフの管理を一括で行おうか迷っています。

f:id:kazuhironagai77:20201011203947p:plain

取りあえず今回は使用しないで作成してみます。

この老人と会話するシステムを作成します。

以下に示したように作ってみました。

まず、NPC_Parentウィジェットの変数、NPCOldmanのセリフを以下の様に変更します。

f:id:kazuhironagai77:20201011204006p:plain

f:id:kazuhironagai77:20201011204014p:plain

0は質問です。

次にInteger変数とText変数を持つStructを作成します。

f:id:kazuhironagai77:20201011204032p:plain

名前はAnswerCommentManagementとしました。

f:id:kazuhironagai77:20201011204049p:plain

このStructを使用してNPCOldmanのセリフ0に対する答えを作成します。

f:id:kazuhironagai77:20201011204106p:plain

f:id:kazuhironagai77:20201011204114p:plain

ここで「はい、そうです。」を選択した場合はJumpToCommnetに1がセットされているのでNPCOldmanのセリフ1が次に表示されます。「いいえ…」を選択した場合はJumpToCommnetに2がセットされているのでNPCOldmanのセリフ2が次に表示されます。

Oldman_Welcomeウィジェット内に以下に示した実装を行います。

f:id:kazuhironagai77:20201011204221p:plain

因みに以下のようなコメントが表示されているんですが、

f:id:kazuhironagai77:20201011204243p:plain

Oldman_WelcomeウィジェットのParentクラスはNPC_Parentウィジェット

f:id:kazuhironagai77:20201011204301p:plain

問題なく継承出来ているみたいですが。widgetからwidgetを継承出来ないとはどういう事なんでしょうか?取りあえずはこの警告は無視して先に行きます。

Oldman_Welcomeウィジェット内で実装されたコードでvertexAnswer(以下に示した四角の部分)内に作成されるボタンウィジェットは、

f:id:kazuhironagai77:20201011204321p:plain

AnswerButtonウィジェットで、

f:id:kazuhironagai77:20201011204339p:plain

OldmanAnswerFor0に含まれているコメントをボタン上に表示します。

f:id:kazuhironagai77:20201011204410p:plain

更にそのボタンをクリックすると

f:id:kazuhironagai77:20201011204428p:plain

Oldman_WelcomeのvertexAnswer内のボタンを全部消した上に、TextBlock_OldmanDialogに表示されているセリフをNPCOldmanのセリフ0からセリフ1もしくは2に変更します。

テストしてみます。

f:id:kazuhironagai77:20201011204446p:plain

はいそうです。を選択します。

f:id:kazuhironagai77:20201011204503p:plain

セリフが変わりました。

もう一回話しかけて、今度は「いいえ…」を選択しました。

f:id:kazuhironagai77:20201011204522p:plain

ここまでは出来ています。

最初の構想からはかなり稚拙な出来になってしまいました。

今の状態では、話しかけるたびに最初の状態に戻ってしまいます。GameInstanceクラスに何も保持する必要もありません。しかしこの部分が出来ないとこの先も何も作れないので取りあえずこれでやってみます。

今度はもっとセリフを増やしてみます。

NPCOldmanのセリフ1に対する返答をOldManAnswersFor1に作成します。

f:id:kazuhironagai77:20201011204539p:plain

それに対する返答もNPCOldmanに追加します。

f:id:kazuhironagai77:20201011204558p:plain

これらの会話が実行されるようにOldman_welcomeウィジェットの実装も少し変更します。

f:id:kazuhironagai77:20201011204621p:plain

テストします。

はいそうです。を選択します。

f:id:kazuhironagai77:20201011204639p:plain

「はい。そうです。」を選択します。

f:id:kazuhironagai77:20201011204656p:plain

新しい選択ボタンが表示されました。

「ビカルト…」を選択します。

f:id:kazuhironagai77:20201011204712p:plain

最初に戻って「珍しい魔石…」を選択してみました。

f:id:kazuhironagai77:20201011204737p:plain

出来てますね。

1.2 NPC会話システムの考察

一応NPC一人分の会話を作成する事は出来ましたが、このやり方では沢山のNPCの会話を管理する事は出来ません。

現在、非戦闘時の会話やコメントは、NPC_Parentウィジェット内で一括管理しています。これは他の言語に翻訳したり、会話の問題を発見したりしやすくするためには絶対必要です。

しかし以下に示すように、NPC一人で、NPCの会話で変数を一個、その解答のための選択ボタンの作成にそれぞれ一個の変数、

f:id:kazuhironagai77:20201011204804p:plain

を作成してったら変数の数が膨大になってどれがどれを指しているのかを管理する事は不可能になります。

色々考えた結果、以下の方法ならこのNPCとの会話の管理が可能ではないかと思いました。

1.1の結果を踏まえると、NPCとの会話はNPCからの質問、それに対するプレイヤーの回答部分に分かれます。更にプレイヤーの回答は何個もあり、その回答によって次のNPCの答えも変わってきます。

これを表にまとめると以下の様になります。

f:id:kazuhironagai77:20201011204823p:plain

これを見て、思ったのですが、NPCの会話はアドベンチャーゲームブックにそっくりです。

中学生の時、アドベンチャーゲームブックを作成するのが趣味の同級生がいて、休み時間によくその友達が作成したアドベンチャーゲームブックで遊んだんですが、どうやってその友達がアドベンチャーゲームブックを作成したのか私は全く分からなかったです。ある日作り方を教えてくれたのですが、それは以下の方法でした。

  1. まず真っ新なノートに目次を振ります。
  2. 次に1ページ目に文章を書いて、aを選択するなら15ページへ、bを選択するなら8ページへと書きます。
  3. その次に15ページに行って続きを書く。それをただただ繰り返せばアドベンチャーゲームブックは完成すると教えてもらいました。

後年、Computer scienceのクラスでalgorithm とは何かを教わった時にこの事を真っ先に思い出しました。彼はalgorithmの概念そのものを自分で発見していたんです。

話がそれ過ぎました。元に戻します。

アドベンチャーゲームブックの作成方法と全く同じ手順でNPCとの会話のデータテーブルを作成しようと思います。完成したデータテーブルはa村の詩人とか、b町の村長と名付けます。そして村ごとにフォルダーを作成して管理します。その村ごとのフォルダーはNPCフォルダーに入れて置けば全部のNPCの会話を一個のフォルダーで管理できるわけです。

多分、本格的なRPGはdatabaseを活用してNPCの会話を一括管理しているのでしょう。しかしこのゲームにdatabaseまで活用するのは重すぎます。まず私はMySQLしか扱った事はないので、このゲームのためだけにDatabaseまで勉強し直すのは正直きついです。それにdatabaseはサーバーで管理しないといけないのでUE4とサーバーの関係も勉強しないといけなくなります。今回はデータベースの活用はパスします。

そういえば去年の一時期、FirebaseとUE4を繋げてサーバー管理はfirebaseに任せようとfirebaseの勉強を熱心にした時期がありました。FirebaseのTutorialは一通り勉強したんですが、そのtutorialで使用されているsample codeがJavaScriptで普通に使用されている方法なのかFirebase独特の関数のための使用方法なのかが良く分からなかったんです。これはJavaScriptをもう一回勉強しないとかやってる内に一年間の無料サービス期間が過ぎてしまいました。更にfirebaseを使用していた人から使用料が結構高額になるから有料のサービスでないと使うだけ赤字になると言われて結局Firebaseの勉強を止めてしまいました。

Epic game社が無料で提供しているオンラインサービスは結構凄いらしいので、具体的にどんなサービスを提供しているのかは分からないですが、いずれは勉強しようとは思っています。

1.3 最初の村にいるNPCの老人の会話用データテーブルの作成

1.2でまとめた方法でもう一度、NPCの会話システムを作成してみます。

まずStructを作成します。今回は試しなのでBPで制作します。

f:id:kazuhironagai77:20201011205027p:plain

このStructを元にしてDataTableを作成します。

f:id:kazuhironagai77:20201011205054p:plain

f:id:kazuhironagai77:20201011205101p:plain

f:id:kazuhironagai77:20201011205109p:plain

1の質問に対する回答は2つ用意されています。一つ目を選択した場合は、2のコメントが表示され、2つ目を選択した場合は、3つ目のコメントが表示されるようにコードを書いて行きます。

f:id:kazuhironagai77:20201011205130p:plain

データデーブルの方も変更しました。

f:id:kazuhironagai77:20201011205146p:plain

まず、NPCの質問は長いので行替え出来る様にしました。Ctl+Enterでは行替え出来できないので、このサイトを参考にして{nextline}を改行したい個所に挿入します。

そして以下のようなコードを通して表示します。(Inputの値はCtl+Enterです。)

f:id:kazuhironagai77:20201011205211p:plain

更にJumpToCommentの番号を1ずつ減らします。

f:id:kazuhironagai77:20201011205228p:plain

これで試します。

f:id:kazuhironagai77:20201011205251p:plain

はい。を選択します。

f:id:kazuhironagai77:20201011205315p:plain

もう一度、会話に戻って今度は「いいえ…」を選択しました。

f:id:kazuhironagai77:20201011205337p:plain

こっちのコメントは行替えしていませんでした。直します。

f:id:kazuhironagai77:20201011205401p:plain

会話をもう少し複雑にします。

f:id:kazuhironagai77:20201011205421p:plain

f:id:kazuhironagai77:20201011205431p:plain

テストします。

f:id:kazuhironagai77:20201011205459p:plain

上を選択します。

f:id:kazuhironagai77:20201011205518p:plain

下を選択します。

f:id:kazuhironagai77:20201011205533p:plain

複雑にしても対応していますね。

これで単純なNPCの会話システムは作成できそうです。

1.4 次の村にいるNPCの老人の会話の作成

これは、セリフだけ別に作成して残りは同じオブジェクトを使用したいです。

まず、村の名前を保持したEnum、villageNameを作成します。

f:id:kazuhironagai77:20201011205556p:plain

次にNPC_OldmanクラスにtypeがvillageNameである変数を作成します。名前はvillageNameとしました。

f:id:kazuhironagai77:20201011205614p:plain

f:id:kazuhironagai77:20201011205620p:plain

そしてこの変数の目の部分をクリックして目が開いている状態にします。

この目が明いている状態だとこの変数はPublicになるんですが、私はこの意味がずっと分からなかったんです。しかし最近やっと分かりました。のでここに解説を残しておきます。

BPに作成した変数はGetter、Setterが自動で作成されるのでpublicだろうがprivateだろうが他のBPから簡単にアクセス出来ます。つまりpublicにしてもprivateのままでも同じなんです。なのに何でこんな機能が付いているのかと言うと、変数をPublicにするのが目的ではなくて、それぞれのインスタンスに別な初期値をセット出来るようにする事が目的だったんです。

確かに以下に示す様に目の解説を見るとそれぞれのインスタンスで編集可能と書かれています。

f:id:kazuhironagai77:20201011205638p:plain

UE4エディターからNPC_Oldmanクラスから作成したインスタンスを選択して

f:id:kazuhironagai77:20201011205708p:plain

そのdetailを見てみると、

f:id:kazuhironagai77:20201011205833p:plain

編集可能な変数としてVillageNameが表示されています。

これが目が開いている事の目的だったんです。

因みにUE4C++におけるUPropertyのSpecifierのEditAnywhereが同じ働きをしますね。(最初、EditInstanceOnlyと同じかと思ったんですが、目が開いた状態の変数の値はInstanceだけでなくBP本体でも編集出来るのでEditAnywhereと同じでした。)

以下に示すように、NPC_Oldmanクラスから2体のインスタンスを作成してそれぞれのvillageNameをFirstVillage、SecondVillageとします。

f:id:kazuhironagai77:20201011205910p:plain

f:id:kazuhironagai77:20201011205918p:plain

f:id:kazuhironagai77:20201011205925p:plain

これで同じNPC_Oldmanクラスでもそれぞれのinstanceで違うセリフをしゃべる土台が出来ました。

今度は、SecondVillageに住む老人用のセリフを作成します。

f:id:kazuhironagai77:20201011205941p:plain

データの要素はFirstVillage_Oldmanと全く同じですが、セリフとその回答のための選択ボタンは全然違います。

f:id:kazuhironagai77:20201011210019p:plain

それぞれのinstanceで違うセリフを表示するための実装は以下に示したように行いました。

NPC_Oldmanクラスでplayerが操るキャラがボックス内に侵入すると、そのNPC_Oldmanクラスのinstanceが持っているVillageNameの値をThirdPersonCharacterクラスの変数、villageNameにコピーします。

f:id:kazuhironagai77:20201011210048p:plain

キーボードのEがクリックされた場合、ThirdPersonCharacterクラス内でOldmanWelcomeウィジェットが作成されますが、その時にVillageNameの値がOldmanWelcomeウィジェットにパスされます。

f:id:kazuhironagai77:20201011210107p:plain

OldmanWelcomeウィジェットではVillageNameの値によって別々のデータテーブルから会話のためのセリフを読み込みます。

f:id:kazuhironagai77:20201011210125p:plain

この部分の実装で、何でswitchを使っているですか?polymorphism使えば一行で書けるじゃないですか?と思う人がいるかもしれません。私もそう思っていました。でも出来ませんでした。DataTableから親クラスや子クラスが作れなかったんです。最初データテーブルでOldmanと言う親クラスを作成してそこから子クラスであるFirstVillage_OldmanとSecondVillage_Oldmanを作成してpolymorphismで一気に行こうとしたら出来ませんでした。もしかしたら私が知らないだけで出来るのかもしれませんが、そもそもDataTable自体がクラスじゃないですし、出来ないと考える方が理に適っていますので、あまり調べずに上記の方法で実装しました。

PolymorphismというかInheritanceはオブジェクト指向言語の悪い点として悪名を轟かせていますが、無ければないで非常に不便です。この例で言えば、switchの分岐した先のコードは全く同じですが、compilerがFirstVillage_oldmanとSecondVillage_Oldmanが同じ形式のデータテーブルと認識してくれないので、全く同じコードを2回書いています。最終的には村の数だけこの分岐は増えるので大変なスパゲティコードになってしまいます。

これで完成したはずなのでテストしてみます。

まず左の老人に話しかけます。

左の老人はVillageNameの値がFirstVillageなので今までと同じセリフで会話してくるはずです。

f:id:kazuhironagai77:20201011210216p:plain

していますね。

今度は右の老人に話しかけます。

f:id:kazuhironagai77:20201011210234p:plain

はい。全く違うセリフを言いました。SecondVillage_Oldmanデータテーブルからのセリフです。

出来ました。

もう時間がなくなってしまったので今週はここまでとします。

2. まとめと感想

今週はNPCの会話システムの作成の途中で終わってしまいました。NPCの会話システムの構築は、思っていたより大変です。来週も引き続きNPCの会話システムを作成していきます。

 

Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する 新しい機能を追加する7

f:id:kazuhironagai77:20201004213256p:plain

<前文>

またまたvtuberについてと中〇の炎上について

私のブログを読み直してもらえばしっかりと書いてあるのが分かりますが、いわゆるネトウヨと呼ばれる人達が、C何とかの事を武〇ウィルスとか、中〇ウィルスと読んでいた時に絶対やめろと。困っている人に対して意地悪な事をするのは人間として恥ずかしい事だと何回も言いました。

これと同じような事をvtuberの人も言っていたら今回こんなに炎上しなかったんじゃないかなと本音では思いました。炎上したとしても「中国人が困っている時に私はこんなに味方に立ったのに、一寸間違えただけでこんなにも叩くのはひどい」と言えたと思います。

ひよっとしたら、炎上したvtuberの人も過去の記録を見れば、中国で最初にcが流行った時に「中国人、伝染病に負けるな!がんばれ!」とか日本語で言っていたかもしれません。

それを最初に出すのが最低限の交渉力だと思いました。

次にですが、殺害予告はいくらなんでも冗談では済まないと思います。警察に連絡しましょう。

今の中国はHxHのメルエムのように、文明国とならず者国が半々で存在しているみたいです。

中国が知的財産やハイテク産業で21世紀を生きてゆくならいずれは完全な文明国になるでしょう。もし中国が文明国としてこれからやって行くのなら、このvtuberの安全を守る責任は中国政府にあると思います。もし中国政府が文明国としての義務を守らなければ、西洋の国や日本も中国の権利、例えばファーウェイの5G の特許などの権利を守る必要もなくなるからです。中国政府は全ての人民をビックデータで管理しているから犯人が誰か直ぐに分かるはずです。国際的に文明国として中国政府が責任を果たしたとみなさる最低条件は犯人の逮捕でしょう。その後で、愛国無罪になったとしてもそれでも文明国としての国際社会の信義は果たしたと思われるでしょうが。

ならず者国家としての中国はアメリカと日本の中を悪くして、日本を侵略する戦略を持っていると感じました。

今回殺害予告を受けたvtuberアメリカ人だと聞きました。日本政府には日本に在住しているアメリカ人の安全を保障する義務があります。それはアメリカに在住している日本人の安全をアメリカ政府が保証しているからです。ここで日本政府が憲法9条があるから守れんかったなどとフ抜けた事を言ったら、一気にアメリカ政府からの信頼を失い、中国が幾ら日本を侵略してもアメリカは助けてくれないでしょう。この状態はならず者国家としての中国には最もおいしい状態です。

前の総理は、色々問題がありましたが何が合っても自分の味方は絶対守りました。どう考えても違法だろうと言う手段をとっても守ったそうです。今度の総理は腹心の部下でも問題を起こしたらすぐ切り捨てたそうです。このvtuberが日本政府に身の安全を求めても自己責任とか言って切り捨てる可能性は高いです。そうなったら我々日本国民が、命を賭けてこのvtuberを守らなければならなくなります。何故ならアメリカからの信頼を失った後で、中国から侵略されたら、その時に失う日本人の命はとんでもない数になるからです。

私が今回の炎上について今思う事はそんな所です。

実は、私には台湾独立運動中華民国からの)に関わっていた知り合いがいまして、その人が亡くなる前に今の中国人も台湾人も知らない生のアジア戦後史を聞いていました。ので私は中国語は全く分からない素人ですが、結構中国の行動を予測するのは得意です。もし来週になっても炎上が収まらなかったらその知識を活用してもう一回この問題について考察します。

それでは今週の勉強を始めます。

<本文>

今週は以下の事をやって行こうと思います。

Programming Part

  1. バグ取をします
  2. 宿屋の主人を作成します。
  3. 夜と昼を作成します。

Design Part

  1. 町をデコボコにする
  2. 村人と会話出来る様にする
  3. 魔法の拾得方法について
  4. 戦闘システムのボタンのデザインを統一する

1. Programming Part

1.バグの直し

先週もバグを直すと言ってやりませんでしたので今週こそやります。

1.1 MPが足りない時の場合

MPが足りない魔法はボタンが表示されないようにしましょう。

魔法ボタンを表示する前にcharacterのMPがその魔法の消費MPより高いが同じである事を確認します。

f:id:kazuhironagai77:20201004213632p:plain

もし低い場合はその魔法は表示されません。

更に、CharacterのMPが低くて使用出来る魔法が無い時、魔法の選択自体が出来ないようにしました。

f:id:kazuhironagai77:20201004213713p:plain

テストします。

f:id:kazuhironagai77:20201004213730p:plain

今、MPが5しかないので使用するのに必要なMPが10である炎(大)は表示されません。

MPが0の時、魔法は選択出来ません。

f:id:kazuhironagai77:20201004213746p:plain

出来ていますね。

1.2 ChooseボックスとTargetsボックス内のボタンウィジェットが消去されていない。

f:id:kazuhironagai77:20201004213814p:plain

Event show action panel の時にChooseボックスとTargetボックスの中をClearします。

f:id:kazuhironagai77:20201004213830p:plain

テストします。

攻撃を選択してその次のターンで道具を選択した時のスクリーンショットです。

f:id:kazuhironagai77:20201004213852p:plain

前のターンでTargetボックス内に作成されたウィジェットは消えています。

出来ました。

新たなバグと言うわけではないですが、魔法のアニメーションが全部同じです。後で直します。

2. 宿屋の主人を作成します

NPC_ItemShopOwnerをduplicateしてNPC_InnOwnerを作成します。

f:id:kazuhironagai77:20201004213929p:plain

RPGGameInstanceのTalkShopの代わりを作成します。

f:id:kazuhironagai77:20201004213947p:plain

TalkInnと名付けました。

f:id:kazuhironagai77:20201004214002p:plain

ビルドします。

TalkShopとTalkInnを交換します。

f:id:kazuhironagai77:20201004214017p:plain

今度はMyPlaceForEventsにPE_Innを追加します。

f:id:kazuhironagai77:20201004214034p:plain

しました。

f:id:kazuhironagai77:20201004214049p:plain

Buildします。

BPのMyPlaceForEventsが古いままです。新しく成りません。

f:id:kazuhironagai77:20201004214107p:plain

UE4editorが2個動いていました。

一回全部閉じて開き直しました。

f:id:kazuhironagai77:20201004214131p:plain

今度は出来ていました。

何で1個しかUE4Editorが開かれていないのに、タスクマネージャーを開くと2個開いている時があるのでしょうか。良く分かりません。

今度は ThirdPersonCharacterを開き、SwitchonEPlaceForEventsのPEInnを実装します。

f:id:kazuhironagai77:20201004214154p:plain

以下の様に実装しました。ItemShopと全く同じです。

f:id:kazuhironagai77:20201004214210p:plain

以下に示したShopWelcomeウィジェットがItemShopの時は作成されます。

f:id:kazuhironagai77:20201004214227p:plain

f:id:kazuhironagai77:20201004214241p:plain

これを元にして、InnWelcomeウィジェットを作成します。

f:id:kazuhironagai77:20201004214259p:plain

ShopWelcomeウィジェットの会話ボタンを押すとShop Talkウィジェットが開きます。

f:id:kazuhironagai77:20201004214315p:plain

f:id:kazuhironagai77:20201004214322p:plain

InnWelcomeウィジェットのためのShop Talkウィジェット、Inn Talkを作成しましょう。

まず、NPC_ParentウィジェットにNPCInnDialogを追加しセリフを書きます。

f:id:kazuhironagai77:20201004214341p:plain

f:id:kazuhironagai77:20201004214347p:plain

そのセリフをInnWelcomeウィジェットから呼び、表示します。

f:id:kazuhironagai77:20201004214419p:plain

Inn_welcomeウィジェットからInn_Talkウィジェットが開かれるようにセットします。

f:id:kazuhironagai77:20201004214434p:plain

ここまでで一端テストします。

動かなかったです。細かい点で直していない所がありました。

もう一度テストします。

f:id:kazuhironagai77:20201004214451p:plain

f:id:kazuhironagai77:20201004214459p:plain

それでは泊まるボタンの実装を作成します。

f:id:kazuhironagai77:20201004214517p:plain

5ゴールド持っていたら、体力と魔力を全部回復します。そして宿屋のセリフが変わります。5ゴールド持っていない場合は宿屋のセリフだけが変わります。

テストします。

f:id:kazuhironagai77:20201004214603p:plain

この状態で泊まるボタンを押します。

f:id:kazuhironagai77:20201004214625p:plain

HPとMPが最大まで回復して店の主人のセリフが変わりました。金貨の数も減っています。

今度は金貨が無い状態で宿屋に行きます。

f:id:kazuhironagai77:20201004214653p:plain

店の主人のセリフが変わりました。

取りあえずはこれで完成です。

3.夜と昼を作成します。

これ作成方法が分かりません。まず調べます。

f:id:kazuhironagai77:20201004214818p:plain

この無料のアセットを使用すれば良いみたいです。

f:id:kazuhironagai77:20201004214838p:plain

Documentをざっと読んでみると

f:id:kazuhironagai77:20201004214857p:plain

DefaultのSkySphereBlueprintを外してこのBPを入れれば良いみたいです。

試してみます。

SkyPresetをMidnightにセットします。

f:id:kazuhironagai77:20201004214916p:plain

HDRが効いているせいなのか、あまり暗くないです。

f:id:kazuhironagai77:20201004214937p:plain

こっちは朝焼けです。

f:id:kazuhironagai77:20201004214955p:plain

取りあえずはこれで十分ですね。

インプットから昼夜を変化させられるかテストします。

Iを押す事に時間が進みます。

f:id:kazuhironagai77:20201004215013p:plain

f:id:kazuhironagai77:20201004215021p:plain

f:id:kazuhironagai77:20201004215029p:plain

f:id:kazuhironagai77:20201004215044p:plain

f:id:kazuhironagai77:20201004215058p:plain

出来ますね。ただ夜は明るすぎますね。

Documentに2つほど夜を暗くする方法が解説されていますが、Directional lightを直接弄った方が良いと思います。

そしたらDocumentに

f:id:kazuhironagai77:20201004215138p:plain

Directional lightのY軸の角度に合わせて昼夜が変わるように出来るとあります。

f:id:kazuhironagai77:20201004215202p:plain

LightSourceのY軸の値を変化しましたが、昼夜は変わりません。

以下のコードでI を押すたびにLightSourceのY軸を30度回転させるようにしました。

f:id:kazuhironagai77:20201004215242p:plain

試してみます。

f:id:kazuhironagai77:20201004215307p:plain

I を押します。

f:id:kazuhironagai77:20201004215326p:plain

Directionallightの角度は変わりましたが、BP_GoodSkyの昼夜は変わっていません。

Refreshをしないといけないのでしょうか?

f:id:kazuhironagai77:20201004215341p:plain

チェックできません。

その下に昼夜をautoで変化させますか?とあります。

f:id:kazuhironagai77:20201004215402p:plain

これ試してみます。

f:id:kazuhironagai77:20201004215418p:plain

f:id:kazuhironagai77:20201004215427p:plain

滅茶苦茶変わっています。

影が動いているのでdirectional lightも動いているはずです。しかし暗くはなりません。

f:id:kazuhironagai77:20201004215444p:plain

を外してみます。

f:id:kazuhironagai77:20201004215503p:plain

今度は真っ暗になりました。

f:id:kazuhironagai77:20201004215518p:plain

昼はそれなりに明るいです。こっちで行きますか。

宿屋に止まったら朝になる必要があります。が何をやってもautoが切れません。最後の手段に寝ている間にこのレベルを一回去って戻ってくる位しか思いつきません。残りは来週考えます。

2. Design Part

1.町をデコボコにする

試しにlandscapeで作成してみました。

f:id:kazuhironagai77:20201004215552p:plain

f:id:kazuhironagai77:20201004215600p:plain

凹凸感は合格だが、データの大きさなどが適切なのかは不明。以下の設定をいじる事でこのサイズにふさわしいデータ量にしたいです。

f:id:kazuhironagai77:20201004215618p:plain

この辺は、デザイナーが担当する個所なので全く知識がないです。勉強しないと分かりません。

もう時間がなくなってしまったので、残りは来週やります。

2.村人と会話出来る様にする

色々な村人を考える。例えば詩人、話しかけるとポエムを披露してくれる。その後で評価を聞いてきて、高評価だと喜ぶ、低評価だと落ち込みます。

3.魔法の拾得方法について

神殿みたいなのを作成してレベルが上がった後にそこへ行くと、レベルに応じた魔法が覚えられるようにします。

4.戦闘システムのボタンのデザインを統一する

3. まとめと感想

今週は墓参りに行かなくてはならなくなったので途中でお終いにしました。

 

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する 新しい機能を追加する6

f:id:kazuhironagai77:20200927213752p:plain

<前文>

Vtuberについて、もうちょっとだけ

中国とアメリカの対決とか、Epic game社の裁判の行方とか、アメリカ国内におけるtiktokなどの中国系企業の活動、さらに来年の大統領選挙の行方とかを予測するのが私は大得意です。ある程度情報が集まるとパッと未来の結果が見えて来て絶対その通りになるんです。それが今回はどれもどうなるのか分かりません。

理由の一つは、Youtubeなどで私の視聴履歴を元に作成されるはずのお勧めのビデオが、一定の思想に基づいたある意味偏見の元に作成された物ばかり表示されるようになったからです。例えばEpic game社の裁判の行方についてはEpic game社が悪いと最初から決めつけている動画ばかり表示されるようになってしまいました。もし私が、Epic game社が悪いと最初から信じて、そのような動画ばかり見ていたなら、その視聴履歴がAIに反映されて、今のような結果になるのも分かるのですが、私はUE4をもう3年近く無料で使用させてもらっています。本当に、Epic game社には感謝しかありません。ので今回の裁判でも少しでもEpic game社の立場に立っている人の動画を探して見続けているにも関わらず、YouTubeのお勧めに現れる動画はEpic game社が悪いと最初から決めつけている動画ばかりです。

片っ端からそういう動画をブロックしたら今度は、一見公平を装っていながら最終的にEpic game社をメタメタに叩く動画ばかり表示されるようになりました。これだけ見たら全世界がEpic game社が悪いと言っている様に見えます。しかし自分で動画を探せば沢山の人がEpic game社が正しい、もしくは応援していると言っています。自分で探した結果を元に述べれば、Epic game社が悪いと言っている人達の方が少数派に見え、Epic game社が正しい、もしくは応援していると言っている人の方が多い感じがしました。

こういう、サブリミナルな情報操作を、一見公平な情報提供サイトでされると偏った情報に基づいて未来の予測をするため、当てるのはかなり難しくなります。偏った情報を元に正しい予測をするには、その予測の元となる情報が、その母集団から無作為に抽出したサンプルからどれだけ乖離しているのかを統計学的な処理をして一々確認する必要があるからです。

しかし私がそう言う問題の予測が出来なくなってしまった本当の理由は、Vtuberの動画ばかりを見るようになって、世界情勢なんか興味が無くなってしまったからです。

先日、Vtuberの切り抜きをまとめた動画を見ていたのですが、あるVtuberがスーパーチャットの内容で泣いてしまった話がありました。そのスーパーチャットをした人がツイッターか何かで、意地悪するつもりはなかったが結果的にそうなってしまってすみません。と謝ったらそのVtuberの人が、わざわざ、その人の所に、私が泣いちゃったから、心無い人からあなたも沢山攻撃されてしまったでしょうね。ごめんなさいね。と返信されたそうです。

それでその事は水に流してお終いになったそうです。

ここから真面目な話になりますが、その動画を見た時、私は素直に感動しました。この人達は他人の痛みを自分の痛みとして感じる事が出来きる知性(empathy?)と、中国の古典で言われる大きな責任*を果たせなかった事に対してごめんなさいが言える勇気がありました。その二つがあったから、この問題が大事に成らずに解決しました。それに私は感動しました。

*自分が正しく行動する事は小さな責任を果たす事になります。自分の周りの人が幸せに過ごせるように行動する事は大きな責任を果たす事になります。小人はこの小さな責任すら守れない人を指すそうです。中人は小さな責任は守れる人を指します。大人は大きな責任も果たせる人を指すそうです。昔読んだ中国の古典に書かれていた内容ですが、どの本に書かれていたのかまでは覚えていません。

人間関係の問題を解決するのに必要なのは、他人の痛みを自分の痛みとして感じれる事と、ごめんなさいが言える勇気だけだったんです。この事が分かっただけでもVtuberを見た甲斐がありました。

今、IT技術の発展はとんでもない勢いで進んでいます。しかし人間関係の問題を解決するのに必要なこの二つの事を支える技術なんでしょうか?例えば上記に記した偏った情報を示すAIの開発はむしろ逆なんじゃないでしょうか?

中国とアメリカの対決だって、21世紀の支配者を決める対決とか色々言われていますが、自分が痛いから相手をぶん殴っていいと考える人達と自分が正しければ幾らでも周りに迷惑かけて構わないという低レベルな人達の争いなんじゃないでしょうか?

そういうわけでますますVtuberの動画ばかり見る様になっています。

それでは今週の勉強を始めます。

<本文>

先週、デザインの途中で止めてしまったのでそれからやります。

Design part

  1. 宿屋がない事に気が付きました。宿屋を作成します。
  2. 完成した村を今のプロジェクトに移し、村人を配置します。
  3. 村のタイルの大きさをもっと小さくします。

プログラミングは以下の事をやります。

Programming part

  1. 戦闘中に「魔法」を選択した時に実行されるコードが攻撃を代用している状態なのでそれを直します。
  2. 魔法のコメントを正しくします。
  3. 先週のバグを直します。

Design Part

1.1 宿屋の作成

よくよく考えたら宿屋に来たからと言ってベッドが必要なわけではありませんでした。

道具屋や武器屋の建物をそのまま流用しました。

f:id:kazuhironagai77:20200927214052p:plain

何か簡単でしたね。

中の人を作成しなければなりませんがそれはprogramming partでやる事なのでここまでで完成とします。

2.2完成した村を今のプロジェクトに移し、村人を配置します。

先にタイルの大きさを調節します。

f:id:kazuhironagai77:20200927214134p:plain

以下のように変えました。

f:id:kazuhironagai77:20200927214209p:plain

それではCh4_3にこのマップを写します。

f:id:kazuhironagai77:20200927214231p:plain

中々いい感じです。

f:id:kazuhironagai77:20200927214253p:plain

モンスターが生成されちゃってますね。

モンスターとアイテムの生成はそれぞれのレベルによって違う事と、別のレベルに移動したら、新しく再生される事の両方を考慮して、もう一度作成し直さないと駄目ですね。

折角なので、町をmap1に追加してみます。

出来ました。

f:id:kazuhironagai77:20200927214340p:plain

中を見てみます。

f:id:kazuhironagai77:20200927214359p:plain

開始した時点で結構な迫力がありますね。

f:id:kazuhironagai77:20200927214424p:plain

地面が平らなのと、地面に貼ってあるmaterialが合って無いのが気になりますが、それ以外は結構いい感じです。

f:id:kazuhironagai77:20200927214449p:plain

町の入り口です。

f:id:kazuhironagai77:20200927214509p:plain

入口の傍にある家です。付属の家をそのまま使用していますので、中に入る事は出来ません。

f:id:kazuhironagai77:20200927214531p:plain

道具屋の前です。

f:id:kazuhironagai77:20200927214555p:plain

道を挟んで反対側に武器屋があります。

f:id:kazuhironagai77:20200927214623p:plain

f:id:kazuhironagai77:20200927214631p:plain

中の人も正しい場所に配置されています。

f:id:kazuhironagai77:20200927214655p:plain

奥の空き地に行くと、地面が平ら過ぎます。後で直します。

f:id:kazuhironagai77:20200927214713p:plain

町には4つの出入り口があります。その内の3つはどこにもつながっていなくて落ちるだけです。

村人を配置します。

と言っても村人に当たるメッシュが無いのでグレイマンをそのまま代用します。

f:id:kazuhironagai77:20200927214734p:plain

f:id:kazuhironagai77:20200927214742p:plain

f:id:kazuhironagai77:20200927214750p:plain

話しかけても全員同じ回答しかしません。これも後で直します。

f:id:kazuhironagai77:20200927214810p:plain

この村人の会話をどうやって管理するかが大問題なのですが、それは後で考えます。2. Programming Part

2.1 戦闘中に「魔法」を選択した時に実行されるコードが攻撃を代用している状態なのでそれを直します。

基本的には戦闘中のアイテムの使用と同じになるはずです。まずアイテムを使用した時の復習をします。

2.1.1 戦闘時のアイテム使用を元に復習する

先週作成したFlow Chartを元にコードを追っていきます。

f:id:kazuhironagai77:20200927214907p:plain

CombatUIWidgetクラスのAttackTarget()関数が呼ばれていますね。

しかし、実際の操作ではまず、CombatUIウィジェットの道具ボタンが押さる所からitemのコードが実行されるので、先週作成したFlow Chartに注意を払いつつ、Itemボタンが押された時から見て行きましょう。

f:id:kazuhironagai77:20200927214950p:plain

f:id:kazuhironagai77:20200927214958p:plain

色々やっていますが、結局はItemウィジェットを開く事が目的です。

f:id:kazuhironagai77:20200927215017p:plain

Itemウィジェットのボタンをクリックしたら、今度はTargetCharacterウィジェットを開きます。

f:id:kazuhironagai77:20200927215100p:plain

アイテムを使用する相手を決定したら、何かのイベントを発して他のクラスに連絡すると思っていたのですが、そのような事はしていませんでした。ここでこのコードは終了していました。

うーん。

分かりました。TargetCharacterウィジェットの相手を選択するボタンを押した時にComatUIWidgetクラスの関数、UseItemInCombat()が呼ばれていました。これを見逃していました。

f:id:kazuhironagai77:20200927215123p:plain

先週のブログを読み直したら分かり易く書かれていました。

以下にUseItemInCombat()の実装部を示します。

f:id:kazuhironagai77:20200927215142p:plain

ここまで、魔法を選択した場合を振り返ります。

f:id:kazuhironagai77:20200927215201p:plain

f:id:kazuhironagai77:20200927215210p:plain

Magicウィジェットを作成します。

f:id:kazuhironagai77:20200927215250p:plain

しかし作成するための元のデータは、

f:id:kazuhironagai77:20200927215317p:plain

Magicsと言う名前のdataTableから取っています。(以下にMagics のdata tableを示します。)

f:id:kazuhironagai77:20200927215342p:plain

ここは、GameCharacterクラスのMagics変数から取るように変更しないといけません。

f:id:kazuhironagai77:20200927215406p:plain

更に、Magicウィジェットから使用する魔法を選択すると

f:id:kazuhironagai77:20200927215426p:plain

AttackTargetOptionウィジェットが開かれます。このウィジェットは攻撃を選択した時に開かれるウィジェットです。魔法を使用した時に開く別なウィジェットの作成も必要です。

f:id:kazuhironagai77:20200927215455p:plain

AttackTargetOptionウィジェットが呼ばれるので実行される関数は当然AttackTarget()関数になります。CombatUIWidgetクラス内に魔法専用のAttackTarget()関数の作成が必要です。

以下にCombatUIWidgetクラス内にある攻撃、アイテム、逃げるに対応した関数を示しておきます。

f:id:kazuhironagai77:20200927215515p:plain

それぞれの関数内では、それぞれのアクションに対応したICombatAction クラスを継承したTestActionクラスが作成されます。

f:id:kazuhironagai77:20200927215536p:plain

魔法を使用した時のためのTestActionクラスの作成も必要ですね。

ここまでで。やっと先週作成したflow chartの最初のブロックが終わりました。

f:id:kazuhironagai77:20200927215556p:plain

ここから先のコードを読んでいきますが、先週のブログの復習になります。重要なのでここでも記録を残しながらやります。

f:id:kazuhironagai77:20200927215631p:plain

UseItemInCombat()関数内で、変数finishedDecisionがtrueになります。

f:id:kazuhironagai77:20200927215702p:plain

CombatUIWidgetクラスのMakeDecision()関数がtrueを返すようになります。

ここで情報の伝達先が切れてしまって、次どうなるのか分からなくなってしまったんですが、先週のブログと先週作成したflow chartにしっかり続きが書いてありました。

f:id:kazuhironagai77:20200927215725p:plain

f:id:kazuhironagai77:20200927215732p:plain

GameCharacterクラスのMakeDecision()関数に行くらしいです。

成程、GameCharacterのdecisionMaker変数は、

f:id:kazuhironagai77:20200927215751p:plain

RPGGameModeBaseクラスのtestCombat()関数内で、

f:id:kazuhironagai77:20200927215817p:plain

作成されたCombatUIWidgetクラスであるCombatUIInstanceがassignされています。

f:id:kazuhironagai77:20200927215851p:plain

のでGameCharacterクラスのMakeDecision()関数内の変数tmpResultはtrueを返します。

結果、GameCharacterクラスのMakeDecision()関数もtrueを返します。

先週書いたブログによれば、この後、CombatEngineクラスのTick()関数に行くそうです。

f:id:kazuhironagai77:20200927215910p:plain

見てみましょう。

f:id:kazuhironagai77:20200927215931p:plain

この辺は、先週のブログと全く同じに成りますが、この確認は非常に重要なのでそのまま続行します。

f:id:kazuhironagai77:20200927215950p:plain

先週書いた通りですが、ここでcurrentTickTarget変数のBeginExecutionAction()関数が一回だけ実行され、その後、currentTickTarget変数のExecutionAction()関数が実行されます。

先週のflow chartで言えば以下の部分に当たります。

f:id:kazuhironagai77:20200927220008p:plain

f:id:kazuhironagai77:20200927220015p:plain

ここも全く先週と同じですが、以下にGameCharacterクラスのBeginExecutionAction()関数の実装部を示します。

f:id:kazuhironagai77:20200927220041p:plain

ここでCombatEngine変数にassignされていた、それぞれのアクションに対応したICombatAction クラスを継承したTestActionクラスのBeginExecutionAction()関数が実行されます。

以下にTestCombatActionItemクラスのBeginExecutionAction()関数の実装部を示します。

f:id:kazuhironagai77:20200927220106p:plain

今回、魔法に対応したTestActionクラスを作成します。

今度は以下の図の②の部分が実行されます。

f:id:kazuhironagai77:20200927220135p:plain

先週のフローチャートでは黄色の枠で囲った部分です。

f:id:kazuhironagai77:20200927220201p:plain

実際のコードを以下に示します。

f:id:kazuhironagai77:20200927220221p:plain

CombatActionはTestCombatActionItemクラスのBeginExecutionAction()関数の実装部を示します。

f:id:kazuhironagai77:20200927220240p:plain

はい。

これで戦闘中にItemを選択した時の流れの復習が出来ました。これを元に魔法を選択した場合を直していきましょう。

2.1.2 戦闘時の魔法使用のBPコードを直す

まず、AttackTargetOptionウィジェットの代わりを作成します。

f:id:kazuhironagai77:20200927220303p:plain

AttackTargetOptionウィジェットをduplicateしてAttackTargetOptionForMagicウィジェットと名付けました。

コメントが攻撃の時のものなのでTargetCharacterウィジェット(道具を使用した時に対象者を選ぶウィジェット)を元にコードとコメントを直しました。

f:id:kazuhironagai77:20200927220322p:plain

その結果、CombatUIとMagicウィジェットのコードも少し変わりました。

f:id:kazuhironagai77:20200927220342p:plain

Magicウィジェットはコメントの部分を変えました。

f:id:kazuhironagai77:20200927220401p:plain

取りあえずコメントの変化だけテスト結果をしめします。

まず、魔法を選択し、炎(小)そして魔法の対象にゴブリンを選択しました。

f:id:kazuhironagai77:20200927220423p:plain

魔法を使用した時のコメントです。

f:id:kazuhironagai77:20200927220553p:plain

あんまり関係ないですが、一個バグを発見しました。

道具を一個も持たない状態で道具を選択すると、何も出来ない状態になります。

f:id:kazuhironagai77:20200927220614p:plain

このバグは後で直します。

2.1.3 AttackTarget() 関数に代わる関数の作成

現在、戦闘中に魔法を選択した場合、最終的にCombatUIWidgetクラスのAttackTarget()関数が呼ばれます。

f:id:kazuhironagai77:20200927220639p:plain

f:id:kazuhironagai77:20200927220646p:plain

この関数は、攻撃を選択した場合のための関数ですので、戦闘中に魔法を選択した場合の関数をCombatUIWidgetクラス内に作成します。

f:id:kazuhironagai77:20200927220707p:plain

Itemの場合を参考にして、HPとMPの値もパスするようにします。

f:id:kazuhironagai77:20200927220728p:plain

実装部はとりあえず、AttackTarget()関数と同じにしておきます。

f:id:kazuhironagai77:20200927220747p:plain

Buildも通りましたのでテストしてみます。

CombatUIWidgetクラスのAttackTarget()関数をUseMagicInCombat()関数に変更します。

f:id:kazuhironagai77:20200927220819p:plain

これでテストします。

f:id:kazuhironagai77:20200927220851p:plain

全く前と同じに動きました。成功です。

2.1.4 TestCombatActionクラスに代わるクラスの作成

TestCombatActionクラスは攻撃を選択した時のためのICombatActionの派生クラスなので魔法専用のクラスを作成します。

f:id:kazuhironagai77:20200927220923p:plain

中身はとりあえず、TestCombatActionItemクラスと同じにします。

f:id:kazuhironagai77:20200927220948p:plain

f:id:kazuhironagai77:20200927220957p:plain

ビルドします。

f:id:kazuhironagai77:20200927221018p:plain

成功しました。

テストします。

f:id:kazuhironagai77:20200927221040p:plain

表示がオカシイです。直していきます。

2.1.5 コメントの直し

まず、魔法のMPとHPですがHPは相手に与えるダメージですが、MPはプレイヤーが操作するキャラの消費分です。

以下のように直しました。

f:id:kazuhironagai77:20200927221104p:plain

後、MPが足りない時の場合も考えないといけない事に気が付きました。これは後で考えます。

これがきちんと機能するかテストします。

f:id:kazuhironagai77:20200927221216p:plain

まず、最も基本的な状態でのテストです。敵に50のダメージを与え、MPを20消費します。

以下に示したように、KUMOのMPは50、ゴブリンのHPは100です。

f:id:kazuhironagai77:20200927221240p:plain

f:id:kazuhironagai77:20200927221247p:plain

KUMOのMPは30、ゴブリンのHPは50になるはずです。

f:id:kazuhironagai77:20200927221309p:plain

f:id:kazuhironagai77:20200927221317p:plain

なっていますね。成功です。

その他の条件のテストも行いましたが予想通りの結果になりました。

次はコメントを直します。

以下の関数のargumentの値を変更しました。MPの場合はTargetをcharacterにも変更しました。

f:id:kazuhironagai77:20200927221351p:plain

更にRPGGameModeBaseクラスのReportCharacterHPisDamagedとReportCharacterMPisDamagedに実装されているBPのコードにプラスとマイナスが間違っているミスがあったのでそれも直しました。

f:id:kazuhironagai77:20200927221413p:plain

結果、以下のようにコメントも直りました。

f:id:kazuhironagai77:20200927221434p:plain

2.1.6 使用した魔法に基づいたHPやMPをUseMagicInCombat()関数にパスする。

今は適当な値をパスしてるので使用した魔法に基づいた値をパスします。

f:id:kazuhironagai77:20200927221500p:plain

以下のように改良しました。

AttackTargetOptionMagicウィジェットに新しい変数、nameOfMagicを作成しそこに選択した魔法の名前を保持させます。

f:id:kazuhironagai77:20200927221524p:plain

その名前を使用してData Table、Magicsからその魔法のデータを引き出します。

f:id:kazuhironagai77:20200927221544p:plain

f:id:kazuhironagai77:20200927221552p:plain

テストします。

f:id:kazuhironagai77:20200927221611p:plain

炎(小)を使用しました。

f:id:kazuhironagai77:20200927221632p:plain

データ通りの結果になっています。

f:id:kazuhironagai77:20200927221649p:plain

炎(大)を使用した時もデータ通りの結果になりました。

2.1.7 使用出来る魔法をDataTableのMagicsから取るのではなく、GameCharacterクラスのMagics変数から取るようにする

以下に示したGameCharacterクラスのMagics変数から使用出来る魔法を得るようにします。

f:id:kazuhironagai77:20200927221717p:plain

この変数に直接、日本語を入れると最終的に文字化けしてしまうので、今回は以下に示したように、

f:id:kazuhironagai77:20200927221759p:plain

M keyを押したらdata tableのMagicsのデータをRPGGameInstanceのMyYourHero変数のMagics変数に保持する事にしました。

余り、前回と変わっていませんが、魔法はキャラクターのレベルが上がったら使用出来るように考えていますので、レベルの上昇のコードを改良する前にあまりいじれないので今回はそうしました。

テストします。

魔法が全くない時です。

f:id:kazuhironagai77:20200927221823p:plain

道具が全くない時と同じになりました。

魔法が使用出来る時は、以下に示したように

f:id:kazuhironagai77:20200927221846p:plain

今まで通りに使用出来ます。

更に使用した後も

f:id:kazuhironagai77:20200927221905p:plain

同じです。

これで戦闘中における魔法の使用もコードの上では他の選択と全く同じ方法で動くようになりました。

2.2 魔法のコメントを正しくします。

2.1の時点で直しました。

2.3 先週のバグを直します。

道具や魔法が無い時は選択出来ない様にします。

以下のようにしました。

f:id:kazuhironagai77:20200927221938p:plain

道具や魔法が無い時は以下に示したようになります。

f:id:kazuhironagai77:20200927221959p:plain

3. まとめと感想

今週はこれで終わりにします。

 

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する 新しい機能を追加する5

f:id:kazuhironagai77:20200920213819p:plain

<前文>

Kawaiiは世界基準に成れるか?

可愛いの英語はcuteとかadorableでしょう。と思っていたのですが、昔、あるアメリカ人からkawaiiとcuteやadorableは全然違うと指摘されました。そん時は、また、アメリカ人の知ったかかよ。と思って無視していました。で、すっかり忘れてしまったのですが、最近大〇なおみ選手が優勝した時、アメリカ人が本当にうれしそうな顔しているのを見てその事を思い出しました。そしてkawaiiとcute(adorableも)が違う事が何となく分かりました。アメリカのマッチョな文化には、大〇なおみ選手のような可愛いさは存在しません。アメリカ文化にもcuteは沢山あります。しかしkawaiiはないんです。ので、アメリカ人はその可愛さを持っている大〇なおみ選手が大好きなんです。

不思議なのは可愛いかどうかは誰でも分かりますが何が可愛いのかは良く分からない事です。

最近vtuberの動画を見まくっていて、日本のvtuberは可愛いのに海外産のvtuberは可愛くないと思いました。理由は上記に説明したように分かりません。今、vtuberは海外からの人気が非常に高いですが、安易に日本語をちょっとしゃべれる外国人にやらせるのはかなり危ういと思います。外国人はこの可愛いと言う文化がないから日本人が当然と思っている可愛さが全くない感じになってしまうかもしれません。

しかしvtuberを見る海外の人はvtuberに可愛さを求めています。この辺を読み違えると大失敗に終わる気がします。

まあ、vtuberの海外進出が失敗に終わってもkawaiiが世界中から切望されるコンテンツである事は間違いありません。失敗しても別な何かで、世界はkawaiiを手に入れるでしょう。

それでは今週の勉強を始めます。

<本文>

今週も、programming partとdesign partに分けてやって行きます。

Programming part

  1. 戦闘中に「逃げる」を選択した時、他の選択をした時と挙動が違っています。これを直します。
  2. 逃げるのコメントを正しくします。
  3. 戦闘中に選択を間違えた時の対応を考えます。

Design part

  1. 宿屋がない事に気が付きました。宿屋を作成します。
  2. 完成した村を今のプロジェクトに移し、村人を配置します。
  3. 村のタイルの大きさをもっと小さくします。

全部は無理かもしれませんが一応、目標です。

1. Programming Part

1.1 「逃げる」の直し

先々週、戦闘中の逃げるを作成しましたが、先週判明した戦闘システムのプログラミングの手順に沿って作成はしなかったので、順序などが滅茶苦茶になっているはずです。それを直します。

今の状態だと以下のようになっています。

まず戦闘システムから逃げるを選択すると、RPGGameModeBaseBPのSetCombatEnginePhaseMakeOff()関数が呼ばれます。

f:id:kazuhironagai77:20200920213952p:plain

以下に示すように、SetCombatEnginePhaseMakeOff()関数内で、CombatEngineクラスのインスタンスであるcurrentCombatInstanceの関数、SetPhaseToMakeOff()が呼ばれます。

f:id:kazuhironagai77:20200920214010p:plain

以下に示したように、CombatEngineクラスのSetPhaseToMakeOff()内でSetPhase()関数が呼ばれます。

f:id:kazuhironagai77:20200920214028p:plain

以下にCombatEngineクラスのTick()関数内でPhaseがMakeOffに変わった時に実行されるコードを示します。

f:id:kazuhironagai77:20200920214047p:plain

Trueを返すだけですね。

今度はTrueを返されたRPGGameModeBaseのTick() 関数がどのようなコードを実行するかを見て行きます。

f:id:kazuhironagai77:20200920214120p:plain

まず、ここでCombatOverがtrueに成るので、if節が実行されます。

更にPhaseがMakeOffだった場合、以下に示したコードが実行されます。

f:id:kazuhironagai77:20200920214142p:plain

ここで大切なのは関数ReportFightIsOver() が実行される事です。

関数ReportFightIsOver()は以下に示したようにBlueprintImplementableEventでした。

f:id:kazuhironagai77:20200920214159p:plain

BPにおける実装は以下に示すコメントの表示内容を決定する部分と

f:id:kazuhironagai77:20200920214217p:plain

以下に示すアニメーションを表示する部分で構成されている関数です。(ただし逃げる時のアニメーションはないので逃げるを選択した時はアニメーションはなし。)

f:id:kazuhironagai77:20200920214238p:plain

今の逃げるを選択した時の流れが分かりました。

次に攻撃を選択した時の流れを見てみます。

まず、攻撃ボタンをクリックした時にCombatSelected変数の値をAttackにセットします。

f:id:kazuhironagai77:20200920214257p:plain

ここは、逃げるを選択した場合も、基本的には同じです。(CombatSelected変数の値をEscapeにセット)

この後、色々やっていますが、基本的には二つの事をやっています。

まずVirtical boxであるTargetsに攻撃対象のキャラを追加する事。

f:id:kazuhironagai77:20200920214324p:plain

次に攻撃を選択した時のコメントの表示

f:id:kazuhironagai77:20200920214343p:plain

を行っています。

この手順に従うならば、逃げるの選択をした場合も、まず、逃げる対象をTargetsに表示して、その後コメントを表示すべきです。

やっと具体的に直す箇所を一個見つけました。

更に見て行きます。

攻撃対象であるモンスターを選択した場合、AttackTargetOptionウィジェットのボタンがクリックされます。その時のBPのコードを見てみましょう。

AttackTarget()関数を読んでいます。

f:id:kazuhironagai77:20200920214403p:plain

AttackTarget() 関数はCombatUIWidgetクラスの関数で以下に実装部を示します。

f:id:kazuhironagai77:20200920214421p:plain

中を見てみると、ここでTestCombatActionクラスを作成しています。Itemを使用した時は、新しいクラス、TestCombatActionItemを作成したので、正しい手順にそうならば、逃げるを選択した場合はここでTestCombatActionEscapeクラスを作成すべきですね。

更に言えば、Itemを使用した場合は、

f:id:kazuhironagai77:20200920214438p:plain

AttackTarget()関数ではなく、UseItemInCombat()関数が呼ばれています。逃げるを選択した時のためにEscapeFromEnemy()関数を作成する必要がありそうです。

次に、ActionPanelをHideしています。

f:id:kazuhironagai77:20200920214509p:plain

以下の図で示した緑でくくった個所を全部隠します。

f:id:kazuhironagai77:20200920214536p:plain

この機能は、ユーザーが一個以上の選択するのを防ぐために勿論必要ですが、実際はユーザーは戦闘中に一個以上のボタンをクリックする事が出来るのでもっと厳しい対策が必要です。この対策は1.3でやります。

最後に表示するコメントをセットします。

f:id:kazuhironagai77:20200920214555p:plain

これで攻撃ボタンをクリックした場合のコードは全て実行されましたが、finishedDecisionがtrueにセットされた事によりCombatEngine並びにRPGGameModeBaseクラスのTick()関数で実行されているプログラム内容が変化します。

f:id:kazuhironagai77:20200920214616p:plain

まず、CombatUIWidgetクラスの関数、MakeDecision()の返し値がTrueに代わります。

f:id:kazuhironagai77:20200920214634p:plain

そのため、今度はGameCharacterクラスのMakeDecision()関数の返し値がTrueになります。

(以下の実装では、直接Trueになってはいません。これはコメントを表示する関係ですこしだけ教科書のコードより複雑になっているためです。)

f:id:kazuhironagai77:20200920214717p:plain

次にCombatEngineクラスのTick()関数内でdecisionMade関数がTrueに変化します。その結果、PhaseがCPHASE_Actionに変化します。

f:id:kazuhironagai77:20200920214737p:plain

PhaseがCPHASE_Actionに変化したためCombatEngineクラスのTick()関数内で以下のコードが実行されます。

f:id:kazuhironagai77:20200920214825p:plain

①から説明します。BeginExecuteAction()関数は、GameCharacterクラスの関数です。以下に示したコードが一回だけ実行されます。

f:id:kazuhironagai77:20200920214844p:plain

正直、教科書のこの書き方は無駄に複雑にしているような気がしていますが、今回はその部分には触れません。

この関数内で今度は、TestCombatActionクラスのBeginExecutionAction()を実行します。

f:id:kazuhironagai77:20200920215059p:plain

ハイ、ここで初めて敵のモンスターにダメ―ジを与えました。

今度は、CombatEngineクラスのTick()関数内の②についての解説です。

f:id:kazuhironagai77:20200920215119p:plain

因みにこの関数はactionFinished変数がTrueになるまで何度も呼ばれ続けます。

CurrentTickTarget変数はGameCharactetrクラスから作成されています。GameCharacterクラスのExecuteAction()関数を見てみます。

f:id:kazuhironagai77:20200920215138p:plain

ここで大切なのはTestCombatActionクラスのインスタンスを保持するCombatAction変数のExecuteAction()関数が呼ばれている事です。

はい。TestCombatActionクラスのExecuteAction()関数を以下に示します。

f:id:kazuhironagai77:20200920215156p:plain

ここで初めて、TestCombatActionクラスの変数delayTimerが0以下になったらTrueが返される事になります。

そして今までたどった経路をさかのぼって、CombatEngineクラスのTick()関数内の②の返し値がTrueになり、

f:id:kazuhironagai77:20200920215215p:plain

CombatEngineクラスのTick()関数内の以下のコードが実行され、

f:id:kazuhironagai77:20200920215238p:plain

また、攻撃、魔法、道具、逃げるの選択画面に戻ります。

以下にこの流れをまとめました。

f:id:kazuhironagai77:20200920215256p:plain

ここから

f:id:kazuhironagai77:20200920215315p:plain

を一回だけ実行した後、

f:id:kazuhironagai77:20200920215332p:plain

となります。

よし完璧に分かりました。それでは「逃げる」を直していきましょう。

まず逃げるを選択した時にCombatUIクラスで実行する内容を以下のようにしました。

f:id:kazuhironagai77:20200920215351p:plain

逃げるのに、対象を選ぶ必要はないので、コメントを表示するだけです。

CombatUIWidgetクラスに

f:id:kazuhironagai77:20200920215409p:plain

を追加します。

攻撃を選択した時のためのTestCombatActionクラスやItemを選択した時のUseItemInCombatクラスのような逃げるを選択した時のICombatActionクラスからの派生クラスを作成します。

f:id:kazuhironagai77:20200920215500p:plain

ここで初期化するTestEscapeActionクラスを作成します。

f:id:kazuhironagai77:20200920215524p:plain

f:id:kazuhironagai77:20200920215534p:plain

と言っても何が必要なのかまではまだ分からないので、ほぼTestCombatActionクラスをコピーしただけです。

一個足りない事が判明しました。逃走が成功したのか失敗したのかがユーザーから不明な事です。

GameCharacterクラスにReportEscapeOrNotToRPGGameModeBase()関数を作成して、それをこのTestEscapeActionクラスのBeginExecutionAction()から呼ぶ事で逃走が成功したのか失敗したのかをユーザーに伝えます。

f:id:kazuhironagai77:20200920215554p:plain

GameCharacterクラスにReportEscapeOrNotToRPGGameModeBase()関数は、基本的にはReportDamageHPtoRPGGameModeBase()関数やReportDamageMPtoRPGGameModeBase()関数と同じです。

f:id:kazuhironagai77:20200920215652p:plain

f:id:kazuhironagai77:20200920215701p:plain

単に結果をRPGGameModeBaseクラスの関数、ReportEscapeOrNot()に伝えているだけです。

RPGGameModeBaseクラスでは、ReportEscapeOrNot()関数を以下に示したように宣言しています。BlueprintImplementableEventを使用しているのでReportEscapeOrNot()関数の実装はBPで行えるはずです。

f:id:kazuhironagai77:20200920215824p:plain

ここまででテストしてみます。

f:id:kazuhironagai77:20200920215920p:plain

Buildは成功しました。

ReportEscapeOrNot()関数の実装を忘れていたのでしました。

f:id:kazuhironagai77:20200920215948p:plain

戦闘を開始して逃げるを選択しました。

f:id:kazuhironagai77:20200920220011p:plain

f:id:kazuhironagai77:20200920220020p:plain

出来ているみたいです。

今度はKUMOもLUCKを下げてみます。

f:id:kazuhironagai77:20200920220050p:plain

戦闘を開始して逃げるを選択しました。

f:id:kazuhironagai77:20200920220121p:plain

ここまでは同じです。

f:id:kazuhironagai77:20200920220142p:plain

今度は逃げられません。

f:id:kazuhironagai77:20200920220216p:plain

逃げるのを失敗すると相手の攻撃を受けてダメージが入ります。

これも成功ですね。

1.2 「逃げる」のコメントの直し

コメントも1.1で直してしまったのでここでやる事はありません。

1.3  戦闘中に選択を間違えた時の対応を考えます。

まず、どんな問題が在るがですが、

f:id:kazuhironagai77:20200920220257p:plain

戦闘中に攻撃を選択した後でも別の選択肢である魔法や道具などがクリック出来ます。

f:id:kazuhironagai77:20200920220357p:plain

クリックすると前に押した攻撃を上書きした状態になります。ただしコメントは直りません。

f:id:kazuhironagai77:20200920220431p:plain

コメント欄は滅茶苦茶なってしまいます。

ので選択ボタンを一個押したら他のボタンは押せないようにします。

f:id:kazuhironagai77:20200920220452p:plain

BP関数、HideButtonsをCombatUIウィジェット内に作成しました。

f:id:kazuhironagai77:20200920220511p:plain

これを攻撃ボタンを押した時に実行されるようにします。

f:id:kazuhironagai77:20200920220534p:plain

攻撃を選択した後は、他の選択が出来なくなりました。

ずっと消えたままだと困るので今度はボタンを表示される関数を作成します。

f:id:kazuhironagai77:20200920220555p:plain

この関数を、ReportExecuteAction()の実装部の最後に実行します。

f:id:kazuhironagai77:20200920220614p:plain

正直、ここがベストなのかどうか良く分かっていませんが、ほぼ正解だと思います。

テストしてみます。

f:id:kazuhironagai77:20200920220633p:plain

次の行動を選択する時に、ボタンが表示されました。

同様に、魔法や道具を選択した時もボタンが消えるように

f:id:kazuhironagai77:20200920220649p:plain

しました。

テストするために道具を買いに行ったらエラーになってしまいました。

f:id:kazuhironagai77:20200920220709p:plain

その理由ですが、UE4C++のRPGGameModeBaseクラスのコードを書き変えるたびにBPのCasttoRPGGameModeBaseBPk関数を作り直す必要があります。

これを忘れていました。でもこれ何で必要なんでしょう。

直ったのでテストしました。

出来ていました。

同じ方法でアイテムを選択した場合もやって行きます。

CombatItemウィジェットのOnClicked eventに以下のコードを追加し

f:id:kazuhironagai77:20200920220729p:plain

CombatUIのItemButtonがクリックされた時のeventに以下のコードを追加しました。

f:id:kazuhironagai77:20200920220746p:plain

テストします。

使用する道具を選択したら、道具全部非表示になりました。

f:id:kazuhironagai77:20200920220805p:plain

次のターンで道具を選択すると

f:id:kazuhironagai77:20200920220825p:plain

道具が現れました。

出来ていますね。

魔法を使用する時も同様にします。

f:id:kazuhironagai77:20200920220842p:plain

f:id:kazuhironagai77:20200920220851p:plain

テストします。

魔法を選択すると

f:id:kazuhironagai77:20200920220911p:plain

選択出来る魔法の一覧が消えます。

次のターンで魔法を選択すると使用出来る魔法の一覧が普通に表示されます。

f:id:kazuhironagai77:20200920220929p:plain

これで良いでしょう。

2. Design Part

家族が突然、私の誕生パーティを開いてくれる事になったので今週はここで中止します。残りは来週やります。