UE4の勉強記録

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

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する  マップの作製など Part 3

f:id:kazuhironagai77:20210328225746p:plain

<前文>

日本人が無邪気に信じる言論の自由アメリカにもないです。

英語の勉強方法に一寸だけ関係する内容を書いたりしていますが、結構な確率で英語の勉強をしている日本人で、アメリカが全ての点で日本よりも優れている国であると信じてる人に会います。10年以上アメリカに住んでいた私がハッキリ言いますが、そんな事は無いです。

言論の自由に限っても、アメリカの方がタブーは多い位です。今回、アジア系を狙った殺人事件がAtlantaで起きましたが、この事件の犯人は「このアジア人達が自分を性的に興奮させる可能性を起こすから殺した。」と動機を述べ、かつその殺された人達の職業が知れ渡った時、ほとんどのアメリカ人が「それなら殺しても仕方ないね。」と思っていたんです。

まずある種の女性を見て性的な興奮をするかどうかは個人の問題で、その人自身の責任でしょう。その責任を他人かつ一部の人種のせいにして、その人種の人達を殺す事はRacism以外の何物でもないです。それをみんなアメリカ人は分かっていたんですが、アジア系のアメリカ人はその差別を受け入れろと言うある種の忖度をアジア系に強制しているんです。

この事件がRacismであるとはっきりと述べたのは、現在、私が知る限りでは黒人のコメンテーター、一人だけです。タブーに切り込むのは非常に勇気が要りますし、彼にしたって何でアジア人のために自分のキャリアをリスクに賭けてまでそんな事を言うのかかなり悩んだはずです。

そんな中で、中国政府がアメリカ政府に面と向かって「人の国の人権問題にクビを突っ込む前に自分の国の人権問題、解決しろよ。」と言ったのは、ハッキリ言って勇敢だと思いました。

あるVtuberに対する嫌がらせで中国がかなり嫌いになっている私ですら、今回の中国政府の発言は肯定的に捉えるしかない位ですので、アメリカ国内における中国政府の評価は、アメリカのタブーに真正面から挑戦したとして、本音の部分では好意的に受け止められるでしょう。

勿論、今回の件だけを切り取って、米国の没落と中国の勃興が始まったと言うつもりはありません。これは中国人自身も良く言うように「中国人は口だけ。言う事は凄いが全然実行が伴わない。」だけかもしれないからです。しかし口だけだってアメリカのタブーに真正面から挑戦した事は評価すべきでしょう。

難民問題と日本の深謀

読書病と言う病気が本当にあるのか知りませんが、あるのなら中学時代の私は読書病でした。買った文庫本だけで800冊以上、記憶にあるだけで最低2回は読み直していました。好きな本なら数十回は読んでいましたし、それでももっと本が読みたくて本屋で本を買うフリして一冊丸々立ち読みしてしまう事もよくやりました。

その時の友達の親で読書が趣味の方が居たのですが、その人は私にどんどん本を貸してくれました。その時に読んだ本で、多分80年代位に書かれたPHPの文庫本だったと思うんですが「将来、中国から大量の難民がやって来て日本が崩壊すると恐怖を煽っている人がいるが、日本がお金持ちに成れたのは科学技術を用いて人を傷つける武器ではなく、人の役に立つ商品を作ったからだ。その事を中国に教え、その方法も中国に教えれば、中国自体がお金持ちに成って、敢えて日本に難民として来たいとも思わなくなる。」と書いてありました。

また大量のメキシコ人が国境を越えてアメリカに侵入しようとしていると言うニュースを聞いてその事を突然思い出しました。前に不法に侵入した人達は、バイデン政権下で特別にアメリカ人に成れる事が判明したので更なる不法移民がアメリカに押し寄せる事は間違いないです。

今、日本人で中国から大量の難民が来る事を恐れている人は、ネトウヨと呼ばれる一部の夢の世界で生きてる人達を除いてほとんどいないでしょう。むしろ日本人の方が中国に行って働きたいと思うくらいです。それは中国が豊かになったからです。そして中国が豊かになったやり方は、まさしく科学技術を用いて人の役に立つ商品を作ったからです。

当時の日本人はそこまで考えて中国に技術を教えていたんです。

自分の周りの国を、暗殺やクーデターのようなダーティな手段を使用してでも潰して常に弱くしておくアメリカと、周りの国にお金の稼ぎ方を教えて豊かにした日本、難民問題に関してはどちらが正解だったのでしょう。

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

<本文>

1.今週の予定

今週は、

  • DocumentのWater、Foliage Tool、Fog Effect、そしてHierarchical Level of Detailを読みます。
  • 日本語の資料で関係している箇所もチェックします。
  • UIWSの使用方法も勉強します

f:id:kazuhironagai77:20210328225957p:plain

  • Paintの見た目がオカシイ所を直します。(出来ればlayerをもっと増やしたいです。)
  • Grass toolで2種類以上の草を生やします。(見た目の違和感も無くしたい。)
  • Map1のLandscapeを完成させます。

以上をやっていきます。

WidgetのBPの整理、そしてEffectの勉強は時間があったらやります。

2.Documentの勉強

2.1 Foliage Tool

一番、先週勉強した分野に近いFoliage についてのDocumentを最初に読む事にしました。

使用しているVersionが4.26でした。4.26からFoliageの配置が変わっています。(正確には4.25から変わっていました。)

f:id:kazuhironagai77:20210328230100p:plain

2種類のMeshが追加出来るとの説明がありました。

Actor FoliageとStatic Mesh Foliageです。

f:id:kazuhironagai77:20210328230115p:plain

Static Mesh FoliageがInstance (instanced static mesh component)を使用した場合に該当しているのでしょうか?

  • 先週のBlogを読み直していたらInstanced static mesh component InstanceStatic meshから作成されたそれぞれのObjectInstanceと呼んでいました。この二つは全く違うモノなのでInstanced static mesh componentを言うときはInstance (instanced static mesh component)と呼ぶ事にします。
  • Instanced static mesh componentは以下に示したイメージのような大量のstatic meshinstancePCに負担をかけずに表示する時に使用するClassの事です。

f:id:kazuhironagai77:20210328230154p:plain

Size、Density、Erase Densityについての解説がありました。

f:id:kazuhironagai77:20210328230221p:plain

Sizeが300の時です。

f:id:kazuhironagai77:20210328230237p:plain

Sizeが1000の時です。

半円の大きさをコントロールしているのが分かります。

f:id:kazuhironagai77:20210328230252p:plain

次はDensityについてです。

Sizeが1000でDensity 0.5 で試します。

f:id:kazuhironagai77:20210328230308p:plain

生成される岩の位置は毎回変わりますが、岩の数は常に2個です。

今度は、Densityを1にします。

f:id:kazuhironagai77:20210328230330p:plain

今度は生成される岩の数が3つになりました。

Densityが高いほど、生成されるinstanceの密度が高くなるようです。

Documentを読むと1の時にそれぞれのMeshのMesh Properties に書かれているMaxのDensityに成るように設定されていると書かれていました。

これの事でしょうか?(1Kuuと言う単位は知りませんが、多分1 kilo Unreal Unitの略でしょう。)

f:id:kazuhironagai77:20210328230351p:plain

試しに10に変更してみます。

以下に示した様に大量の岩が出現しました。

f:id:kazuhironagai77:20210328230408p:plain

定量的に判断したいので2にセットして試してみます。

以下に示した様に1の時の2倍の6個の岩が出現しました。

f:id:kazuhironagai77:20210328230423p:plain

何で生成するためのInstanceの数をコントロールするParameterが2つもあるのか分かりませんが、Densityの使用方法は理解出来ました。

Erase Densityについてです。

これは、Ctrl + Shift + クリックで生成したInstanceを消す時の割合を決定するための値のようです。

0で試すと半円内の全部のInstanceが消えます。1の時は全く消えません。0.5の時は6個の内2個消えました。残り2個になったら消えなくなりました。

解説を読んだら以下の様な説明がされていました。

f:id:kazuhironagai77:20210328230451p:plain

これを読むとInstanceを消すと言うより、濃すぎるDensityでpaintした箇所を薄くするために使用するみたいです。濃く塗ってしまった箇所を初めからやり直す必要が無くなると書かれています。

次はFilterについての解説です。

f:id:kazuhironagai77:20210328230523p:plain

Filterで選択したタイプのActorにのみFoliageを追加する事が出来ます。

それだけです。

Toolの中で赤枠で囲った機能の解説です。

f:id:kazuhironagai77:20210328230540p:plain

解説を見ないで覚えてるだけで説明してみます。

  • SelectInstance1個だけ選択する時に使用するはずです。
  • Paintはいつも使用するやつです。
  • Reapplyは全く分かりません。
  • SingleInstanceを一個だけ追加します。
  • Fillは選択したlandscape全体にInstanceを追加します。
  • Eraseは多分Erase Densityと同じでしょう。

解説を見ます。

Reapplyは既にPaintした箇所のparameterを変更する時に使用するそうです。

Fillの説明が以下の様に書かれていましたが良く分かりませんね。

f:id:kazuhironagai77:20210328230607p:plain

後は、大体合ってました。

次はSelectで選択したInstanceに対してどんなActionが取れるかの説明がされていました。

End Keyだけ良く分かりません。Instanceを接地する機能なんでしょうか?

Paint、Reapply、SingleのActionについての解説がありましたが、大体知っている事なので省略します。

Fillの解説でFillはLandscape上をInstanceでFillするのではなく、Statice Mesh上をInstanceでFillすると解説されてました。

Erase Toolは選択した箇所を全部消すようです。Erase Densityは指定したDensityに減らすので機能としてはReapplyに近かったです。

Cullingについてです。

f:id:kazuhironagai77:20210328230627p:plain

遠くのMeshはCullingされて作成されません。その距離を指定するのがMaxのようです。しかし01でいきなりCullingをすると見た目が変になるのでCullingを始める距離をMinで指定して完全にCullingでmeshを消す距離をMaxで指定するようです。

LODについてです。

LOD自体の細かい設定を理解していないので、パスします。

Lightingについてです。

Lightingで注意しなければならないパラメーターは2つだそうです。

まずLight Map Coordinate Indexの値とUV channelの値を同じにする必要があるそうです。

f:id:kazuhironagai77:20210328230649p:plain

f:id:kazuhironagai77:20210328230658p:plain

Mesh Editorで確認しましたが、同じ値に初めからセットされているようです。

次はLight Map Resolutionです。

f:id:kazuhironagai77:20210328230716p:plain

f:id:kazuhironagai77:20210328230723p:plain

この数字は小さくないといけないと説明されていました。

その理由は(Defaultでは100個の)InstancesのShadow mapを一つのクラスターにして保存するのでそのクラスターのTextureのResolutionが4094x4096を超えないようにするためだそうです。

64 x 10 = 640 なので十分小さいでしょう。

Foliage Scalabilityについてです。

Foliage Scalabilityとは何ぞやと言う事ですが、FoliageにあるStatic Mesh Foliageの使用方法の説明のようです。

f:id:kazuhironagai77:20210328230744p:plain

先週、Landscape Grass Typeの使用方法について学んだので、Static Mesh Foliageの使用方法も勉強しておきましょう。

Static Mesh Foliageを作成したらMeshにStatic mesh を指定して、Enable Density Scalingをチェックします。

f:id:kazuhironagai77:20210328230824p:plain

作成したStatic Mesh FoliageをPaintのAdd Foliage Typeに追加します。

f:id:kazuhironagai77:20210328230841p:plain

Paintで塗った後です。

6個の岩があります。

f:id:kazuhironagai77:20210328230902p:plain

Playしてゲームを実行します。

@を押してコマンド入力可能にします。

foliage.DensityScale 0.1 を入力します。

f:id:kazuhironagai77:20210328230920p:plain

岩の数が1個に減りました。

こうやって使用するそうです。

以上でした。

2.3 Water

次は水のDocumentを読んでみます。

来ました。

Water pluginの説明です。

f:id:kazuhironagai77:20210328230941p:plain

これの使い方が勉強したかった。

まず、ビデオがありました。35分もあります。

f:id:kazuhironagai77:20210328230958p:plain

見ました。

高等なテクニックは全く理解出来ませんでしたがLandscapeを作成した後、

f:id:kazuhironagai77:20210328231013p:plain

をドラックして追加すれば海が出来るみたいです。

更に、

f:id:kazuhironagai77:20210328231027p:plain

を追加すれば湖が出来き、

f:id:kazuhironagai77:20210328231041p:plain

を追加すれば川が出来るみたいです。

取りあえず、これを試してみます。

何か出来るんですが、サンプルの様にLandscapeの形状が勝手に変わってくれません。

自分でLandscapeのScriptで形状を変化させる必要があります。

凄い調べたら答えらしきものが分かりました。

Landscapeを作成する時に、以下に示したEnable Edit layersをチェックする必要がありました。

f:id:kazuhironagai77:20210328231105p:plain

やっと以下のような海、湖、そして川を含んだ地形を作成出来ました。

f:id:kazuhironagai77:20210328231122p:plain

見た目もしょぼいし、川と海や川と湖のつなぎ目も変ですが、最初の一歩としては十分です。

それではDocumentを読んでいきます。

Enabling and Using Water

Water pluginを使用出来る状態にする手順が説明されています。

Water Tool Features

このpluginに付属のToolについてです。具体的なToolの使用方法はこの後で解説しています。

Water Body Actors and Spline-based Tools

Water Body Actorには以下の種類があります。

f:id:kazuhironagai77:20210328231146p:plain

先程使用したのは、Ocean、Lake、そしてRiverです。

Water Body Actor の特徴はa spline-based workflowです。

Water Body Actorは以下の事が出来ます。

  • 移動、回転、拡大縮小、そして複製。
  • Alt + ドラッグで新しいSpline 点を作成。
  • 水の流れるスピード、深さそして音をそれぞれの点で調節。
  • 水の特徴を調節するために、それぞれの点からメニューにアクセス出来る。
  • Landmass pluginにある非破壊のLandscape Edit Layersを使用する事で自動的にLandscapeに干渉出来る。

最後のLandscape Edit layersを使用するためには、Landscapeを作成する時にEnable Edit layersをチェックする必要があります。

Landmass Pluginは既にチェックされているので使用するために時に何かをする必要はないです。

f:id:kazuhironagai77:20210328231215p:plain

Gerstner Waves

正直、初めて名前を聞きました。これは波の動きを表す方程式のようです。

この方程式については後で詳しく調べます。

Fluid Simulation and Gameplay Physics

波にさらわれた浮遊物や波の波紋、水飛沫、そして泡を作成するための機能のようです。

表示されているイメージにGIFと書かれれていたのでクリックしたらイメージが消えてしまいました。

force impulsesについての説明があります。浮遊物を表すactorそれぞれが持つ値で、その浮遊物の掛かる力を表すパラメーターだそうです。でも実際は良く分かりません。使って見ないと。今回はこんなパラメーターがあるよ。位の理解にしておきます。

Water Mesh Actor

全ての海、湖そして川は一枚のWater Mesh Actorで作られているそうです。

川と海の様に重なっている箇所は一枚のメッシュを共同で使用するみたいです。本当でしようか?

先程自分で作成したヤツの川と海のつなぎ目をBrush Wireframeにして表示してみました。

f:id:kazuhironagai77:20210328231237p:plain

川と海のメッシュの大きさは違いますが、確かにつながっています。(赤枠で囲った部分)

GIFのアニメーションがこのWater Mesh Actorの特徴を分かり易く説明していました。

簡単に説明し直すと。

最初は以下の図の様に成っています。

f:id:kazuhironagai77:20210328231255p:plain

中心と端のVertex以外のvertexが以下の図の様に移動します。

f:id:kazuhironagai77:20210328231311p:plain

これで波が起きる状態を表します。

天才的なアイデアです。

Vertex Shaderでこの動きを管理しているんでしょうか?

先程の海と川と湖を作成したプロジェクトでWater Mesh Actorを調べたらありました。

f:id:kazuhironagai77:20210328231327p:plain

このクラスのパラメーターで重要なヤツの解説がされています。

一回も使った事がないパラメーターの役割を説明されても理解出来る訳在りません。スキップします。

一応以下のパラメーターについての説明があったとだけまとめておきます。

  • Force Collapse Density LevelTile SizeExtent in TilesTessellation FactorLOD ScaleFar Distance Material、そしてFar Distance Mesh Extentです。

Rendering Features

Renderingの中で重要な部分を説明している様です。

Underwater Post Process materialは、水中からの景色を担当しているみたいです。

川と海の水の繋がりについて

川のActorが担当しているみたいです。

このDocumentに例として紹介されている川と海の繋がっている部分は非常にきれいです。

私が作成したのを以下に示しますが川と海の違いがはっきりしています。

f:id:kazuhironagai77:20210328231352p:plain

これって直せるんでしょうか?

Caustics generation tools(水面のキラキラを作成するツール)

流石にこれをいじるレベルにはまだ達していませんので今回はスキップしておきます。

Water Bodies

海、湖、川などの紹介です。

以上です。

2.4 Fog Effect

こんな沢山のDocumentの集まりでした。

f:id:kazuhironagai77:20210328231418p:plain

Level designに霧は必要ですが、霧を追加するのは最後で良いので今回の勉強はパスしておきます。

2.5 Hierarchical Level of Detail

Hierarchical Level of Detail ってHLODの事だったです。何か勘違いしていました。Level designの高レベルな(つまり抽象的な)構造の説明だと思っていました。

これも今回はスキップします。

3.Landscapeの作成に関しての勉強:日本語資料

マテリアルデザイン入門」にstatic mesh の代わりにInstance (instanced static mesh component) を使用する事でmemoryの負担を大幅に減らす事が出来る話が普通に解説されていたと思ったら、単なるMaterial instanceの作成方法でした。全く違う話でした。

Landscapeに関係している13章から15章まで軽く読みました。それぞれの話は大変興味深いですがMaterial 中心の話なのでmaterialの勉強する時にじっくり読む事にします。

一番重要なのは、遠距離と近距離でTextureのサイズを変える方法ですが、

個人的に特に面白いと思ったのは、

  • World Position Offsetノードの解説。
  • Two Side Signノードと法線の向きの関係を図で解説した所

でした。

World Position OffsetもWater Mesh ActorもVertex Shader内で頂点を動かしているんでしょうか?

Two Side Signノードと法線の向きに関してですが、法線は物理を勉強してない人には馴染みのない概念です。法線に対して何かしたら予想と違う挙動をして困ってしまった。と言う経験をした人は結構多いと思います。コラム内で法線の挙動を表すために使用されている図は、法線の挙動を非常に分かり易くかつ正しく可視化していて、驚嘆しました。

法線に関してですが、昔、Vertex shader内で頂点の法線を正規化(normal vectorをnormalize!?)するとFragment shader(direct xではpixel shader ?)内でその正規化した法線を元にpixel毎の法線が生成されますが、その法線は単に正規化した頂点の法線を補間しただけで、pixel毎の法線が正規化した訳ではないのでFragment Shader内でpixel毎の法線をもう一度正規化すると影の付き方がこんなに違うと立方体を例にして見せた時がありました。

興味すら持ってもらえなかったです。

この法線の図も多分、興味持って貰えないのでコラム送りになったのでしょうが、私的には読んでいて大興奮する箇所でした。

4.UIWSの使い方について

せっかくWater pluginの使い方を覚えましたが、今作成しているゲームのVersionは4.24なのでwater pluginは使えません。

代わりにUIWSを使用しようと思います。

f:id:kazuhironagai77:20210328231524p:plain

このPluginのdocumentを見たのですがあまり詳しい説明はないですね。YouTubeには何個かTutorialがありました。

取りあえずInstallしてYouTubeのtutorialを見てみます。

何個かYouTubeのtutorialを見た所、以下のUIWS ManagerをLevel内に配置して

f:id:kazuhironagai77:20210328231550p:plain

更にUIWS Water Bodyを配置すれば、水面が出来るみたいです。

f:id:kazuhironagai77:20210328231604p:plain

こんな感じでした。

更に、Third Person Character が水に干渉出来るようにThird Person CharacterのMeshのRenderの以下の設定を変更しました。

f:id:kazuhironagai77:20210328231619p:plain

水中を歩いていると水しぶきや波紋が見えます。

f:id:kazuhironagai77:20210328231634p:plain

これだけしか分かりません。

正直、water-pluginを見た後だと、かなりしょぼく見えます。

そうだ提供されているサンプルを見てみます。

View OptionのShow Plugin Contentをチェックして

f:id:kazuhironagai77:20210328231650p:plain

UIWS Contentを表示させます。

f:id:kazuhironagai77:20210328231706p:plain

その中にあるUIWS Demo Levelを開きます。

f:id:kazuhironagai77:20210328231722p:plain

水飛沫が表現されています。

f:id:kazuhironagai77:20210328231736p:plain

Water pluginを見た後だからかもしれませんが、正直な感想を言うと、水飛沫じゃなくてプラスチックの削りカスが生成されているみたいです。

f:id:kazuhironagai77:20210328231754p:plain

キャラを滝に飛び込ませるとしっかり水飛沫がキャラに反応しています。

反対側の滝にも飛び込んでみました。

f:id:kazuhironagai77:20210328231810p:plain

こっちの滝はかなり滝でした。水飛沫の量が多すぎるとプラスチック感が出るみたいです。

生成された水面を見ています。こっちはかなり水面しています。

f:id:kazuhironagai77:20210328231826p:plain

水面下からの画像です。

f:id:kazuhironagai77:20210328231840p:plain

これも水面下から見てる感は凄いですがWater pluginと比較すると一寸落ちる気がします。

まあ、文句ばかり書きましたが、十分凄いですので、これを使用して海と川を作成する事にします。

川も作成してみました。

f:id:kazuhironagai77:20210328231900p:plain

川の頂点を増やす方法は、頂点を選択した状態でalt + ドラックでした。Duplicateを作成するのと同じ要領でした。

Third Person Characterと川が干渉しません。

f:id:kazuhironagai77:20210328231915p:plain

製作者が結構真剣にUserからの質問に答えてると聞いたので、同じ疑問を持った人もいるかもと思い調べたらありました。

f:id:kazuhironagai77:20210328231931p:plain

Uiws boyのBPを開いて見るとRippleとコメントに書かれた箇所でApply Force at Locationが使用されていました。

f:id:kazuhironagai77:20210328231945p:plain

この関数の使用方法が分かれば川にキャラが侵入した時に波紋や水飛沫が発生するように出来そうです。

この関数と川との干渉は後で検討します。

滝も川を曲げて作成しました。

f:id:kazuhironagai77:20210328232002p:plain

正面から見るとかなり凄いですが、横から見ると単なる線です。

f:id:kazuhironagai77:20210328232027p:plain

先程の水飛沫ですが、Particle systemで作成していました。

f:id:kazuhironagai77:20210328232043p:plain

プラスチックの破片が飛んでいるみたいと散々悪口言いましたが、Particle systemから自作するのは大変なのでこれを使用する事にします。

以下の水飛沫用に以下のParticle が提供されていましたので、これらも使います。

f:id:kazuhironagai77:20210328232059p:plain

残りは川や海を作成しながら勉強するようにします。

5.Landscapeの直し:準備編

5.1 Tutorialで勉強

偶然でしょうがYouTubeのお勧めに「How to HIDE Texture REPETITION in Unreal Engine - UE4 Tutorial」と言うLandscape作成のビデオが表示されていました。そこでこんな駄目なLandscapeを制作していませんか?と紹介されていた駄目なLandscapeが、私が作成したLandscapeをそのまま載せたんじゃないかと言うくらいそっくりでした。

そのビデオで紹介されていた駄目なLandscapeの例

f:id:kazuhironagai77:20210328232137p:plain

先週、私が作成したLandscape。あんまりにもそっくり。

f:id:kazuhironagai77:20210328232155p:plain

お茶飲みながら見てたんですが、もう少しでこぼすとこでした。

これは全部見るしかないと思い、ちょっとそのTutorialで勉強する事にしました。

  • 質の高いTextureを使用する。Mega ScanTextureを使用する。
  • Macro variationのあるtextureを使用する。
  • Distance blendを使用する
  • Materialを変化させる。

などが解説されていました。

うん。

このTutorialに沿ってやり直しますか。

やり直しますが、それはLandscape作成の基本を勉強すると言う意味でやり直します。Photo realistic renderingにシフトする訳ではないです。

マンガの絵がオカシイと言われ続けウン十年ですが、写実的に正しくなくてもマンガは売れています。

ゲームはほとんどやらない私ですがアニメや漫画は良く見ます。アニメや漫画でよく言われるのが「キャラは記号である」と言う事です。あるキャラは“強い”という記号を表している。別なキャラは“美人”という記号を表している。と言う意味です。

この記号と言う概念は、キャラだけでなくその世界観にも繫がります。

例えば、カイジと言うマンガは独特の世界感があります。その作者の独特な絵柄がその独特な世界観を表す記号になっていて絵柄をパッと見るだけで読者は、ああ「カイジの世界なのか」と納得します。

写実的に正しい描写はこの物語において大切なそのキャラが持つ記号やそのゲームが持つ世界観を曖昧にしている気がします。

その辺の現実は受け入れた上で勉強します。

5.2 Landscapeもう一度

この作者の前のtutorialで最初からLandscapeを作成しているのがあります。Unreal Engine 4 Beginner Tutorial - Create Worlds in UE4です。それで勉強する事にします。ただそのTutorial、4時間もあります。取りあえず最初の10分だけ見てみます。

10 分だけ見ました。本当の初心者向けのTutorialです。Unreal Engineのinstall 方法から説明していました。

Mega Scanを使用するやり方は2時間後から始まっています。しかも5分で終わっています。その後からLandscapeの作成が始まっていますのでこの後半の2時間だけ見れば十分みたいです。

ですが、復習も兼ねて一応全部見てみます。

5.3 Unreal Engine 4 Beginner Tutorial - Create Worlds in UE4を勉強して:前半部

お菓子を食べながら映画でも見る感じで軽く見ました。

それぞれの章で知らなかった事や、大切だと思った事を以下にまとめました。

User Interface

この赤で囲った画面の部分の名前知っていますか。3D viewportと言うらしいです。

f:id:kazuhironagai77:20210328232310p:plain

4.25からModeがtoolに移行してModeのある場所にはPlace Actorが配置されているんですね。私は今しばらく4.24でやっていくのでこの辺は後の楽しみとします。

このオレンジの三角をクリックすると、タブが表示されるのも知りませんでした。

f:id:kazuhironagai77:20210328232332p:plain

こんな感じです。

f:id:kazuhironagai77:20210328232346p:plain

<3D Navigation>

Standard NavigationとGame style Navigationの二つの操作方法があるのは知りませんでした。自分がどんな方法で操作しているのかもう意識していないので分かりません。多分全部知っているでしょう。

<Viewport Settings>

この部分の説明です。

f:id:kazuhironagai77:20210328232428p:plain

この意味知っていますか。

f:id:kazuhironagai77:20210328232446p:plain

ExposureってHDRの事だと思うんですがLevel designの時それをOn Offするためだそうです。

<Manipulating Object>

Gを押す事で、以下に示したようなeditorでのみ表示されるウィジェットの表示を消す事が出来るそうです。  

f:id:kazuhironagai77:20210328232533p:plain

これも知りませんでした。

<Intro to Materials、Physically Based Rendering、Migrating and Downloading Unreal Assets>

特になかったです。

<Advance Materials>

色を管理するTextureのみsRGBにチェックを入れます。

f:id:kazuhironagai77:20210328232624p:plain

Normal map や Roughnessを管理するTextureはsRGBのチェックを外します。

何となくは覚えていましたが、念のために記録しておきます。

Normal mapにはDirect XタイプとOpenGLタイプの2種類があってUE4はDirect Xタイプを使用しています。

OpenGL用のNormal mapを使用する場合はTexture のFlip Green Channelにチェックを入れます。

f:id:kazuhironagai77:20210328232646p:plain

正直この事は全く知りませんでした。

<Creating a 3D asset>

特にないです。

<Reflections and Post Process>

Screen space reflection とその停止方法が知らなかったです。

Screen space reflectionの停止方法について解説します。

Post Process Volumeを追加します。(下の図はView modeでReflectionを選択しています)

f:id:kazuhironagai77:20210328232735p:plain

Rendering FeaturesのScreen Space ReflectionsのIntensityをチェックします。

f:id:kazuhironagai77:20210328232752p:plain

値を0にセットします。

Screen space reflectionがOffになりました。

f:id:kazuhironagai77:20210328232809p:plain

床に移っていた家が消えました。

<Lighting: Static vs Movable>

特にないですが、Lightに関しての自分の知識が正しかった事の確認は出来ました。

<Lighting: Types of Lights>

Rect lightを知らなかったです。

Sky lightとatmospheric fog の関係を説明していたのですが良く理解出来なかったです。この辺はLighting の勉強する時にしっかり復習します。

Static lightingとStationary lightingの具体的な違いを知りました。Stationary lightingはDynamic shadowをcast出来ます。

2020-11-08のブログ

f:id:kazuhironagai77:20210328232857p:plain

と書いていましたが、この辺の理由もStatic lightingとStationary lightingの違いから説明出来そうです。

<Lighting: Build Lighting - realistic interior>

これは、実際に提供されているsampleをdownloadして自分で試さないと完全な理解は出来ませんね。

建築業界の人には必須かもしれませんが、あんまり興味ある話ではないです。

5.3 Unreal Engine 4 Beginner Tutorial - Create Worlds in UE4を勉強して:Mega Scanの使用

地面に岩のTextureがタイルのように繰り返し貼り付けられているのを目立たなくするためには、まず「質の高いTextureを使用すべき。Mega ScanのTextureを使用すべき。」と「How to HIDE Texture REPETITION in Unreal Engine - UE4 Tutorial」で説明されています。

そのためにはUE4からMega Scanの使用方法を勉強しなければなりません。

ここでします。

Bridgeをdownloadすれば良いみたいです。

適当に入力してたらDownload出来たみたいですが、このTutorialの説明、親切じゃないです。

調べたらMegascans to Unreal Engine 4 Workflow - How to install Bridge plugin in UE4にdownloadするための手順が一つ一つの説明されていました。こっちのやり方に沿ってやればよかったです。

以下の箇所のDefault Projectの部分に適当なFolderを指定したのですが大丈夫でしょうか?

f:id:kazuhironagai77:20210328232943p:plain

「Megascans to Unreal Engine 4 Workflow - How to install Bridge plugin in UE4」では何も指定していませんでした。

Projectを開いて見たらMega scansのTool buttonが出来ていました。

f:id:kazuhironagai77:20210328233016p:plain

よし、試しに何かBridgeからUE4に送ってみます。

兎に角、質の高いRockのTextureがほしい私は、Rockで検索して適当に選択していったら以下のAssetが出て来たのでこれをDownloadします。

f:id:kazuhironagai77:20210328233045p:plain

TutorialではBridgeの右側に設定を選択出来るDetailみたいなのが表示されていますが、私のにはありません。

f:id:kazuhironagai77:20210328233104p:plain

ないんですが、右上の矢印をクリックしたらDownload出来ました。そしてもう一回クリックしたExport出来ました。

UE4をみたらDownloadされています。

f:id:kazuhironagai77:20210328233123p:plain

出来たと思ったら、全部4kじゃないですか。

やっぱりBridgeのDetail画面をどうにかして表示して2kに設定しないと。

Assetをクリックして選択した状態になったら普通に表示されました。

f:id:kazuhironagai77:20210328233141p:plain

もう一回やります。

f:id:kazuhironagai77:20210328233157p:plain

今度は2kでImport出来ました。

一応Bridgeからassetを得る事が出来るようになったので、Unreal Engine 4 Beginner Tutorial - Create Worlds in UE4に戻ります。

5.4 Unreal Engine 4 Beginner Tutorial - Create Worlds in UE4を勉強して:Landscapes

重要だと思った事をまとめておきます。

Section、Componentについての説明はありませんでした。しかしLandscape Technical Guide のリンクを紹介してそこで紹介されている設定から合っているのを選べば良い。と説明していました。

Landscapeにおいて特別な技法は紹介していませんでした。

山や谷を本物そっくりに作成する方法が紹介されるのかと期待したのですが、そういうのは無かったです。本物そっくりのLandscapeが作成したい時は、World machineやGaiaを使用して作成するそうです。「マテリアルデザイン入門」でもLandscapeはWorld machineを使用して作成したものをHeight mapにしてImportしたと書かれていました。

一寸だけ調べて見ます。

World machineで調べたら、以下のソフトが検索で出て来たのですが、

f:id:kazuhironagai77:20210328233233p:plain

似たような名前で、World Creatorと言うソフトもありました。

f:id:kazuhironagai77:20210328233256p:plain

World machineと言っているように聞こえるですが、ひょっとするとWorld Creatorと言っていたのかもしれませんね。

Gaiaも調べて見ます。

これの事かと思ったらこっちも

f:id:kazuhironagai77:20210328233316p:plain

Gaeaと言う似た名前のソフトがありました。

f:id:kazuhironagai77:20210328233336p:plain

LandscapeがUE4より簡単に作成出来かつ無料ならこっちのソフトの使用も考えてみます。

後で、検討しましょう。

Paintの設定の仕方は、先週勉強した「Landscape Essential Concepts」のPaintのやり方とほとんど同じでした。確認のために「マテリアルデザイン入門」も見ましたら、こっちでも同じやり方していました。

あんまりにもやり方が同じ過ぎて、ちょっと気持ち悪い位です。

5.5 Unreal Engine 4 Beginner Tutorial - Create Worlds in UE4を勉強して:Foliage

特にないです。

5.6 Unreal Engine 4 Beginner Tutorial - Create Worlds in UE4を勉強して:Blueprints

Tick関数を使用した時に、Delta secondで時間を調整していたのですが、私の作成中のゲーム、このやり方でやってなかったかもしれません。後で確認します。

初心者にはかなり難しい内容のような気がします。作れるんでしょうか?

5.7 Unreal Engine 4 Beginner Tutorial - Create Worlds in UE4を勉強して:Creating a Photorealistic World

のっけから、超重要な情報がありました。

f:id:kazuhironagai77:20210328233412p:plain

UE4であるfolderをMove toした後、前のFolderが残っているので消去しようとすると出来なかったり、「これこれのfileが残っています。本当に消していいんでしょうか?」と聞かれたりします。その時はこのFix Up Redirectors in Folderを選択するそうです。すると前のFolderに残っているfileが無くなって安全に消せるようになるそうです。

試してみました。

Imported Assets フォルダーを作成してその中にGood Skyを移動させました。

f:id:kazuhironagai77:20210328233430p:plain

残ったGood Skyのフォルダーを消そうとすると、消せません。

Contentを選択して、Fix Up Redirectors in Folderを選択します。

すると残ったGood Skyのフォルダーが消せました。

Landscape内をスムーズに移動するためには、Camera Speedの値を最大にすると良いみたいです。

f:id:kazuhironagai77:20210328233448p:plain

流石に一時間以上の作成は見ていられなくて途中で飛ばしてしまいました。

5.8 Unreal Engine 4 Beginner Tutorial - Create Worlds in UE4を勉強して:まとめ

ハッキリ言って直せる所と直せない箇所がある事が分かりました。

  • Textureは、Mega Scanを使用する。->出来るようになった。
  • Photorealistic なLandscapeは別なソフトで作成する必要がある。->今日は無理。

しかしTutorialの内容は理解したので次の「How to HIDE Texture REPETITION in Unreal Engine - UE4 Tutorial」を勉強してタイルのようなTextureを直そうと思います。

5.9 How to HIDE Texture REPETITION in Unreal Engine - UE4 Tutorialを勉強して

もう一回、このTutorialを見ます。

<質の高いTextureを使用する。Mega ScanのTextureを使用する>

Mega Scanを使用出来るようにしました。

<Macro variationのあるtextureを使用する>

GoldのmaterialにMacro variationがあるみたいに言っていたので、開いてみたらありました。

f:id:kazuhironagai77:20210328233607p:plain

ありましたが、これで距離によってTextureが変わるのでしょうか?

良く分かりません。

<Distance blendを使用する>

やり方は大体理解出来ました。「マテリアルデザイン入門」に書かれている方法と同じに見えますが、作成した後でそれも検討します。

<Materialを変化させる>

Palinノイズを使用して地面と草を分けたりしています。

6.Landscapeの直し

それでは先週作成したLandscapeを直していきます。

Foliageは邪魔なので外します。

f:id:kazuhironagai77:20210328233707p:plain

6.1 Landscapeの直し

UE4のLandscapeではphotorealisticな地形を作成するのは限界があるのは分かりましたが、だからと言って以下のようなlandscapeで良い訳はないです。

f:id:kazuhironagai77:20210328233731p:plain

出来るだけ自然な形になるように直していきます。

f:id:kazuhironagai77:20210328233820p:plain

6.2 Mega Scanから Dirt MaterialをImport

soil_dirt_pjExP0をMega ScanからImportしました。

f:id:kazuhironagai77:20210328233841p:plain

Material Functionに変更して

f:id:kazuhironagai77:20210328233901p:plain

M_Landscapeに使用してみます。

f:id:kazuhironagai77:20210328233920p:plain

こんな感じになりました。

f:id:kazuhironagai77:20210328233936p:plain

近づいて見ると、タイルタイルしています。

f:id:kazuhironagai77:20210328233957p:plain

6.3 Macro variationのあるtextureを使用する

M_Metal_Goldから丸々コピーしました。

f:id:kazuhironagai77:20210328234032p:plain

近くを見ると、Textureがタイルタイルしているのは変わりませんが、遠くが斑模様になってちょっとだけリアルに見えます。

f:id:kazuhironagai77:20210328234051p:plain

以下にMacro variationを外した状態を示します。一目瞭然です。

f:id:kazuhironagai77:20210328234107p:plain

6.4 Distance blendを使用する

Distance blendを作成する前に、Textureの大きさを変えたら本当にタイルタイルしている状態が消えるのか試してみました。

0.25を掛けています。Textureは4倍の大きさになっているはずです。

f:id:kazuhironagai77:20210328234129p:plain

それでもタイルタイルしています。

f:id:kazuhironagai77:20210328234154p:plain

このTextureは駄目なヤツかもしれません。

別なTextureに替えました。

f:id:kazuhironagai77:20210328234219p:plain

既にタイルタイルしていません。

一番タイルタイルしている距離でも以下に示したようにほとんど分かりません。

f:id:kazuhironagai77:20210328234245p:plain

ゲームを始めてもTextureがタイルタイルしている所はないです。

f:id:kazuhironagai77:20210328234305p:plain

一応勉強と言う事で、Dirtの方だけ実装してみました。

f:id:kazuhironagai77:20210328234345p:plain

TutorialではDistance_Blendのパラメーターは単なる数字ですが、

f:id:kazuhironagai77:20210328234409p:plain

以下の様になっています。想像しているよりもかなり複雑な関数のようです。

f:id:kazuhironagai77:20210328234428p:plain

結果の違いは良く分かりませんが、

f:id:kazuhironagai77:20210328234446p:plain

敢えて結果の違いが可視化されている箇所を探すと

f:id:kazuhironagai77:20210328234507p:plain

数メートル下がるとイメージが以下の様に変化します。

f:id:kazuhironagai77:20210328234527p:plain

正し、Textureが良いので、イメージが変化しなくてもタイルタイルはしません。

作り方は分かりました。

6.5 Materialを変化させる

これ実際に作成してしまうと、折角塗った個所が上書きされてしますので、コードだけ真似て見ました。

f:id:kazuhironagai77:20210328234558p:plain

この状態でUVをTexCordから直接とるのは間違っている気がしますが、今回は形だけの作成なので深くは検討しません。

残念ながら時間が無くなってしまったので今週はここまでです。

7.まとめと感想

今週は以下の事をやりました。

  • DocumentLandscape関連の所を読みました。WaterFoliage Toolです。
  • Fog Effect、そしてHierarchical Level of Detailは、直接は関係なかったので今回は読まなかったです。
  • 日本語の資料は「マテリアルデザイン入門」を少しだけ読みました。
  • UIWSの使用方法も勉強しました。
  • LandScapeの直しとして「How to HIDE Texture REPETITION in Unreal Engine - UE4 Tutorial」と「Unreal Engine 4 Beginner Tutorial - Create Worlds in UE4」を勉強しました。
  • そこで勉強した知識を活用して先週作成したLandscapeを改善しました。

来週は

  • Landscapeの直しの続き(Tutorialの2と3も勉強する)
  • Foliageの改善(Cull Distanceの使用)
  • 水の追加
  • Projectに使用する実際のLandscapeの作成

などを行いたいと思います。

今週はかなり疲れました。

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する  マップの作製など Part2

f:id:kazuhironagai77:20210321230424p:plain

<前文>

引用とネタバレ。もしくは批評と著作権

引用の英訳はCitationだと思います。何で和英辞典には引用の英訳としてQuotationが紹介されているんでしょうか?Quotation って‘’の事でしょう。論文の書き方でも引用について説明する時はCitationって書いてあります。

しかし心配なので調べて見たら、英語のCitationとQuotationの定義も結構曖昧でした。その中で以下に示した3つの意見が多かったです。

  1. 同じ
  2. イデアを引用するのがCitation。文章をそのまま引用するのがQuotation
  3. ‘‘の中の引用した文章がQuotation。どこから引用したのか説明した部分がCitation

因みに、文章内で引用元を説明したのがCitationで、文章の下に引用元を詳しく解説したものがReference(参照)と言う説明もありました。

これ論文だったら、その論文を発表する雑誌の編集方針に従う訳で、別な雑誌が別な基準を示していたとしても、基準を明確に示してくれさえすれば問題ないです。

同じ事が著作権についても言えるわけで、国によって著作権の考え方が違っても明確に基準さえ示してくれれば問題ないはずです。

私は著作権について漠然と理解したのはアメリカに住んでいた時だったので、多分にアメリカ人の考える著作権的な考えが自分の中で正しい著作権として存在しています。そしてその考え方は日本でも通用するとずっと思っていました。

所が最近、アメリカ人の友人から「日本の著作権にはFair useの概念がない。」と言われかなりビックリし、本当かどうか調べました。その過程で日本の著作権に対する考え方を理解し更にアメリカの著作権と比較する事で、結構著作権の本質的な部分に気が付きました。

勿論それは、私の主観ですので、法律的に正しい訳ではないかもしれませんが、一遍の真実を含んでいると思いますのでここに書き記しておきます。

アメリカ人の考える著作権とは>

まず、アメリカ人の考える著作権について説明します。

最初に断っておきますが、あくまで一般的と言う注釈付きです。ので絶対アメリカ人全員がこの考え方を持っているとは断言はしません。ただしキリスト教徒かつ、大卒レベルのアメリカ人、大学でしっかり勉強したアメリカ人なら絶対共有しているイメージだとは、私は思っています。

宇宙は神様が作りました。そこから地球や海とか動物、そして最期に自分に似せて人を作りました。

では、人が作成したモノの所有権は誰にあるのでしょう。

はい。人が作成したモノの元は全て神様が作成したので…。

神様のモノです。

これは幾つかバリエーションがあって、人が作成したものはその人から神様が具現化したと考える人もいます。しかし結論は一緒で、全てのモノの所有権は神様に属します。

この全てのモノは神様のモノと言う考え方はまあ、日本人である私でも、納得出来るのですが、教会が全部持っててしまうとなると、全然納得出来ません。

それとはまったく別に、これ神様が作った物を元にしては作れないよね。というモノがあります。例えば、地球が丸い事を発見した事です。その当時の教会は「地球は平らである。」と主張してたので、教会=神様である限りは、神様は地球が丸い事を発見する事は出来ません。

じゃ誰が発見したんだとなると、人が発見した事になります。

ここでアメリカ人は、人は神様に似せて作られたので、神様しか出来ない新しいモノを作成する事は可能なのだ。とかなり宗教的な納得をしますが、当時の私もそんな理解をしました。(ただし、この人の中にアジア人が入っているかどうか、かなりの数の白人は、この場合の人の定義に、白人しかも一部の白人しか入れていない気がします。)

では、その人が作成したモノの所有権は誰に属するのでしょうか?

勿論、その作った人です。

その所有権はどこまであるのか?

神様が作った物は何処まで進んでも神様のものである様に、人の作った物も何処まで進んでもその人の物であるはずです。

それを法律として明確化したものが著作権です。

だから著作権は元々、神様の持つ権利と同等の強さを持つとんでもない権利なんです。

そしてこの地上でその神様の持つ権利を管理するのが教会であるように、人の作った物の権利を管理するのは大学なんですが、著作権に代表される人の権利を、旧約聖書に出て来る神様のように行使したら、研究が全く出来ない。なので権利はあるのだけども実行出来る部分に制限を付ける事にしました。その制限の基準がFair useな訳です。

そしてこのFair useの本当の意味は「常識の範囲で自由に運用してね。」と言う事です。

そしてこのFair useの中で非常に大きなウェイトを示すのが引用なんです。

だから日本ではFair useの概念がないと言うと「えっ」なるんです。

<日本の著作権への考え方>

自分で調べた限りの感想ですが、日本の著作権にFair useの概念がないというのは全くの誤解です。Fair useと言う曖昧な概念をそのまま導入すると、何はOKで、何が駄目なのか分からないので、具体的に著作権の例外事項を定めているだけです。その証拠に引用は著作権の例外として認められています。

明治時代に西洋の文明の本質を輸入し接ぎ木したのが日本の近代化なので、例え日本にキリスト教的な神様の所有物と言う概念が無くても日本の著作権は、暗黙的にですが、人の製作物に対して神の製作物と同等の権利を認めています。のでFair useの概念が本当になかったら実際の運用が全く出来ないです。

正直笑ってしまったのが「大学受験における入試問題に使用される文章は著作権の行使が出来ない。」と定めている文章でした。

アメリカと言うか、西洋的な考えに立つと、人の作った物の権利を管理するのは大学なので、大学こそが著作権をどの程度守るのかを決める訳です。法律はそれを文章化して、違反に対しての罰則を規定する事しかできません。上の文章はまさしくそれを体現しています。日本の話ですが。

これは日本における著作権の本質は、アメリカを代表とする西洋と全く変わらない事の証明でしょう。

はい。

私の結論としては日本の著作権の考え方、実行の仕方に他の西洋諸国との本質的な違いはない。と言う事です。

<引用とは何ですか?>

はい。ここで引用とは何ぞや。という事に対して私の考えを述べます。私の考えそのものはかなりアメリカの著作権の考え方に負っていますが、今までの議論から日本の著作権にもほぼそのまま当てはまると考えられます。

引用とは「○○さんはこう言っているけど。それって違うよね。」の○○さんの言っている内容が、引用に当たります。

ここで大切なのは、それって違うよねの後が、主体になっていないといけない事です。これから何故○○さんの言っている事が間違っているのか、そして何が正しいと考えるのかについて説明しなければ引用になりません。

所謂、転載やplagiarismは「○○さんの言っている事」の紹介が主体になり、自分の意見はありません。

<引用とネタバレ。もしくは批評と著作権

でも、それだと、自分の主張を言う為なら、ネタバレしても良いって事になりませんか?

もしくは勝手に引用されてボロクソに批判されても黙っていないといけないって事でしょうか?

との疑問が出て来ます。

それで最初のFair useの概念に戻る訳です。常識に基づいて判断してと。

自分が使用する側の時は、使用される側の立場に立って考えてねとか、今までの慣習ではどうなっているのか調べて見てね?とかそう言う事です。

 

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

<本文>

1.今週の予定

以下の事をやっていきます。

  • Level デザイン
  • Widget内のBPの整理
  • Effectの勉強と作成

2.Levelデザイン: Tutorialで勉強する

2.1 使えるTutorialを探す

Level Designの超基本はこれまでの勉強で理解しました。しかしもっと勉強すべき事はあります。色々なTutorial を見て参考になる優秀なものを見つけましょう。

判断の基準として以下の事を念頭に置いておきたいと思います。

  • 川のデザインが正しい。
  • 水のMaterialに最新のモノを使用しているか、使用しない場合は理由を明確に述べている。
  • Component EdgeSection Edgeそして Individual Quadの正しい解説をしている。

まず川のデザインですが、はるか昔に勉強したLevel designで川と言うのは、以下のイラストの様に上流が分岐していて下流に行く程、一本に成るらしいです。

f:id:kazuhironagai77:20210321230713p:plain

決して以下の絵のように上流が一本で下流に行く程、沢山の支流が現れる事はないそうです。

f:id:kazuhironagai77:20210321230728p:plain

所が、Level Designの初心者はかなりの確率で上流が一本で下流に行く程、沢山の支流が現れる川を作ってしまうらしいです。

まあ、ゲームなんで科学的に正しい地形をデザインしなければならない訳ではないですが、現実の地形に対してこの程度の注意も向けられない人が作ったtutorialは、大きな欠陥がある可能性が高いので使えるTutorialには該当しないでしょう。

次に水のMaterialですが、4.26 からはWater Simulationが付いていますし、それ以前のversionでは

f:id:kazuhironagai77:20210321230745p:plain

が使用出来るからです。

Unityではなく敢えてUE4を選択する人達の理由の一つが、その最新のグラフィックにあります。その利点が教えられないUE4のデザイン系のTutorialは価値がないです。

最後に、先週勉強したComponent Edge、Section Edgeそして Individual Quadの正しい解説がされていないTutorialも質が低いと思われるので避けます。

まず公式のOnline learningからは以下の教材を見てみようと思います。

f:id:kazuhironagai77:20210321230802p:plain

f:id:kazuhironagai77:20210321230811p:plain

f:id:kazuhironagai77:20210321230819p:plain

CourseとLearning Pathはどう違うんでしょうか?

調べたけど解説は見つからないですね。

YouTubeにはUE4のLevel DesignのTutorialをまとめた人がいました。

f:id:kazuhironagai77:20210321230840p:plain

この一番最初に紹介されているUE4: Good-Looking Randomization for Procedural Buildingsを作成したTech Art AidはMeshではなくInstanceを使用する事でLevel上のサイズをとても小さく出来る事を教えてくれた貴重なTutorialです。この選択は結構期待して良いかもしれません。ここに載っている Tutorialもチェックしてみましょう。

そう言えば、「マテリアルデザイン入門」にInstanceを使用する話が普通に解説されていて何かガックリした事を思い出しました。この本も今回、読み直しましょう。

Learningの一番左にLevel design Quick Startがあります。

f:id:kazuhironagai77:20210321230855p:plain

念のため調べて見たら、いつものヤツでした。

f:id:kazuhironagai77:20210321230912p:plain

流石にこのレベルのヤツは全部理解していると信じたい。今回はこいつはスキップします。

更に、UE4公式のDocumentationでは、

f:id:kazuhironagai77:20210321230930p:plain

Water、Foliage Tool、Fog Effect、そしてHierarchical Level of Detailのような私が見たいものズバリの内容がありました。

これらのTutorialはかなり良さそうです。

f:id:kazuhironagai77:20210321231137p:plain

全然関係ないですが、hierarchyの発音はカタカナで書くと「ハイ、ラ~キィ」で決して「ヒエラルキー」ではないです。

2.2 Tutorialの候補を絞る。

絶対やりたいのはDocumentのWater、Foliage Tool、Fog Effect、そしてHierarchical Level of Detailですが、なんせ文章なんでビデオで見るよりかなり面倒です。さらに読書は他の学習方法より効果が薄いらしいので最後に回します。同じ理由で「マテリアルデザイン入門」の読書も最後に回します。

となるとやっぱり最初にチェックするのは以下に示した公式のOnline learning の3つですかね。

f:id:kazuhironagai77:20210321231202p:plain

2.3 UE4公式のOnline Learning感想

2.3.1 World Building Kickstart

Sjoerd de Jong氏のTutorialでした。

彼のTutorialはLevel designに限らずどのTutorialも見て損はないです。前に彼が作ったMaterialのTutorialを見て大変勉強になったのを覚えています。

今回は全体像を把握するためにTutorialの半分位まで見ました。Level designに使用する機能を全部簡潔に説明しています。ただこれだけ見たのでは、実際に使用出来るまでには成れなさそうです。UE4には、こんな機能があるのかと言う情報源として使用するのが最適かもしれません。

後、彼もhierarchyを「ヒエラルキー」って発音していたんですが。前にインド人の先生でhierarchyを「ヒエラルキー」って発音する人が居て、hierarchyを「ヒエラルキー」って読むのは日本人だけじゃなかった。と思った事があったんですが。

ひょっとしてイギリス英語だとhierarchyを「ヒエラルキー」と発音するの?と調べたんですが、少なくとも私が調べた範囲では、イギリス英語でも「ハイ、ラ~キィ」と発音していました。

ドイツ語?と思ってそれも調べましたが、何か全然違う発音でした。

アメリカでcomputer scienceの勉強をしたら()、{}、そして[]の読み方を知らないって事はないので、アメリカ出身じゃない事だけは確かですが、彼、何処出身なんでしょうか?

2.3.2 Becoming an Environment Artist in Unreal

最初のイントロ部分だけ見たんですが、Landscapeの作り方についての解説は無いみたいです。

良く考えて見れば、Environmentの作成はLandscapeの作成だけじゃなくて、Lightingの設定やMaterialの作成もある訳でそっちが主体なのかもしれません。というかLevel designer = Environmental artist と勝手に思っていましたが違うのかもしれません。

そう言えば「Unreal Engine 4で極めるゲーム開発」にゲーム開発に関わる専門職の説明が載っていました。

見てみるとLevel designerはEnvironmental artistが作った建物なんかをLevel上に配置するのが仕事みたいです。つまり二つは別な職種みたいです。

このゲーム開発に関わる専門職ってあくまでも「Unreal Engine 4で極めるゲーム開発」における定義だと思っていたんですが、違うみたいですね。

ここでちょっとUE4の教育システムに対して苦言を言います。

この分類がUE4を使用したゲーム開発全般に通じる職種なのか、それとももっと大きくゲーム産業全体でこのような職種分けが行われているのか知りませんが、少なくともProgrammerである人がUE4の勉強をしたいと言ったら、この職種の中のEngineer になるための勉強をしたいと言う意味になるはずです。しかし私が今までUE4で見たProgrammer向けの教材のほとんどはBPの代わりにC++をScript languageとして使用するだけのもので、とてもこの職種で言うEngineerに当たる役割が果たせるように成れるとは思えません。

ゲーム産業全体でこのような職種分けが元々存在しているのなら、ゲーム産業内でEngineer としてやって行きたいprogrammer向けにこんな教育しか提供してこなかったUE4の教育システムはちょっと問題だと思います。

ゲーム制作ってProgrammerの分野でも花形ですのでGoogleAmazonでProgrammerやっているレベルの人でもちょっとゲーム作ってみたいと思う人はいっぱいいるはずです。そういう人たちがUE4のProgrammer向けの教材見て「俺ら向けの教材じゃないね。Unityでゲーム作ろう。」となっていたとしたら悲しいです。

今は、Online learningがあるのでProgrammer向けの教育も内容のレベルが上がっているのかもしれませんが、一寸文句を言わせて頂きました。

2.3.3 Constructing Believable Environment

これもEnvironmental artist用の教材ですね。ざっと目次を見ましたが、ほとんどLightingについての話です。Environmental artistは別名、Lighting artistとも呼ばれるらしいので、これはこれで納得です。

2.4 UE4公式のOnline LearningにあるLandscapeを勉強するための教材

がありました。こっちを勉強すべきでした。

f:id:kazuhironagai77:20210321231322p:plain

イントロ部分を見た所は、Landscapeの基本についての解説のようです。

軽くですが全部見てみます。

2.4.1 Accessing Project Files Using Marketplace Redemption Codes

Redeemして提供されているsampleからProjectを作成しようとしたら、見つかりません。

散々探しても見つからないのでlauncherの方からRedemption Codeを入力したら、あなたは既にこのprojectを持っています。と返答されました。

もう一回vaultを確認したらありました。

f:id:kazuhironagai77:20210321231357p:plain

ずっとTerrain‗ECで探していました。

2.4.2 Importing Terrain from a File

Landscapeの元をHeight map Fileから作成する方法が説明されています。

f:id:kazuhironagai77:20210321231419p:plain

これが知りたかった。

前にUEC++の勉強でPerlin Noise からLandscapeを作成ました。やり方は完全に忘れてしまいましたが、その過程でイメージをlandscapeとしてimport出来る事は何となく覚えていました。

このやり方が分かれば自作した地図からLandscapeが作成出来ます。

f:id:kazuhironagai77:20210321231435p:plain

あれ、提供されたProjectにはHeightmap用のPNGがないですね。

4.25だからかもと思って4.24で作り直しましたがやっぱりないです。

まあいいです。残りは自分で調べます。

2.4.3 Creating a New Terrain

Section のサイズ、Section / ComponentそしてComponentの数を決定しました。

f:id:kazuhironagai77:20210321231457p:plain

しましたが、これらのパラメーターが何を示しているのかについての説明が全くありません。

減点1ですね。

先週、まとめた内容をもう一度ここに記しておきます。

まず、Landscapeに置いて最初単位は1m^2です。固定されています。パラメーターをどう弄ってもこれは変わりません。

次の最小単位であるSection内にこの最小単位である1m^2の面が何個入るのかを決定するのがSection Sizeです。今回は63x63個入るようにします。入る1m^2の面の数が多くなればなるほどsectionのサイズが大きくなります。だからSection Sizeと名付けています。

次の単位はComponentです。基本的にSectionとComponentは1:1の関係ですが、一つのComponentに2x2、計4つのSectionを入れる事も出来ます。

最後にComponentの数ですが、今回は2x2で4つとしました。UE4はComponentの数は1024以下にする事を進めています。

63*2*2 = 254で1を引くとOverall Resolutionの253になります。

Componentは rendering, visibility calculation, そして collision を担当しています。

あれ?

一つのComponentが一つのHeight Map用のText を保持しているから一つのComponent内のSectionの数が増えると、計算は楽になる代わりに雑な作りになるはずでしたがrendering, visibility calculation, そして collisionもComponentで担当していると全部の計算が楽になりますね。

ちょっと確認してみます。

Landscape Technical Guideによると、Componentは、Componentは rendering, visibility calculation, collision の計算をして、更に一つのComponentが一つのHeight Map用のTextを保持しているそうです。つまり先週の説明は合っています。

Sectionは大前提としてLandscape LODの計算をするための最小単位だそうです。それでSection の数を増やす事で、Componentの数が減ればパフォーマンスは向上すると書かれていました。まあそうですね。rendering, visibility calculation, そして collisionも含めて全部の計算が楽になるわけですね。

2.4.4 Understanding Components

はい。ここでしっかりComponentについて説明されていました。

Componentがrendering, visibility calculation, collisionを担当する事、一つのComponentが一つのHeight Map用のTextを保持する事、Sectionを増やす事でComponentの数を減らしてその結果、Performanceが向上する事など全部説明されていました。

2.4.5 Managing our Landscape

LandscapeのManageのSection toolのSelection, Add, Delete, Move to LevelそしてChange Component Sizeについての解説です。

f:id:kazuhironagai77:20210321231535p:plain

Selectです。

Componentを選択します。

f:id:kazuhironagai77:20210321231551p:plain

Addです。

Componentを追加します。追加出来る箇所はComponentと隣り合っている箇所のみです。

f:id:kazuhironagai77:20210321231607p:plain

Deleteです。

選択したComponentを消去します。先程Addで作成したComponentを消去しました。

f:id:kazuhironagai77:20210321231622p:plain

Move to Levelです。

f:id:kazuhironagai77:20210321231651p:plain

と説明されています。動画でも同様の説明がありました。ありましたが、この意味が分かりません。

Change Component Sizeです。

Componentの設定を変更します。

f:id:kazuhironagai77:20210321231706p:plain

これって全部のComponentの設定を変えるのでしょうか?それとも一個だけ変える事も可能なんでしょうか?

こんな機能知らなかったです。

大変勉強になりました。

2.4.6 Quiz 1

満点でした。

流石にArtiest向けなのでクイズは簡単に作られています。

2.4.7 Brush Settings and Viewport Feedback

Sculpt Toolの使用方法についての解説でした。

f:id:kazuhironagai77:20210321231732p:plain

このcircleのサイズが

f:id:kazuhironagai77:20210321231748p:plain

以下の二つのParameterでコントロールされている事についての解説でした。

f:id:kazuhironagai77:20210321231801p:plain

流石にこれは知っています。

2.4.8 The Most Common Landscape Brushes

ここでは実際にブラシを使用してLandscapeの形を変形させました。

f:id:kazuhironagai77:20210321231822p:plain

Shiftを押すと逆になるのはすっかり忘れていました。

2.4.9 Using Advanced Forms of Sculpting

更なるツールの使い方の説明でした。

2.4.10 Utilizing the Region Tools

Selectionの使用方法についての説明でした。

f:id:kazuhironagai77:20210321231850p:plain

イラストを描くときに使用する選択のLandscape版ですね。

こういう機能はいっぺんに教わっても忘れてしまいます。使いながら覚えるのが一番良いですが、何せLandscapeの作成を趣味にする訳にもいかないので覚えるしかないですね。

2.4.11 Quiz 2

また満点でした。

テスト至上主義の弊害ですね。何もToolを使える様に成っていないのにテストだけ見ると理解したかのようになっています。

2.4.12 Understanding Material Layers

流石にもう飽きて来ました。

Landscapeがしょぼくて炎上したゲームでも紹介してこの機能を使えばそのLandscapeでもこんなに綺麗に仕上がります。みたいな紹介されないと正直、勉強する気になれないです。

数えたら後、10個もLectureがあります。

あー、きついです。

ここからのTutorialではLandscapeに色付けしていくようです。

更にInstructorがここでハッキリ忠告しています。「ここで習う程度の技術では、本物そっくりのLandscapeを作成する事は出来ないと。しかしそのために使用するToolの使い方を理解する事は出来ます。」と。

Material functionについての説明でした。

もう分かりました。

Landscapeを30個位、自分で制作しないとこれ以上の勉強は机上の空論に成ってしまいます。一端ここで区切りましょう。

2.4.13 ここまでのまとめ

まず、Landscapeに関して使えるTutorialを見つけると言う目的に対しての結論ですが、公式のOnline learning にある「World Building Kickstart」で十分と考えられます。

更にこのTutorialで基本を身に付けたら、公式のDocumentにあるWater、Foliage Tool、Fog Effect、そしてHierarchical Level of Detailを勉強すべきでしょう。その上でWater Simulationか、UIWSで水の製作の仕方を勉強すると良いと思います。

Landscapeの作成には地形を色付けする必要があり、その工程ではMaterialの作成が必要になります。「World Building Kickstart」ではQuiz 2 の後のLectureで解説しています。この部分を勉強する時は「マテリアルデザイン入門」も読み直しておくべきでしょう。

後、Environment artの勉強もする必要があります。これはLandscapeとは別の分野ですが、密接に関係しています。

2.5 兎に角Landscapeを作成

今まで学んだ事を使用してLandscapeを作ってみます。

出来ました。

f:id:kazuhironagai77:20210321231932p:plain

  • ManageのAdd やdeleteを使ってComponentを減らしたり増やしたりしました。
  • Selectionでマスクして一定の場所にだけFlatしたりしました。
  • Change Component sizeを使用してComponent のパラメーターを変更したりしました。
  • Erosion, hydro erosion, noiseも使用してみました。
  • Rampで坂を作りました。

ゲーム内の風景は以下の様に成っています。

f:id:kazuhironagai77:20210321231958p:plain

正直、これでも良いんじゃね。と言う気分です。

HeightmapとしてExportしてみました。

以下のボタンを右クリックでExport出来ます。

f:id:kazuhironagai77:20210321232011p:plain

正し、この単純な方法を見つけるのに結構時間かかりました。

何かUE4のdesignerの中には結構意地悪な回答して質問者を煙に巻いている人がいますね。

気を付けましょう!

Exportしたイメージはこんな感じです。

f:id:kazuhironagai77:20210321232027p:plain

白い方が高いみたいですね。

地面にあたる部分でも結構灰色ですね。

サイズは509x382 pixelでLandscapeのResolutionと同じでした。

f:id:kazuhironagai77:20210321232042p:plain

2.6 ゲーム内に作成する予定のLandscapeと同じサイズのLandscapeを作成

実際に作成する時と同じ条件でLandscapeを作成してみます。

f:id:kazuhironagai77:20210321232105p:plain

こんな状態です。

f:id:kazuhironagai77:20210321232119p:plain

この状態でHeightmapでExportしてみます。

Medi bangで開いて見るとまっさらのheightmapの色は128に成っていました。

f:id:kazuhironagai77:20210321232137p:plain

つまり、真ん中ですね。

そこに作成する島の絵を重ねます。

f:id:kazuhironagai77:20210321232153p:plain

以下の様になりました。

f:id:kazuhironagai77:20210321232209p:plain

試しにこれをheight mapとしてimportしてみます。

f:id:kazuhironagai77:20210321232232p:plain

一応出来ました。

微調整していたら川が無くなってしまいました。

f:id:kazuhironagai77:20210321232249p:plain

失敗です。

今度は以下の様に島と川だけのheight mapにしました。

これを加工します。

f:id:kazuhironagai77:20210321232303p:plain

これでも島とそれ以外の部分の差があり過ぎました。

f:id:kazuhironagai77:20210321232324p:plain

もう一回作り直します。

f:id:kazuhironagai77:20210321232342p:plain

今度は川の段差もほとんどありません。

f:id:kazuhironagai77:20210321232359p:plain

Landscapeの形状はこれでいいです。Level designerになる訳ではないのでこの位で十分です。

2.7 Tutorialの続き

先程のtutorialの続きをやります。残り10個なので頑張って終わらせます。

2.7.1 Painting Tools

凄い。下のscreen shotを見て下さい。 本物そっくりです。

f:id:kazuhironagai77:20210321232427p:plain

しかしどうしてコレが作成出来たのかが全く分かりません。

取りあえず手順を以下にまとめます。

最初にLandscapeを選択してLandscape Materialに付属のmaterial Landsape_Materialを選択します。

f:id:kazuhironagai77:20210321232454p:plain

するとLandscapeのPaintのTarget Layers内にGrassとRockが表示されます。

f:id:kazuhironagai77:20210321232514p:plain

f:id:kazuhironagai77:20210321232523p:plain

Grassの+を押すとWeight-Blend Layer(normal)とNon Weight-Blended Layerが表示されるのでWeight-Blend Layer(normal)を選択します。

f:id:kazuhironagai77:20210321232539p:plain

するとlandscapeがGrass一色になります。

次にRockの+を押して同様にWeight-Blend Layer(normal)を選択し、以下の設定に値を変えて

f:id:kazuhironagai77:20210321232557p:plain

ブラシでLandscapeをこすると、こすった所が、岩が草からむき出しになる絵が追加されます。

f:id:kazuhironagai77:20210321232613p:plain

うーん。良く分からん。

Landsape_Materialを開いたら以下の様に成っていました。

f:id:kazuhironagai77:20210321232628p:plain

成程、ここでMFunction_GrassとMFunction_Rocksがパスされている訳ですね。

2.7.2 Painting the Terrain

「2.7.1 Painting Tools」の続きです。それだけでした。

2.7.3 Foliage Edit Mode

Foliage の追加の仕方についての解説でした。

やり方を簡単にまとめるとFoliageを選択します。

f:id:kazuhironagai77:20210321232653p:plain

Add Foliage Typeに指定のmeshを追加します。

f:id:kazuhironagai77:20210321232709p:plain

現れたアイコンをクリックするとそのMeshの詳細が表示されます。

f:id:kazuhironagai77:20210321232727p:plain

これだけです。

この方法って実際のMeshじゃなくてInstanceを使用するんじゃなかったんでしたっけ。

うーん。良く分かりません。

2.7.4 Painting with Meshes

以下に示したFoilageのtoolの使用方法を説明しています。

まずPaintです。

f:id:kazuhironagai77:20210321232748p:plain

指定されたMeshをPaintします。

f:id:kazuhironagai77:20210321232805p:plain

Selectです。

f:id:kazuhironagai77:20210321232822p:plain

PaintされたMeshから一個だけ選択します。

f:id:kazuhironagai77:20210321232840p:plain

Lasso Selectです。

f:id:kazuhironagai77:20210321232855p:plain

円の中のMeshを全て選択します。

f:id:kazuhironagai77:20210321232914p:plain

Paint Bucketです。

f:id:kazuhironagai77:20210321232928p:plain

一括でLandscape全体にMeshを追加出来るとの説明でしたが、やり方が分かりません。

2.7.5 Using the Grass Tool

Material エディター内でGrass ノードをどのように使用するかについての説明と、そのGrass ノードを使う事によって自動で草のmeshを地形に追加する方法を説明します。

以下に草のメッシュを追加した後のscreen shotを示します。

f:id:kazuhironagai77:20210321232949p:plain

やり方を簡単に説明します。

FoliageからLandscape Grass Typeを選択します。

f:id:kazuhironagai77:20210321233005p:plain

Grass_2と名付けます。

f:id:kazuhironagai77:20210321233020p:plain

Grass Varietiesにgrassを追加します。

f:id:kazuhironagai77:20210321233035p:plain

以下の様にパラメーターを調節します。

f:id:kazuhironagai77:20210321233051p:plain

Landscape_Materialを開き

f:id:kazuhironagai77:20210321233105p:plain

LandscapeGrassOutputを追加します。

f:id:kazuhironagai77:20210321233120p:plain

f:id:kazuhironagai77:20210321233127p:plain

LandscapeGrassOutputのGrass Typesに先程作成したGrass_2 を追加します。

f:id:kazuhironagai77:20210321233148p:plain

次にLandscapeLayerSampleノードを追加します。

f:id:kazuhironagai77:20210321233204p:plain

f:id:kazuhironagai77:20210321233210p:plain

LandscapeLayerSampleノードのパラメーターを以下の様にセットします。

f:id:kazuhironagai77:20210321233228p:plain

以下に示した様にLandscapeLayerSampleノードとLandscapeGrassOutputノードを繋げます。

f:id:kazuhironagai77:20210321233244p:plain

以上です。

このやり方は今まで見た事ないかもしれません。単に忘れているだけかもしれませんが。

2.7.5 Debugging Painted Layers

現在、2つのLayerを使用しています。

f:id:kazuhironagai77:20210321233307p:plain

しかし、どちらがどの程度Landscapeに使用されているのかを区別するのは大変難しいです。

これを以下の様に表示しる方法を説明しています。

f:id:kazuhironagai77:20210321233324p:plain

やり方は、LitからVisualizersを選択してそこからLayer Debugを選択します。

f:id:kazuhironagai77:20210321233341p:plain

ModesからLandscape、Paintを選択し

f:id:kazuhironagai77:20210321233410p:plain

Target LayersのLayersにあるそれぞれのMaterialのRやGにチェックを入れます。

f:id:kazuhironagai77:20210321233428p:plain

これで上記のようにlayer毎に色分けされたlandscapeを見る事が出来ます。

2.7.6 Landscape Size Guide

Landscape Technical Guideを絶対読んでおけよ。と言うかなり素晴らしいアドバイスでした。

2.7.7 Controlling Collisions

ActorとlandscapeのCollisionを正確にすればするだけMemoryの負担は増えます。

その調整をするのがLandscapeのDetailにあるCollision内のCollision Mip LevelとSimple Collision Mip Levelです。

f:id:kazuhironagai77:20210321233454p:plain

0に近い程、正確に計算します。そしてMemoryの消費も上がります。

2.7.8 Landscape Essential Concepts Next Steps

特に記録に残す内容はないです。

2.7.9 Quiz 3

2問も間違えてしまった。

Grass Toolが、BPを使用する方じゃない方だと思って即答して間違えたのが一つで、これはケアレスミスみたいなもんです。

Collision Mip Levelが何をコントロールしているのかの質問の解答ってLecture中に説明あったんでしょうか?

全く記憶に無かったです。

全然分からなかったです。

一応、全部のLectureを見終えました。

これでPaintのやり方もFoliageのやり方も大体分かりました。

達成感が凄いですが、全体を見ると全然進捗していませんね。

2.8 Paintしてみる

早速、教わったやり方でPaintしてみます。

MyPaint folderを作成して

f:id:kazuhironagai77:20210321233530p:plain

M_Ground_GrassとM_Rockをコピーしました。

f:id:kazuhironagai77:20210321233546p:plain

この二つをFunctionに変更するのですがやり方を忘れました。

調べます。

Material functionの作り方そのものを教えているサイトばかり引っかかって、MaterialをMaterial Functionに変更する方法を教えてるサイトは見つかりませんでした。

仕方ない。

Landscape essential concept内のMaterial Functionを開いてどう作っているのかを確認します。

Make Material Attributesノードを使用していました。

f:id:kazuhironagai77:20210321233602p:plain

これが知りたかった。

MF_Ground_Grassを作成しM_Ground_GrassのBPを丸コピーしてMake Material Attributesノードに繋げます。

f:id:kazuhironagai77:20210321233618p:plain

MF_Rockも同様にします。

f:id:kazuhironagai77:20210321233633p:plain

次にMaterial、landscapeを作成します。

早速、問題にぶち当たりました。二つのMaterial functionをblendしよう、Layer Blendを探したら沢山の種類がありどれを選べばよいのか分かりません。

f:id:kazuhironagai77:20210321233650p:plain

調べたら、Layer Blend ノードの正式名称はLandscape Layer Blend ノードでした。

ふう。一安心と思ったらInputがないです。

f:id:kazuhironagai77:20210321233705p:plain

終わった。

このノードも違うのか。と思ったらLayersに追加する事でInputを作成出来るみたいです。

f:id:kazuhironagai77:20210321233720p:plain

f:id:kazuhironagai77:20210321233726p:plain

開いて見ると、色々なパラメーターを指定しないといけないみたいですね。

f:id:kazuhironagai77:20210321233746p:plain

これらのパラメーターの正しい値は何となく想像も付きますが、一応Landscape essential conceptで確認してみましょう。

f:id:kazuhironagai77:20210321233805p:plain

Layerの名前は何でも良いのでinputに接続するMaterial Functionと同じ様な名前にすると思われます。やっぱりGrassやRockを指定していました。

BlendTypeはLectureでTarget LayerでこのLayerを指定する時にWeight Blendを選択していたので、

f:id:kazuhironagai77:20210321233822p:plain

勝手にWeight Blendである可能性が高いと思っていましたらやっぱりその通りでした。

Preview Weightは、LectureでこのMaterialをLayerとして使用したら全部GrassだったのでGrassが1でRockが0に何処かで指定しているはずと思っていました。それがここだった訳です。

Landscape essential conceptで使用された値と全く同じにします。

f:id:kazuhironagai77:20210321233839p:plain

Landscape essential conceptではAmbient Occlusion は接続していないんですが、MF_Rockでは使用しているんですが。

分からないですが一応繋げておきます。

f:id:kazuhironagai77:20210321233902p:plain

やっとM_Landscapeが完成しました。

「2.7.1 Painting Tools」の解説の通りですが、Landscapeを選択してLandscape MaterialにM_Landscapeを指定します。

f:id:kazuhironagai77:20210321233918p:plain

するとTarget LayerにGrassとRockが現れるので

f:id:kazuhironagai77:20210321233937p:plain

Grassの+を押してWeight…を選択します。

するとLandscapeが緑一色になりました。

f:id:kazuhironagai77:20210321233952p:plain

今度は、Rockの+を押して同様にセットします。

ブラシの設定は適当にして、岩をpaintしてみます。

出来ていますが、これじゃ駄目ですね。

f:id:kazuhironagai77:20210321234009p:plain

拡大してみると分かりますが、Rockに使用しているMaterialがちょっと想像と違っていました。

f:id:kazuhironagai77:20210321234032p:plain

色を変えて、かなり弱めにPaintしてみましたがTextureのパターンが目立って違和感が凄いです。

f:id:kazuhironagai77:20210321234053p:plain

Lectureで習った事は出来るようになったのは確かなので、この問題は来週、検討します。

2.9 Grass Toolを使用してみる。

最後にGrass Toolを使って草を生やしてみましょう。Quiz 3でも間違えてしまった所なので慎重にやっていきます。

手順は「2.7.5 Using the Grass Tool」に書かれている通りなのでParameterのみ記していきます。

f:id:kazuhironagai77:20210321234115p:plain

草のStatic meshはいっぱいあったので適当に一個選びました。

M_Landscapeに以下のコードを追加しました。

f:id:kazuhironagai77:20210321234133p:plain

想像してたのより10倍位やばい状態になっています。

f:id:kazuhironagai77:20210321234155p:plain

草の種類を変えます。

更にGrass Densityも思いっきり下げました。

f:id:kazuhironagai77:20210321234213p:plain

結果です。

f:id:kazuhironagai77:20210321234233p:plain

f:id:kazuhironagai77:20210321234242p:plain

PCが凄い音だして計算してました。

一応、自分でも使える事は確認出来きました。

残念ながら時間が無くなってしまったので今週はここで終了します。

3.Widget内のBPの整理

来週にまわします。

4.Effectの勉強と作成

来週にまわします。

5.まとめと感想

今週はLevel design、WidgetのBPの整理、そしてEffectの勉強をやる予定でしたが、Level designの復習で終わってしまいました。

Level designはまず優れたTutorialを探す事から始めました。公式のOnline learningにある

f:id:kazuhironagai77:20210321234321p:plain

はかなり良いTutorialと思います。

後、Landscapeを作成するLevel designerとEnvironmental artistは違う職種のようです。Environmental artistはLevel designerが配置する個々の部品を作成する人で、Lightingの専門家みたいです。

Landscape Essential Conceptsで勉強して、Landscapeの作成方法、Paintの使用方法、Grass toolを使用したFoliageの作成方法などは一応出来るようになりました。

来週の予定は、

  • DocumentのWater、Foliage Tool、Fog Effect、そしてHierarchical Level of Detailを読みます。
  • 日本語の資料で関係している箇所もチェックします。
  • UIWSの使用方法も勉強します。

f:id:kazuhironagai77:20210321234350p:plain

  • Paintの見た目がオカシイ所を直します。(出来ればlayerをもっと増やしたいです。)
  • Grass toolで2種類以上の草を生やします。(見た目の違和感も無くしたい。)
  • Map1のLandscapeを完成させます。

以上です。

WidgetのBPの整理、そしてEffectの勉強は時間があったらやります。

 

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する  マップの作製など

f:id:kazuhironagai77:20210315000253p:plain

<前文>

英語発音まとめ

このブログに記載しているプロジェクトとは別に、日本人に区別が出来にくい英語の音素をminimal pairを使用して練習する事で区別が出来るようになる学習ゲームも作成しています。

そのソフトを作成するためかなり英語の発音について勉強しました。

その勉強成果をここにまとめておきます。

ただし最初に断っておきますが、私は10年以上アメリカで暮らしていましたが、言語学は全く勉強した事はありません。のでここから書く事は全くの独学です。専門家の人から見れば常識な事が書かれていたり、間違っている事もあるかもしれません。しかし私にとってはこれが正しいと思われる内容になります。

  • minimal pairで練習してもあるタイプのL とRの区別は限定的にしか出来な

のっけから否定的ですがminimal pairを使用する学習方法もSilver bulletではないと言う事です。非常に効果の高い学習方法ですが常識の範囲で使用する必要があります。

この区別が付かないタイプのLとRの場合も二つの単語の音を交互に聞いて、テストするとある程度の練習を積めば誰でも正解を選べます。しかし時間を置いてテストすると全く分からなくなります。

これは日本人が分からない音のもう一つの代表であるThと全く違う結果です。

ThはSやSHとminimal pairを使用して音の違いを学習すると大人でも完璧に分かるようになります。そして一端分かるようになると、自転車の乗り方を覚えたら生涯忘れないのと同じようにThの音そのものをずっと覚えています。他の日本人が英語をしゃべっている(通訳とか)のを聞いて「この人、THをチの音で代用しているわ。」とか「英語下手と思ったけどTHの音しっかり発音している。」みたいな事まで分かるようになります。

この結果を元に、私が考えてるのはLとRの究極的な区別は絶対音感的な物が関わっているのではないか?と言う事です。音楽が専門の人によると、大人になってからどんなに音楽の練習をしても絶対音感は付かないそうです。その差は年齢だそうで、ある程度の年齢に達すると絶対音感を身に付ける事は出来ないそうです。これに近いものが、LとRの究極的な区別にはあるような気がします。

  • /ʌ/の音はイギリス英語とアメリカ英語で違う

発音記号のもっとも重要な取り決めは「一つのアルファベットに一つの音」です。これは平仮名が言葉の基本単位である日本人には当たり前過ぎる事ですが、英語では、一つのアルファベットに沢山の発音方法があり、また同じ発音を別のアルファベットで共有したりしてるため、アルファベットで音を表す事が出来ません。

その弊害を無くすために開発された発音記号ですので、絶対に「一つのアルファベットに一つの音」の原則は守らなくてはいけないはずなのに、もっとも基本的な音の一つである/ʌ/の音がイギリス英語とアメリカ英語で違います。

  • 発音記号は、音を以下の条件で定義している。ただし2と4は条件付きで、結構曖昧。
    1. 口の開ける大きさ、
    2. 舌の動かし方や位置、
    3. 唇の形、
    4. 息の吐き方(イントネーションやアクセントを含む)

日本人には違う音に聞こえるのに同じ音で表示されている英単語が沢山あります。逆に日本人には全く同じ音なのに違う音として定義されている発音記号もあります。上記の原則を理解しておくと英語圏言語学者は、その上記の原則に照らし合わせて同じ音、違う音として認識しているんだと分かります。

  • Schwa /ə/音について

アメリカ英語の発音指導の専門家の多くは/ə/と/ʌ/は全く同じ音で、/ʌ/は強く、/ə/は弱く発音するだけであると言っています。これは上記の発音記号の定義で、アメリカ英語の/ʌ/と/ə/を当てはめて考えると全くその通りです。

しかし日本人にとってはちょっと違う話で、/ʌ/はどの単語で使用される場合でも、ウに近いアに聞こえるのに対して、/ə/は単語によってはアに聞こえたりオやウに聞こえたりします。

そうなると実際のSchwa音が分からなくなり大変不安になります。

これについてですが、口を全く動かさないで、喉の奥だけでアイウエオと言うと結構アイウエオと聞こえる事と関係していると思っています。

もう少し詳しく説明すると、/ə/が実際の発音で使用されるときは、必ず元の音があって、それの退化した音として使用されます。だから実際に単語の中で/ə/が使用されている場合、口の開ける大きさ、舌の動かし方や位置、唇の形、息の吐き方は/ə/の発音になっていても、喉の奥で発する音は元の音に近い音が発せられていると思われます。

それが実際の単語内の/ə/の音がアに聞こえたりオやウに聞こえたりする理由だと考えています。

  • /ɜ// ɝ //ɚ// ɜɹ// əɹ/は同じ音として扱う

これはRの音についてなんですが、日本語でアを伸ばすとアーになります。イギリス英語だと日本と全く同じで単に音を伸ばすだけなんですが、アメリカ英語だと音を伸ばす時に丸めるんです。音を巻くというともっと正確かもしれません。その巻いた音を日本語で表現するとア~となりますが、この音がアメリカ人にはRに聞こえるらしいんです。つまりアRと聞こえます。それで発音記号は二つのアルファベットで表すべきと考える人と、いやア~と一つの音が伸びているだけだから一つのアルファベットで表すべきと考える人たちがいます。更に単語によってアRと聞こえるものとア~と聞こえると主張する人もいます。

2つのアルファベットで表すべきと考える人は/ ɜɹ// əɹ/を使用しますし、一つの音で表すべきと考える人は/ ɝ //ɚ/を使用します。更に単語によって違うと主張する人達は、単語によって/ ɜɹ// əɹ/と/ ɝ //ɚ/を使い分けたりします。

更に混乱する要素として/ɜ/と/ɚ/はアメリカ英語だと同じ音だったりします。

前提としてイギリス英語だと、/ʌ/は/ə/とは全く違う音です。ので/ə/のストレス音が必要になります。それが/ɜ/らしいのです。しかしイギリス英語では/ɜ/は/ə/より強く発音するだけでなく長く伸ばして発音するらしいんです。つまりアメリカ英語なら丸くなる訳です。それで/ɜ/がストレスのある/ɚ/になる訳です。所がこの強弱ですがアメリカ英語の場合、ほとんど変わりません。つまり同じに聞こえます。

それじゃ最後に/ɜ/と/ ɝ /の違いは何ですかとなりますが、アメリカ英語にもイギリス英語の/ɜ/と同じ音がある。だからRで巻いた時は/ ɝ /を使うべきと言う人達です。彼らによるとBirdなんかはRで巻いていない。と言っています。言っていますがBirdをRで巻いて発音するアメリカ人はいくらでもいるでしょうし、その例えは、何か日本人でもカナダをキャナダと発音する人がいます。みたいなちょっと恥ずかしい感じがします。

これらの音は結局ERの音を表しています。HerとかMotherなどのerです。勿論“英語”ですのでアルファベットと実際の音は限定的にしか繋がっておらず、girlのirやhurtのurはこの場合のerと同じ音です。

ちなみにですが、この音とɑɹの音(例えばHurtとHeart)はどちらも日本語で「ア~」と聞こえますがMinimal Pairで練習すれば5分位でこの2つの音の区別がつけられるようになります。

  • Lの発音はTHみたいに舌を噛んでする

これ、私ずっと疑問に思っていた事なんですが、アメリカ人にLの発音の仕方を聞くと、上歯の付け根に舌をタッチさせてと、所謂教科書的な説明をみんなします。それなのに実際の会話でLを発音する時には、THの時の様に舌を噛んで発音しているんです。

そしたら確認出来ました。沢山のアメリカ英語の発音の専門家がLの発音の時にTHのように舌を噛んで発音する方法もあると言っていました。

それらの専門家の話を聞くまで、この舌を噛んで発音するLはDark Lの音なのかな?とか思ったりもしたんですが、実際はもう少し複雑でした。

Dark Lの音は口の奥、喉側で出す音なので、舌の付け根の形だけが大切だそうです。ので舌を噛んでいてもいなくてもDark Lの音を出す事は出来るそうです。

そしてLight LはDark Lを前半で発音して後半に所謂教科書的な上歯の付け根に舌をタッチさせて発音するそうです。この時に舌の脇から息が抜けるとLの音になり、その時の舌の位置は歯の裏でも舌で歯を噛んでも関係ないそうです。

それで実際の発音の場合ですが、例えばGirlと発音する時はLはDark Lになります。Light Lの前半部分だけ発音する訳です。しかしGirl isと発音する場合は残り後半も発音するのでLight Lになるそうです。その時に最初から舌を噛んで発音するとDark Lの発音で止める事もそのままLight Lの発音に移行する事も舌の先を動かさないで出来るので楽に発音出来るらしいんです。

ふーん。って感じです。

  • 二重母音について

[aɪ], [eɪ], [ɔɪ], [aʊ]のような音を二重母音( diphthong ) と言いますが、これってすごい不思議でした。だって単に母音が二つ並んでいるのと同じじゃないですか?日本語でアオとかアイとかをアイウエオとは別に習う必要はないですよね。なんでこれらの音は特別なんでしょうか?

ずっとその理由があると思っていたんですが無かったです。単にこれらの母音は二つ続けて発音する事が多いだけでした。

ただ気を付けないといけないのはこれらの母音の中に、/a/や/e/の音のように二重母音にしか存在しない音があります。

この辺はまだ勉強中ですが、/a/の音は日本語のアとほとんど同じで、/e/の音は日本語のエの音とこれもほとんど同じに聞こえます。Vowel Chartを見てもこれらの音と日本語のアとエは非常に近いか全く同じ位置に記されています。

  • /ɑ/について

この音、日本語が喋れるアメリカ人で日本語のアの音と同じ音と言っている人達がいます。いますが、これそのまま信じるのはかなり危険だと思います。

私は/ɑ/の音はオの混じったアで単なるアとは違う音だと思います。

その理由は、/ɑ/と/ʌ/の音のMinimal Pair(duckとdock、hutとhot、nutとnotなどです。)で練習すると、日本人なら直ぐに実際の単語の発音では/ɑ/の音にはオが僅かですが完全に聞こえ、それ故に区別が容易につく事に気づくからです。

確かに中にはFatherのようなオが全く聞こえない単語もあります。しかしこれだけ沢山の/ɑ/の音を使用している単語が僅かながらオが聞こえるのですから実際はオに近いアだと思います。

更に言えばVowel chartの記載から見ても、/ɑ/の音は、日本語のアの音とはかなり違う音です。まず日本語のアは口の先で発音しています。それに対してこの/ɑ/の音は口のかなり後ろで発音します。それに日本語のアの音はかなり広い範囲のアの音で、あんまり口を開けないでしゃべるおとなしい女の子なんかはむしろ/ʌ/に近い音でアを発音しています。

もう一つ言うと、一般的な日本語のアに一番近い英語の音は、先程の二重母音に出て来た/a/の音だと思います。

まだまだ書く事はありますが、今週はこの辺で止めておきます。

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

<本文>

1.今週の予定

以下の事をやっていきます。

  • 対話システムとアドベンチャーゲーム本形式によるセリフの整理方法の考察
  • Game Modeクラスの整理の続き
  • バグの直し
    • 勝利のポーズの時に剣が頭に刺さる。
    • モンスターが後ろを向いているとプレイヤーの操作するキャラがすぐ後ろに立っていても気が付かない。
    • 戦闘時の武器のモーションの追加
    • 魔法の杖を装備したら名前が枠からはみ出て表示されていたのでそれを直す。
    • 魔法のeffectを増やす。
  • Levelデザイン

2.対話システムとアドベンチャーゲーム本形式によるセリフの整理方法の考察

先週、戦闘時における報連相のための対話システムを一から直しましたが、その際にNPCとの会話で考案したアドベンチャーゲーム本形式によるセリフの管理方法を採用しました。

このやり方は全てのセリフを一枚のData tableで管理出来るだけでなくData Tableを見ればそのセリフの前後関係も把握出来る優れたシステムです。

ここで思ったんですが、実際のゲームではどうやってセリフを管理しているんでしょうか?調べて見ます。

2.1 実際のゲームにおける対話システムの管理方法はどうなっているのかを調べる。

あんまりゲームにおける対話システムの管理方法についての記事がありませんでした。

あるにはあったんですが、何か高校生がまとめたみたい感じのサイトとか、ユーザー側に寄り過ぎてどうやってそのセリフを整理・管理しているかについては全く述べていないサイトばかりでした。

その中でWikipediaにDialogue Tree についての解説が載っていました。

このシステムは、私が採用したアドベンチャーゲーム本形式によるセリフの書き方をTree Diagramに表した方法でした。

やっぱりアドベンチャーゲーム本形式は商業レベルのゲームでも採用される優れたシステムだったんですね。

後、Tree Diagramにすると非常に見やすいです。

ここの説明によるとこの方法が最初に採用されたのは1941年と書かれていました。このやり方を現在のゲームでも採用し続けているのかそれとももっと優れた方法があるのかは書かれていませんでした。

後、ユーザー視点からですが、対話システムの進歩としてWe Should Talkと言うゲームが挙げられていました。

このゲームではNPCとの会話は、複数ある回答の中から一つを選ぶのではなく、文の一部を変更して自分で回答する文を作成するそうです。

f:id:kazuhironagai77:20210315000741p:plain

確かに進歩と言えば進歩ですが、Tree Diagram が出来てから80年以上経っている訳ですから。革命的とまでは言えないですね。

もっと機械学習したAIが回答するみたいなのはないのでしょうか?

調べて見たら、論文は出て来るんですがゲームは出てこないですね。

何か、日本のゲームとか世界の先端を走っているイメージがあったんですが、大人数で開発しているようなゲーム会社でも80年以上前に開発された対話システムをそのまま使用しても何とも思わないでゲーム作っているんですかね。ちょっとがっかりです。

2.2 Not Yet Dialogue Plugin System について

Tree Diagramと言えば、UE4のplugInであるNot Yet Dialogue Plugin Systemは対話をTree Diagramで管理しています。

f:id:kazuhironagai77:20210315000807p:plain

ゲームにおける対話システムの進歩について、あんまり考察する事もないみたいなので、このPlugInの使用方法をここにまとめる事にします。

3.Not Yet Dialogue Plugin Systemの使用方法の解説

Not Yet Dialogue Plugin SystemはUE4用のPlugInで対話のセリフを管理するのに上記で紹介されたDialogue Tree を採用しています。

3.1 無料版について

marketで売られているNot Yet:Dialogue Plugin Systemは5千円以上します。

しかしhttps://gitlab.com/NotYetGames/DlgSystemに行くと無料で公開されています。

f:id:kazuhironagai77:20210315000844p:plain

これって勝手に使用して良いんでしょうか?と心配しながら、ここの説明文を読んだらBinaryファイルにするのに思っていたより手間がかかるので、Binaryファイルはマーケットで販売する事にした。しかしここにある分は完全にフリーで商業も可と説明してありました。

のでここからダウンロードして使用します。

3.2 使用方法のまとめ

公式のTutorial (Dialogue Plugin System - Create your first Dialogue Project Tutorial)で使用方法が説明されていますが、これでもかなり複雑です。こういう使い方の解説は、最低限の機能の紹介に徹するべきだと私は思います。

それで自分でまとめてみました。

  • 準備編
    • Actor クラスを使用したNPCの作成とDlg Dialogue Participantの継承、そしてParticipant Nameの指定
    • ThirdPersonCharacterDlg Dialogue Participantの継承、そしてParticipant Nameの指定
    • 対話の作成とその対話をDialogue BPに落とし込む
  • 本番編
    • 作成したDialogue BPを使用する。
    • 最初のセリフを表示する。
    • 選択条件を表示する。
    • 次のノードに移る。

本番と準備を分ける事で、このプラグインを使用するに当って最低限準備しなければならない事と、使用するに当たって本当に最低限知らないといけない事が区別して理解出来るはずです。

かなり分かり易い説明になると思っています。

3.3 準備編

Not Yet:Dialogue Plugin Systemを使用するためには3つの準備が必要です。

  • Dialogue BP内に実際の対話を書く。
  • 対話に必要なNPCを作成する。
  • Playerの操作するキャラと作成したNPCParticipant Nameをつける(そのためにはDlg Dialogue Participantの継承が必要)。

これらについて解説していきます。

3.3.1 ダウンロードしたDialogue systemをProjectから使用出来るようにする。

このブログを読んでいる人でダウンロードしたPluginの使用方法を知らない人はいないと思いますが一応書いておきます。

まずProjectを作成します。Template はいつも使用しているThird Personにします。

作成したProjectを開き、新しいFolderを作成します。名前は必ずPluginsとします。

f:id:kazuhironagai77:20210315000948p:plain

先程ダウンロードしたファイルを解凍し、このフォルダー内に移します。

f:id:kazuhironagai77:20210315001005p:plain

これだけでProjectから使用出来るようになります。

確認します。

いつもBPを作成する手順で、以下の選択肢を開くとDialogue Systemと表示されていたら、Dialogue systemが使用出来る様になりました。

f:id:kazuhironagai77:20210315001029p:plain

3.3.2 Actor クラスを使用したNPCの作成

まずNPCを作成します。

Actorクラスから派生させたBPを作成します。

f:id:kazuhironagai77:20210315001052p:plain

中を開きSkeletal Meshを追加します。

f:id:kazuhironagai77:20210315001108p:plain

f:id:kazuhironagai77:20210315001115p:plain

NPCが出来ました。

3.3.3 Dlg Dialogue Participantの継承

今度はDlg Dialogue Participantを継承させます。

ToolbarからClass Settingを選択します。

f:id:kazuhironagai77:20210315001136p:plain

するとDetail欄にInterfaceが現れます。

f:id:kazuhironagai77:20210315001152p:plain

Addボタンを押して、Dlg Dialogue Participantを選択します。

そうすると以下の様にDlg Dialogue Participantが表示されます。

f:id:kazuhironagai77:20210315001207p:plain

これでDlg Dialogue Participantが継承出来ました。

3.3.4 Participant Nameの指定

次にParticipant Nameの指定をします。

以下に示した様に、My BlueprintのInterfaceにGet Participant Nameがあります。それをクリックして開きます。

f:id:kazuhironagai77:20210315001237p:plain

それをクリックして開きます。以下の様になっています。

f:id:kazuhironagai77:20210315001254p:plain

このNoneの箇所にこのNPCの名前を入れます。今回はMeNPCとします。

f:id:kazuhironagai77:20210315001310p:plain

3.3.5 同様の方法でThirdPersonCharacterDlg Dialogue Participantの継承を行い、Participant Nameを指定する。

しました。

f:id:kazuhironagai77:20210315001334p:plain

3.3.6 セリフを作成する

Dialogue BPに記入するセリフが必要です。それを作成しました。

f:id:kazuhironagai77:20210315001355p:plain

3.3.7 Dialogue BPを作成し、上記の会話を移す

作成した会話をNot Yet: Dialogue system内に落とし込みます。ほとんどカンで操作しても作成出来ますが、注意しなければならない箇所が幾つかあります。その辺は詳しく解説します。

まずDialogue SystemからDialogue BPを作成します。

f:id:kazuhironagai77:20210315001418p:plain

このDialogue BPこそがNot Yet: Dialogue Systemにおいてセリフの管理を行うクラスです。

名前をMe_dialogueとしました。

f:id:kazuhironagai77:20210315001433p:plain

開きます。

f:id:kazuhironagai77:20210315001453p:plain

このStartノードから会話を作成していきます。

AIの作成で使用したBehavior Treeの時と同じようにこのノードから→を引っ張ります。そしてAdd Speech nodeを選択します。

f:id:kazuhironagai77:20210315001534p:plain

Speech Nodeにセリフを追加します。

f:id:kazuhironagai77:20210315001551p:plain

するとNode側もセリフが表示されます。

f:id:kazuhironagai77:20210315001609p:plain

今度はこのセリフを言うキャラのParticipant nameを追加します。

f:id:kazuhironagai77:20210315001626p:plain

するとセリフと同じようにMeNPCがノード側にも表示されます。

f:id:kazuhironagai77:20210315001641p:plain

同様にPlayerのセリフも追加します。

f:id:kazuhironagai77:20210315001656p:plain

更に会話を追加していきます。

f:id:kazuhironagai77:20210315001711p:plain

この次は選択肢が表示されます。

f:id:kazuhironagai77:20210315001727p:plain

選択肢は以下の方法で作成します。

まず選択肢の答えのためのノードを作成します。

f:id:kazuhironagai77:20210315001744p:plain

矢印を選択します。

f:id:kazuhironagai77:20210315001800p:plain

するとDialogue Graph Node内に以下の内容が表示されます。

f:id:kazuhironagai77:20210315001814p:plain

ここからTextのNextの部分に選択肢の質問を書いて行きます。

最初の選択肢は「武器を買いに来た。」ですので、それを書き込みます。

f:id:kazuhironagai77:20210315001830p:plain

同様に残り二つの選択肢も埋めていきます。

f:id:kazuhironagai77:20210315001848p:plain

f:id:kazuhironagai77:20210315001856p:plain

今度はそれぞれの選択肢が選ばれた場合のセリフを追加します。

f:id:kazuhironagai77:20210315001912p:plain

ですので、

f:id:kazuhironagai77:20210315001928p:plain

となります。

2と3を選択した場合のセリフも追加します。

f:id:kazuhironagai77:20210315001946p:plain

これで全てのセリフをDialogue BPに書く事が出来ました。

Nodeの最後には、End Nodeを追加します。

f:id:kazuhironagai77:20210315002006p:plain

最後にもう一つやる事がありました。

どのノードも選択しない状態でDialogueを見ると以下の様に表示されています。

f:id:kazuhironagai77:20210315002024p:plain

Participant NameがそれぞれMeNPCとPlayerであるクラスの名前をParticipant Classに記入します。

Playerの方は既にThirdPersonCharacterが選ばれています。MeNPCのParticipant Classを選択します。

f:id:kazuhironagai77:20210315002041p:plain

これで準備が完了しました。

3.4 本番編

ここから、今まで作成したモノを使用してゲーム中にセリフを表示させる方法を説明します。

3.4.1 作成したDialogue BP(Me_dialogue)を使用する。

3.3で作成したDialogue BP(Me_dialogue)を使用するためには、そのinstanceを作成する必要があります。

どのBPでもいいのですが、今回はLevel BP内で作成する事にします。dialogue BPのinstanceを作成する関数はStart Dialogueです。

f:id:kazuhironagai77:20210315002107p:plain

Me_dialogueで使用されているParticipant nameを持つクラスのInsntanceをLevel上に配置します。

f:id:kazuhironagai77:20210315002137p:plain

それらのInstanceをMake Arrayに繋げます。

f:id:kazuhironagai77:20210315002159p:plain

3.4.2 最初のセリフを表示する

前節でDialogue BPのインスタンスの作成までは成功しました。そのインスタンスからどうやってセリフを取り出すのかをここで解説します。

取りあえずアクセスを簡単にするためにMe_dialogueのインスタンスは変数MeDialogueにセットします。

f:id:kazuhironagai77:20210315002223p:plain

ここで突然ですがDialogue BPのActiveなノードと言う概念について説明します。

アドベンチャーゲーム本で考えると分かり易いと思うんですが、自分がゲームのどこをやっているのかを表すためには、今、自分が読んでいる文章の番号が必要です。誰か友達にどこまで進んだ?と聞かれたら「今、180でドラゴンと戦っている。」と答える訳です。

この時の180と言う番号は、考えようによっては、今自分が居る場所とも言えます。Dialogue BPでは、この今自分が居る場所をActiveな場所(ノード)と呼んでいます。

勿論、Dialogue BPは対話のセリフなので、Active Nodeは、今どのセリフを読んでいるのかを表します。

f:id:kazuhironagai77:20210315002242p:plain

当然ですが、ActiveなノードはDialogue BP内で一個だけです。

Dialogue BPのインスタンスからセリフを取り出すためには、このActiveなノードにアクセスする必要があります。

Activeなノードにアクセスするために使用する関数はGet Active Nodeです。

f:id:kazuhironagai77:20210315002303p:plain

アクセスしているNodeのテキストを読む関数はGet Node Textです。

I keyを押すとnodeに書かれたテキストをプリントする様にします。

f:id:kazuhironagai77:20210315002319p:plain

すると、Iを押すたびに

f:id:kazuhironagai77:20210315002334p:plain

とプリントされました。

これで現在のactiveなノードのセリフをゲーム画面に表示する事が出来ました。

3.4.3 次のノードに移る

最初のセリフが表示出来たら、今度は次のセリフを表示する必要があります。その為にはActive Nodeを次のノードに移す必要があります。

次のノードに移すための関数はChoose Optionです。

f:id:kazuhironagai77:20210315002403p:plain

次のノードが1つ以上ある場合はOption indexに移動したいノードに着いている矢印の番号を記入します。矢印の番号は0から始まり左から数えていきます。

以下の様に実装してセリフが変化する様子を見てみます。

f:id:kazuhironagai77:20210315002419p:plain

結果です。

f:id:kazuhironagai77:20210315002433p:plain

Iを押すたびにActive nodeが前進して表示されるセリフが変わっています。

3.4.4 選択条件を表示する

最後に矢印に追加したセリフの表示方法を説明します。

f:id:kazuhironagai77:20210315002456p:plain

そのために使用する関数はGet Optionです。

f:id:kazuhironagai77:20210315002510p:plain

矢印の中から一個の矢印を選ぶのがChoose Option関数だから、矢印そのものを収得する場合はGet Option関数であるのは納得です。

正しこの矢印、Text以外にも沢山の情報を持っています。

f:id:kazuhironagai77:20210315002527p:plain

のでその中からTextを選ぶ必要があります。

なので以下の様な実装になります。

これで矢印のテキストにもアクセスする事が出来ました。

f:id:kazuhironagai77:20210315002542p:plain

テストします。

f:id:kazuhironagai77:20210315002557p:plain

あれ。Nextってなんですか?

となるかもしれませんが、

f:id:kazuhironagai77:20210315002709p:plain

矢印は何も指定しないとdefaultでNextが指定されているんです。

問題はそこじゃないんです。

「ここは武器屋です。今日はどんな御用ですか?」の後のセリフを見て下さい。

「武器を買いに来た。」しか選択肢が表示されていません。

選択肢は全部、表示されないと選択出来ませんよね。そこを次の節で直します。

3.4.5 選択条件を全て表示する。

選択肢を全部表示させる関数は…。残念ながらありません。普通のC++のように選択肢の数を調べてその分ループして表示します。

ノードが持つ選択肢の数を調べてくれる関数はGet Option Numです。

f:id:kazuhironagai77:20210315002733p:plain

この関数を使用して以下の様に実装します。

f:id:kazuhironagai77:20210315002750p:plain

結果を示します。

f:id:kazuhironagai77:20210315002805p:plain

はい。選択肢が全て表示されました。出来ました。

3.5 Not Yet Dialogue Plugin SystemのTutorialの作成のまとめ

公式のTutorialをかなり参考にしましたが、自分で作成した初めてのTutorialです。

自分ではこのTutorialが分かり易いのか、あるいは途中で詰まって出来ない箇所があるのか、もしくは興味が持てるほど引き付けられる内容があるのかなどは、全然分かりません。

一応以下の事を考えて書きました。

Hooking (読者を引き付けるための口上)としてこの5000円以上するこのNot Yet Dialogueが無料で手に入る方法を教えています。

このpluginを使用するために必要な事と、使用するための準備に必要な事を、明確に分けるために準備編と本編に分けました。こうする事で読みやすいかどうかは分かりませんが理解はし易くなったと思います。

本編ではActive Nodeと言う考えを最初に説明してそのActive nodeに従ってセリフを抽出していく事を説明しています。この方が分かり易いと思ってそうしました。

更に覚えなければならない関数の数を最小にするためにStart Dialogue()、Get Active Node ()、そしてGet Option()の三つと、その補助としてGet Node Text()、Get Option Num()の二つだけに絞って説明しています。

4.Game Modeクラスの整理の続き

先週、RPGGame Mode BPを整理して思ったのがまだまだ整理出来そうでした。

なので今週の引き続き整理していきます。

4.1 MyGameInstance変数の整理

f:id:kazuhironagai77:20210315002845p:plain

以下の様に整理しました。

f:id:kazuhironagai77:20210315002901p:plain

こんな感じで全部直していきます。

4.2 Third Person Character BP変数の整理

次はThird Person Character変数を直します。

f:id:kazuhironagai77:20210315002930p:plain

4.3 Combat UI変数の整理

Combat UI変数も直します。

CombatUI変数ですが戦闘中に同じウィジェットを三か所もセットしています。

f:id:kazuhironagai77:20210315002955p:plain

既に外していますが、Action状態で二か所です。

f:id:kazuhironagai77:20210315003011p:plain

f:id:kazuhironagai77:20210315003019p:plain

戦闘中にDecision状態でセットされたウィジェットがAction状態になった途端に外れる事はないので、Action状態にあるSETは全部消します。

以下の様に整理していきます。

f:id:kazuhironagai77:20210315003046p:plain

f:id:kazuhironagai77:20210315003057p:plain

f:id:kazuhironagai77:20210315003106p:plain

f:id:kazuhironagai77:20210315003133p:plain

f:id:kazuhironagai77:20210315003143p:plain

f:id:kazuhironagai77:20210315003153p:plain

f:id:kazuhironagai77:20210315003202p:plain

一応確認のためにテストします。

  • 武器なし:負け
  • 武器あり:勝ち
  • 魔法あり:バグで表示されず。

魔法はレベル1では覚えないので、Mを押すと魔法を覚えるように設定していたのですが、下に示したThirdPersonCharachterBPのコードでもMを使用していてこれとぶつかっているみたいです。

f:id:kazuhironagai77:20210315003248p:plain

このコードはワープの時に使用しているコードですが、Mでマップを開く必要ないのでMを外しました。

f:id:kazuhironagai77:20210315003304p:plain

今度は使用可能になりました。

更に一個バグを発見しました。

罠に一回侵入してモンスターを発生させ、その後逃げ出します。するとモンスターが死んで死体が消えるのですが、消える前にもう一度、罠に入ります。

f:id:kazuhironagai77:20210315003322p:plain

するとモンスターは発生しませんが、戦闘は開始します。

直します。

以下の様に改良しました。

f:id:kazuhironagai77:20210315003338p:plain

もしSpawned Monsterに実体が在る場合は待つ事にします。モンスターが消えるアニメーションは1.2秒なので、その間は罠に侵入しても新しいモンスターは発生しないようにしました。

テストします。

f:id:kazuhironagai77:20210315003353p:plain

何十回やっても死体が消えてから新しいモンスターが発生するようになりました。

直りました。

直りましたが、こんな僅かな一手間でバグが跡形もなく消えるのは正直、驚きです。

 

UE4C++でコードを書いていた時は「Buildに掛かる時間ウゼー、BP最高!」と思っていましたが、こうやって整理のためにBPを見直して見ると、BPは直ぐにスパゲッティコードになってしまっていますね。これを見やすいコードに戻すのはかなり難しいかもしれません。

5.バグの直し

先週見つけたバグを直していきます。

5.1 勝利のポーズの時に剣が頭に刺さる。

以下に示した様に剣が頭に刺さっています。

f:id:kazuhironagai77:20210315003503p:plain

調べたら、武器を装備した状態用の勝利のポーズのアニメーションがありました。

f:id:kazuhironagai77:20210315003533p:plain

f:id:kazuhironagai77:20210315003542p:plain

武器を装備した時はこのアニメーションを使用する様にします。

f:id:kazuhironagai77:20210315003559p:plain

テストします。

武器を装備している時は武器の勝利のポーズを取ります。

f:id:kazuhironagai77:20210315003619p:plain

勿論、剣が頭に刺さる様な事はありません。

f:id:kazuhironagai77:20210315003633p:plain

武器がない時は無い時のモーションを取ります。

5.2 モンスターが後ろを向いているとプレイヤーの操作するキャラがすぐ後ろに立っていても気が付かない。

現状のモンスターの視界の広さを示しています。特に狭いとは思えませんね。

f:id:kazuhironagai77:20210315003654p:plain

視界をこの状態より広げればこの問題は解決しますが、それよりモンスターが辺りを見渡す動作を追加した方がリアルな気がします。

色々検討しましたが、回りを見渡すアニメーションがないとどうしても動作が機械的になってしまいます。

別な方法で解決しましょう。

モンスターの視界がかなり狭いみたいです。

モンスターの視界を1500から2500に変更しました。

f:id:kazuhironagai77:20210315003709p:plain

単位が分からなかったから1500でも凄い気がしていましたが、15m先までしか見えてないって近眼でレベルじゃないでしょう。25m先まではプレイヤーの操作するキャラを認知出来るようにしました。

以下に示した緑のサークルがモンスターが認知出来る範囲です。

f:id:kazuhironagai77:20210315003724p:plain

更にIsHeInTerritory Serviceでモンスターの縄張りを決定していますが、その範囲も2500に変更しました。

f:id:kazuhironagai77:20210315003741p:plain

f:id:kazuhironagai77:20210315003748p:plain

最後に休憩時間も2秒から0.5秒に変更しました。

f:id:kazuhironagai77:20210315003805p:plain

これでテストします。

これだけ条件を厳しくするとモンスターの後ろに貼りつくのは無理になりました。

f:id:kazuhironagai77:20210315003822p:plain

ただ、モンスターから見ると休憩時間が4分の一になってしまったので、かなりブラックな職場になってしまいました。

心なしかモンスター達も苛立っているように見えます。

私がモンスターの首を回して辺りの警戒をする方法が分からなかったばかりに、モンスター達のパトロールはかなりハードなものになってしまいました。何かブラックな職場が生まれる原因を見た気がします。

取りあえず解決です。

5.3 戦闘時の武器のモーションの追加

弓を装備した時も、剣の装備した時と同じモーションです。これを直します。

最初、攻撃モーションだけ直せば良いかと思い、それだけ直したんですが、攻撃以外のモーションが非常に不自然だったので全部のモーションを直します。

まず装備が弓と矢の場合とそれ以外を判別するための関数を作成しました。

CheckArrowBow()関数と名付けました。

f:id:kazuhironagai77:20210315003904p:plain

この関数で武器や防具を装備するたびにその装備が弓矢かどうかをチェックします。弓矢の場合は専用のアニメーションを流すようにします。

以下の箇所で武器を装備していますので、その装備が弓矢かどうかをチェックします。

f:id:kazuhironagai77:20210315003922p:plain

そして装備が弓矢だった場合は、MyThirdPersonAnimBPに新しいBoolean変数を作成して下の部分から更に分岐させます。

f:id:kazuhironagai77:20210315003938p:plain

最初のSet Weapon Equipped()関数が呼ばれている箇所ですが、StartScreenで使用していました。

f:id:kazuhironagai77:20210315003954p:plain

この時にCheckArrowBow()関数でチェックして、mapが変わったら全部消えてしまうので、ここでチェックする意味はありません。代わりにRPGGameModeBPのEvent Begin Playでチェックしましょう。

f:id:kazuhironagai77:20210315004009p:plain

ついでにMy Third Person Anim BPに弓矢を装備した事を知らせる変数、Bow And Arrowも作成しました。

Anim Graphのdefaultでは以下のStateを追加しました。

f:id:kazuhironagai77:20210315004024p:plain

弓と矢を装備した状態でも魔法を使用する事もあるので上記のような形になりました。

他のSet Weapon Equipped()関数が呼ばれている箇所も同様にします。

Weapon Widgetの場合です。

f:id:kazuhironagai77:20210315004040p:plain

Is Weapon Equipped変数の後で、My Third Person Anim BPのwithWeapon変数の値をTrueに変更しているので、その後でCheck Arrow Bow()関数を呼びました。

f:id:kazuhironagai77:20210315004058p:plain

次は、pause画面で武器や防具を外した時の場合です。(Pause Equipment ウィジェット)

f:id:kazuhironagai77:20210315004116p:plain

武器と防具のどちらか一方でも外せばその時点で弓矢を装備していない事は確定なのでCheckArrowBow()関数を使用する必要はありません。

f:id:kazuhironagai77:20210315004134p:plain

これで、新しいマップが開かれた時、武器を装備した時、武器を外した時に、弓矢を装備しているのかどうかをチェックします。

防具を装備した時、外した時の場合も上記のコードが、弓矢を装備しているのかどうかをチェックしますので、新しくコードを追加する必要はないはずです。

今度は、弓矢を装備している場合のアニメーションの作成をします。

武器を装備した状態のIdle、Walk、そしてRunのアニメーションを管理しているMyThirdPerson_IdleRun_2DwithWをDuplicateしてMyThirdPerson_IdleRun_2DwithBowArrowを作成し、中のアニメーションを弓矢のそれにします。

f:id:kazuhironagai77:20210315004156p:plain

デモ画面では刀が頭に刺さっていますが、弓矢に交換したら直るんでしょうか?

作成したアニメーションをMy Third Person Anim BPのIdle/Run And Arrowから呼び出します。

f:id:kazuhironagai77:20210315004211p:plain

f:id:kazuhironagai77:20210315004218p:plain

弓矢で攻撃するモーションも追加します。

f:id:kazuhironagai77:20210315004235p:plain

f:id:kazuhironagai77:20210315004241p:plain

これで完成です。

テストします。

弓矢を装備します。

f:id:kazuhironagai77:20210315004257p:plain

中々カッコイイですね。

装備を外します。

f:id:kazuhironagai77:20210315004317p:plain

弓だけ装備します。

f:id:kazuhironagai77:20210315004339p:plain

はい。弓だけだと弓矢のアニメーションにならないですね。出来ています。

矢だけでも同様です。同様ですが、矢の先が地面にめり込んでいます。このアニメーションは直す必要があります。後で直します。

f:id:kazuhironagai77:20210315004404p:plain

この後、色々な条件で試しましたが、全部出来ていました。

今度は戦闘時のアニメーションをチェックします。

弓矢を装備した時は、弓矢のアニメーションになっています。

f:id:kazuhironagai77:20210315004420p:plain

問題はこの状態で魔法を使用した時です。

魔法を試すために一端、ゲームを終了したら、Errorが出ている事に気が付きました。

f:id:kazuhironagai77:20210315004436p:plain

これは武器や防具を片方だけ外した時に発生したエラーのようです。

エラーが発生した箇所のコードを見直すと、MyThirdPersonAnimBPにアクセス出来ていない事が判明しました。

直しました。

以下に示した黄色で囲った部分を追加しました。

f:id:kazuhironagai77:20210315004511p:plain

これでエラーは出ないはずです。

テストして確認します。

片方だけ武器や防具を変更したり、外したり、装備したりしましたがerrorは出ませんでした。

直っています。

それでは魔法のテストをします。

まず弓矢を装備した状態で魔法を使用しました。

f:id:kazuhironagai77:20210315004528p:plain

普通に使用出来ました。

魔法の使用後は、弓矢のモーションに戻っています。

f:id:kazuhironagai77:20210315004544p:plain

今度は弓矢で攻撃します。

f:id:kazuhironagai77:20210315004603p:plain

普通に弓矢で攻撃しました。

又、魔法で攻撃します。

f:id:kazuhironagai77:20210315004618p:plain

出来ていますね。

ああ…

勝利のポーズが弓矢のそれではなかったです。

直します。

f:id:kazuhironagai77:20210315004638p:plain

これで勝利のポーズも弓矢のそれになるはずです。

テストします。

f:id:kazuhironagai77:20210315004653p:plain

手から弓が離れていますし、矢が地面に刺さっています。

これが本当に正しい勝利のポーズなんでしょうか?

デバックした限りでは正しい様です。

f:id:kazuhironagai77:20210315004727p:plain

ここで流しているVictory_Bow_Animの元のアニメーションをチェックしましたが、モーションは同じでした。

f:id:kazuhironagai77:20210315004741p:plain

多分合ってはいますね。

今回はここまでとして、アニメーションのおかしい所は、後で検討しましょう。

先週のブログを読み直したら、

f:id:kazuhironagai77:20210315004756p:plain

と書かれていました。

これも後で検討します。

5.4 魔法の杖を装備したら名前が枠からはみ出て表示されていたのでそれを直す。

単純に後ろの絵を引き伸ばしました。

f:id:kazuhironagai77:20210315004817p:plain

それよりもSoldierの表示はオカシイです。

こっちも直します。

f:id:kazuhironagai77:20210315004835p:plain

はい。出来ました。

5.5 魔法のeffectを増やす。

今回は、魔法の炎(小)を作成するだけにします。

Effectは大変大きな分野で、バグの直しの一環で全部を勉強出来る訳はなかったです。

改めて時間を作って勉強する事にします。

更に今はParticle Systemで作成していますが、Niagaraを勉強すべき時が来ているのかどうかも調査しようと思います。

Fire Ballのサイズが小さいversionを作成しました。

f:id:kazuhironagai77:20210315004858p:plain

実際に使用したら、何故か同じサイズの火の玉が飛び出してきました。ので以下の方法で炎(小)を作成しました。

f:id:kazuhironagai77:20210315004918p:plain

FireBallをSpawnする時にそのサイズを指定します。炎(小)の時はサイズを0.25倍にしました。

炎(小)の場合、

f:id:kazuhironagai77:20210315004936p:plain

f:id:kazuhironagai77:20210315004944p:plain

炎(大)の場合はいつもと同じです。

f:id:kazuhironagai77:20210315005000p:plain

6.Levelデザイン

以下の地図通りに作成します。

f:id:kazuhironagai77:20210315005023p:plain

サイズはどうしましょうか?

先週のスピードに関する調査でマップの大きさも分かりました。

島の全体を見るのに歩いて1時間もかかったら面白く無くなります。精々10分で全体が見れるべきです。300m^2位にしますか。

調べて見た所、前回作成したLandscapeの大きさが127m^2でした。

f:id:kazuhironagai77:20210315005103p:plain

町の大きさが127m^2で普通なのに、島の大きさが300m^2では小さすぎますね。

Landscape Technical Guideによるとお勧めのサイズは以下の様になっています。

f:id:kazuhironagai77:20210315005120p:plain

1009m^2にしておきますか。

どっちを選択しましょうか。

f:id:kazuhironagai77:20210315005140p:plain

f:id:kazuhironagai77:20210315005147p:plain

Component Edge、Section Edgeそして Individual Quadについての軽い復習をしてから決める事にします。

2020-11-08のブログにLandscapeについてまとめがありました。

読んでみると結構重要な事が書かれていました。

f:id:kazuhironagai77:20210315005206p:plain

最小単位は1㎝もしくは1 Unreal Unitなんでしょうか。

前回作成したLandscapeは一辺のサイズがだいたい130mでした。

そして以下の表の下のパラメーターで作成しました。

f:id:kazuhironagai77:20210315005222p:plain

63x2=126で1を足して127、127mだとすると、最小単位は1mですね。

Component、Sectionについての解説がLandscape Technical Guideに載っていました。

それによるとComponentはrendering, visibility calculation, そして collisionを計算する単位だそうです。

RenderingとCollisionは分かりますが、Visibility Calculationは何を計算しているのでしょうか?

直接、Visibility Calculationが何を指しているのかを説明しているサイトは見つからなかったですが、色々なサイトを読んで総合的に判断するとCullingのような事を指しているみたいです。

後、もう一つ大切な事は、一つのComponentが一つのHight Map用のTextを保持しています。これはSectionの数とPerformanceに影響します。

SectionはLandscape LOD calculationの単位みたいです。LODはLevel of Detailsの事です。

このSectionの数を増やすと、1枚のテキストで対応するHight Mapの大きさも増えます。つまりパフォーマンスが向上します。

なるほど。

良く分かりました。

後、Epic社はComponentの数は1024 以下にすべきと忠告しているそうです。

ちょっと時間が無くなってしまったので今週はここまでとします。

7.まとめと感想

今週は以下の事をやりました。

  • 対話システムとアドベンチャーゲーム本形式によるセリフの整理方法の考察
  • Not Yet Dialogue Plugin Systemの使用方法の解説
  • Game Modeクラスの整理の続き
  • バグの直し
  • Levelデザイン

対話システムに関してはこれ以上現状ではやる事は無いです。

Game Mode BPクラスの整理はほぼやり尽くしましたが、沢山あるWidgetクラスも同様に整理する必要があります。こっちもやって行こうと思います。

バグの直しでeffectはとてもバグの直しの範囲では対応出来ないので別に枠を作ってやる事にします。

Level デザインは来週やります。

来週やる事は、

  • Level デザイン
  • Widget内のBPの整理
  • Effectの勉強と作成

です。

 

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する バグ出しの続き

f:id:kazuhironagai77:20210307231748p:plain

<前文>

センシティブな同人アニメ絵とアメリカからの圧力による規制について

アメリカに10年住んでいた関係でアメリカの本当の政治事情に詳しい私が予言しますが、これから同人絵のようなセンシティブなアニメ絵に対してアメリカ政府が日本政府に規制するように圧力をかけて来る事はないです。

これは日本のアニメ、特に深夜アニメはポル〇であると断定し、積極的に規制するよう日本政府に圧力をかけていたアメリカの宗教右派がトランプが失脚した事により権力を失ったからです。

アメリカの宗教右派が、日本の同人アニメの絵を規制しないといけない表向きの理由は、彼らの宗教が欲情する事自体を罪にしているからです。しかしその本当の理由は、宗教とは関係なく、有色人種の男性がちょっとでも白人女性に欲情したら使用人として使えないからです。彼らは元々、農場のオーナーなんです。つまりいっぱいいる小作人や使用人を管理する事が彼らにとっての仕事なんです。昔、中国の後宮で働く男性は、宦官にならなければならなかったですが、それと同じ事をやると世論の批判を浴びるので、心理的、精神的に追い込む事で実質、宦官にして使用人にする訳です。そのために欲情する事自体を罪にしている訳です。

アメリカの宗教右派の連中は、日本人全体を彼らの使用人みたく思っていますから、同人アニメの絵の存在そのものを叩く訳です。

これから4年から8年の間、アメリカの政治を司る左派の連中は、元々都会で働くサラリーマンですので、そんな事を考える人は全くいません。むしろ、宗教右派に対して「日本人を使用人にするよりお前のボスにした方が経営上手く行くんじゃないの。」と言う位の連中です。

だから左派の連中がアメリカの政治の中枢にいる限り、日本の同人アニメの絵を叩く事は考えられません。

ただし左派にも僅かにですが同人絵のようなセンシティブなアニメ絵を規制すべきと考える人はいて、彼らが何かのアクションを取る可能性はあります。しかし絶対数が少ないので大した問題にはならないと思われます。

更にアメリカでも、アニメで育った世代が成人となりつつあります。

彼らの中で同人アニメの絵で生計をたてる人も必ず出て来ます。そうなったら規制どころではないです。マリファナですら自分たちに利益をもたらすなら合法化する国が、同人アニメの絵が自分達の利益になった時、180°今までの意見を変えてくるっと賛成派になるのは目に見えています。

そういう訳で、これから同人絵のようなセンシティブなアニメ絵に対してアメリカ政府が日本政府に規制するように圧力をかけて来る事はないと思われます。

ただし、気を付けないといけないのは、これからナチ関係の書籍は逆に大変厳しい規制が課せられたり、賛同するような意見を書いた作者に信じられない位の厳罰が課せられたりするようになると思われます。

先月の前文で説明した通りGenocideの思想そのものがない日本で、本気でナチズムを信じている人はいません。しかしUFOやオカルト本の影響で軽い気持ちで賛同するような意見を書く人はいます。もし貴方の周りにいたら絶対止めるべきです。絶対に最悪な方に誤解されるからです。

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

<本文>

1.今週の予定

以下の事をやって行きたいと思います。

  • モンスターの歩く速度の設定がアニメーションとAIで全然違う
  • BGMの変更、町や村、モンスターが居る廃墟で別々のBGMにする。
  • BPの整理。RPGGameModeBPを整理します。
    • そこら中でThirdPersonCharacter変数に値をセットしている。過去、EventBeginPlay関数がBPから呼べなかったので毎回呼び出していた。これを直す。
    • 戦闘システムから呼ばれるEventを整理してまとめる。
  • セリフシステムの整理。確認出来るだけで3種類以上の方法でセリフを管理している。これを統一する。
  • 更なるバグだし。せめて1時間はこのゲームで遊んでバグ出しする。(今週は10分だった。)
  • Levelデザイン。

2.アニメーションとAIにおけるモンスターの歩く速度の設定の検証

Monsterが使用しているAIではMonsterの歩く速度を150に指定しています。

f:id:kazuhironagai77:20210307231912p:plain

一方で、Monsterが使用しているアニメーションでは歩くアニメーションは85でセットされています。

f:id:kazuhironagai77:20210307231932p:plain

実際は歩き始めるモーションを100%使用し始める速度が85で、そこからは歩くモーションと走るモーションのブレンドになります。

移動する速度が345を超えると100%走るモーションに変わります。

実際の速度が150の時の移動のモーションを見ると、そんなに不自然ではありません。

f:id:kazuhironagai77:20210307231952p:plain

ここで検証したい事は、この二つの速度は同じ単位なのか?もし同じ単位ならその単位は何なのかについてです。

もう一つ検証したい事は、モンスターが探索している時、微妙に浮いている気がする事です。

f:id:kazuhironagai77:20210307232013p:plain

この辺も直せたら直したいです。

2.1 UE4の速度の単位

それではこれから調べて行きましょう。

まずAIの方ですが、モンスターの移動スピードはMonsterBPのCharacter MovementのMax Walk Speedが担当しています。

f:id:kazuhironagai77:20210307232042p:plain

Character Movement:WalkingのMax Walk Speedの設定をみると600.0にセットされています。

f:id:kazuhironagai77:20210307232104p:plain

この単位を探してみましょう。

UE4 Answer Hubのこのサイトで質問されていました。

f:id:kazuhironagai77:20210307232148p:plain

質問者はUU/Sが単位でdefaultでは1UU が1cmなので㎝/sが単位ではないかと予測しています。ただしソースは見つかっていないと言っています。

まずここでの議論で、当たり前のように使われているUUの意味が分からないです。Defaultの1UUは1㎝とか言っています。

UUの意味を調べます。

どうやらUnreal Unitsの意味らしいです。ただし参考で出て来た資料がUE3の時代のものでした。

その後「UE4ではUUは1cmに設定された。」とか「World Settingsで値を変更出来る」とかの断片的な情報は出て来ましたが、確認までは出来ませんでした。

特に、World Settingの項目は以下に示した様にVRの項にあって、普通のゲームにも使用されるのか不明です。

f:id:kazuhironagai77:20210307232207p:plain

ただ100 cmは1 mなので完全に違うとも言い切れないのが難しい所です。

もしMax Walk Speedの単位がcm/sだとしたら6 m/sです。秒速は感覚的に分かりづらいですが、少し速すぎないでしょうか?

色々調べてみたら、大体、日本の高校生の100m走の平均は14秒だそうです。そうすると100 m ÷14 sで7.14 m/sになります。ので6 m/sが速すぎると言う事はないですね。

何か、cm/sで合っている気がしてきました。

この質問に対してのコメントで、実際にゲーム内で100m走って速度を測った人がいました。20回測って平均は5.9 m/sになったそうです。6 m/sに大変近い値です。となるとcm/sがMax Walk Speedの単位である可能性は高いですね。

所がこの質問に対する回答で、スピードは実際はSecondに負っているのではなくFrame rateに負っているから、単純にSecondには変換できないと主張する人がいました。

確かにFrameに負うのはその通りですが、前にUE4のDelta Timeの違いの影響を測定したら差がなかったです。

どの週のブログで測定したのかは覚えていないですが、今100m走の秒速などを計算するためにエクセル開いたら、その時のデータと図だけ残っていました。

ので図だけ貼っておきます。

f:id:kazuhironagai77:20210307232228p:plain

これらの結果から推測するとMax Walk Speedの単位がcm/sであるのはかなり正しそうです。

100m走をUE4内でやるのは面白そうですね。

これは試してみましょう。

良く見たら一目盛り1mと書かれていました。

f:id:kazuhironagai77:20210307232247p:plain

これは簡単に100mが作れますね。

走って見ました。

f:id:kazuhironagai77:20210307232306p:plain

100m走るのに16.809秒かかりました。

計算すると5.949 m/sでした。20回測定して平均を出します。

f:id:kazuhironagai77:20210307232322p:plain

ほとんど時間が変わらないので3回で中止しました。

結果は約5.95m/sです。

多分同じ結果になるんでしょうが、Animationの方も確認します。

Animationの方は、Animation BPであるSkelSwordAni BPで以下の様に設定されていました。

f:id:kazuhironagai77:20210307232340p:plain

Get Velocityノードが何の値を取り出しているのか分かれば、Max Walk Speedの単位と同じであるかどうかが判明出来ますね。

取りあえずどんな説明しているのか、カーソルをノードに添えてみたら、思いっきり説明されていました。

f:id:kazuhironagai77:20210307232357p:plain

Cm/s、つまりUnreal Units/secondと書かれていました。

図らずもAIで使用しているMax Walk Speedの単位とAnimationで使用しているGet Velocity関数の単位が同じであるだけでなく、Max Walk Speedの所で推測したUnreal Unitsの単位が㎝である事と、その速度を測るために秒を測っている(frameではなく)と言う事までも正しい事も分かりました。

これだけ解れば十分ですね。テストなら満点です。

次に行きましょう。

2.2 探索時、モンスターが微妙に浮いている気がする。

これは単純にCollusionを担当するCapsule ComponentがMonsterのMeshより下に飛び出ているからでしょう。

確認すると僅かにですが確かに飛び出していました。

f:id:kazuhironagai77:20210307232426p:plain

直します。

直しましたが、モンスターの動きを見るとまだオカシイです。

スクリーンショットではうまく伝わらないんですが滑っている感じがします。

f:id:kazuhironagai77:20210307232443p:plain

理由が分かりました。

歩いている歩幅に対して実際に進んでいる距離が短すぎるんです。今の2倍位は進むべきです。

85が歩く速度に設定されていますが、どうなんでしょうか?

調べて見ると成人だと1分間で80m歩く位が普通だそうです。1.34 m/sでした。となると134 cm/sですから、歩くを85にセットしていたのはかなり遅いかもしれません。

SkelSword1Dでモンスターの歩く速度を130に変更しました。

f:id:kazuhironagai77:20210307232502p:plain

前より滑ってる感じは無くなりました。かなり良くなってはいます。完全に滑っていないとは言えませんが、これだけ改善されたら十分と思います。

f:id:kazuhironagai77:20210307232518p:plain

3.BGMの変更、町や村、モンスターが居る廃墟で別々のBGMにする

これはLevel designが完成してからやります。ので今回はスキップします。

 

4.BPの整理。RPGGameModeBPを整理します。

4.1 昔、BPからEvent Begin Play関数が呼べなかったので、event毎にThirdPersonCharacter変数に値をセットしていた。これを直します。

まずAnimation for Victoryにありました。外します。

f:id:kazuhironagai77:20210307232606p:plain

Game Overにもありました。外します。

f:id:kazuhironagai77:20210307232623p:plain

Moving Camera Positionの所です。外します。

f:id:kazuhironagai77:20210307232641p:plain

Event Report Character Level Upの所です。外します。

f:id:kazuhironagai77:20210307232658p:plain

テストします。

モンスターと戦闘して勝利しました。レベルも上がりました。エラーの表示が出たりゲームが中断したりする事もありませんでした。

念のためにモンスターと戦闘して負けてみました。単にゲームオーバーになっただけでした。

外しても何の問題も起きませんね。

4.2 戦闘システムから呼ばれるEventを整理してまとめる。

以下に示したのが現在RPGGameModeBP内にあるEventです。

黄色に囲って在るEventの全てが戦闘に関係しています。

f:id:kazuhironagai77:20210307232731p:plain

これらを整理していきます。

以下の様に整理しました。

戦闘に関係のないEvent Begin PlayとEvent Tick 関数をまとめました。

f:id:kazuhironagai77:20210307232752p:plain

f:id:kazuhironagai77:20210307232800p:plain

戦闘に関するEventは以下の様に整理します。

  • Decisionに関係するEvent
  • Action に関係するEvent
  • 戦闘後のVictoryGameOverなどに関係するEvent
  • それ以外のEvent

Decisionに関係するEventです。

f:id:kazuhironagai77:20210307232833p:plain

f:id:kazuhironagai77:20210307232841p:plain

Actionに関係するEventです。

f:id:kazuhironagai77:20210307232900p:plain

f:id:kazuhironagai77:20210307232909p:plain

戦闘後のVictory、GameOverなどに関係するEventです。

f:id:kazuhironagai77:20210307232928p:plain

f:id:kazuhironagai77:20210307232942p:plain

それ以外の関数で戦闘に使用するものです。

f:id:kazuhironagai77:20210307233001p:plain

f:id:kazuhironagai77:20210307233011p:plain

全部で12個でした。

あれ、2つ足りません。

Confirmed Button Is Clicked EventはDecisionの中にありました。

f:id:kazuhironagai77:20210307233032p:plain

同様にConfirmed Button Is Clicked 2EventはActionの中にありました。

f:id:kazuhironagai77:20210307233048p:plain

雑と言えば雑ですが一応整理出来ました。

5.対話システムの整理。確認出来るだけで3種類の方法でセリフを管理している。これを整理する。

5.1 対話システムの確認

今まで作成した対話システムの確認をします。

5.1.1 NPCとの対話

まず、NPCとの会話です。

全てのNPCとの会話はStructであるNPC_ConversationBaseを元にして作成しています。

f:id:kazuhironagai77:20210307233128p:plain

NPC_ConversationBaseはTestタイプの変数とAnswer Comment ManagementタイプのArrayで構成されています。

f:id:kazuhironagai77:20210307233150p:plain

Answer Comment ManagementはStructです。

f:id:kazuhironagai77:20210307233206p:plain

中身はInteger型であるJumpToCommnetとText型であるAnswerCommnetで構成されています。

f:id:kazuhironagai77:20210307233223p:plain

実際のNPCにおける使用は以下の様になります。

f:id:kazuhironagai77:20210307233239p:plain

内容にはこれ以上は深入りしませんが、会話を管理するには非常に優れたシステムとなっています。

5.1.2 店主との対話

次に店主との対話システムです。これは教科書に書かれていた方法をそのまま使用しています。

まずNPC_ParentというWidgetを作成してこれに全ての対話を保持させます。

f:id:kazuhironagai77:20210307233303p:plain

Text型のarrayを作成してそれぞれの店主に対応したセリフを保持させます。

f:id:kazuhironagai77:20210307233322p:plain

実際の保持されているセリフです。

f:id:kazuhironagai77:20210307233342p:plain

このWidgetクラスから子クラスを作成してその子クラスにそれぞれの店主との会話を担当するWidgetにします。

その子クラスからNPC_Parentにある対話用のarrayにアクセスさせ実際の会話に使用します。(以下の様な方法でアクセスします。)

f:id:kazuhironagai77:20210307233431p:plain

この方法では「5.1.1 NPCとの対話」でやったやり方のように、答えの選択が複数ある場合には対応できませんが、それ以外ではまあまあなシステムです。

例えば全てのセリフをNPC_Parentで管理しているので全てのセリフをチェックしなければならない時はNPC_Parentを見れば良いからです。

Widgetを継承するやり方は3種類位勉強しましたが、ここで採用されているやり方でPackaging まで出来るのか不明です。ちょっと心配です。

5.1.3 戦闘システムにおける報連相

以下の方法で管理されています。

f:id:kazuhironagai77:20210307233454p:plain

この方法の何が最悪であるかと言うと、セリフの一括管理が出来ない事です。更にそれ故に対話の流れがセリフからだけでは分からなくなります。

しかしこのシステムから提供されるセリフは、ユーザーからは絶対に必要なものです。何故からそれはユーザーに対する戦闘における報告、連絡、そして相談を担当しているからです。

それでこの戦闘システムの報連相の機能は一切なくさないで、実装方法とセリフの管理方法だけ変更したいんです。

5.2 戦闘システムにおける報連相の全容を確認する。

この戦闘システムの報連相の機能は一切無くさないで、実装方法とセリフの管理方法だけ変更するためにはどうすればいいでしょうか?

取りあえず最初にする事は全容を把握する事です。

そこから始めます。

まず前節で示した戦闘システムの一番最初に出て来る報連相を見ると、セリフをCombat Window ウィジェットのComment Text Contentにセットしています。

f:id:kazuhironagai77:20210307233519p:plain

Combat Window ウィジェットのComment Text Contentを調べます。

調べようとしたらCombat Window ウィジェットのBPがゴチャゴチャしていたので整理をします。

f:id:kazuhironagai77:20210307233536p:plain

以下に示したように整理しました。

f:id:kazuhironagai77:20210307233553p:plain

戦闘前、戦闘システムのDecisionの状態で実行されるEvents、戦闘システムのActionで実行されるevent、最後に「読みましたボタン」を押した時に実行されるEventです。

最初のブロックである戦闘前ですが、ここではComment Text Content変数は全く使用されていないのでこのブロックは無視します。

f:id:kazuhironagai77:20210307233611p:plain

次のDecisionブロックですが、Decisionで決定すべき、攻撃、魔法、アイテム、そして逃げるの4つの選択をした後にそれぞれ実行するコードが書かれています。

f:id:kazuhironagai77:20210307233626p:plain

f:id:kazuhironagai77:20210307233633p:plain

f:id:kazuhironagai77:20210307233641p:plain

攻撃を選択した場合、Comment Text Content変数に関しては以下のTextが追加されます。

f:id:kazuhironagai77:20210307233658p:plain

魔法を選択した場合、Comment Text Content変数に関しては以下のTextが追加されます。

段々、分かって来ました。このComment Text Content変数はDecision状態のユーザーと敵のモンスターの選択した内容を報告するためのセリフを管理するためのものだったんです。

f:id:kazuhironagai77:20210307233734p:plain

こんどはItemを選択した時です。

魔法を選択した時と全く同じです。

f:id:kazuhironagai77:20210307233806p:plain

逃げるを選択した時です。

逃げるを選択した時はここで、Comment For Execute Action変数にもセリフを追加しています。

f:id:kazuhironagai77:20210307233824p:plain

ここはDecisionで選択された結果に対しての実行なのでActionですべき事をここでやってしまうのは、論理的にオカシイ気がしますが、この辺は後で検討しましょう。

ここまで来てComment Text Content変数に保持されているTextは何時、Combat UI ウィジェットのコメント欄に反映されるのかと思ったら、

f:id:kazuhironagai77:20210307233843p:plain

直ぐに反映される仕組みになっていました。

f:id:kazuhironagai77:20210307233859p:plain

確認のために戦闘してみます。

f:id:kazuhironagai77:20210307233918p:plain

f:id:kazuhironagai77:20210307233927p:plain

攻撃を選択しました。

f:id:kazuhironagai77:20210307233956p:plain

攻撃の対象にゴブリンを選択しました。

f:id:kazuhironagai77:20210307234013p:plain

f:id:kazuhironagai77:20210307234021p:plain

Decisionの状態はここまでで、残りはAcitonの状態のはずです。

一応確認するために次のコメントも見ておきます。

f:id:kazuhironagai77:20210307234040p:plain

はい。Actionの状態です。

兎に角、Comment Text Content変数に保持されているTextは直ぐにCombat UI ウィジェットのコメント欄に反映されている事は確認が取れました。

5.3 Comment Text Content変数と戦闘システムにおける報連相の管理方法のまとめ

まだ「5.2 戦闘システムにおける報連相の全容を確認する。」の途中ですが、ここまで整理して来て戦闘システムにおける報連相の管理方法についてのアイデアが出ました。のでここでまとめておきます。

今までずっとComment Text Content変数が、戦闘システムのセリフを管理しているにもかかわらず、その内容はその場その場で管理しているので、戦闘システムにおけるセリフの全体像が把握出来ないという問題がありました。

今回、その問題を解決すべく大々的に戦闘システムのセリフの整理、検討を始めたのですが、ここに来てComment Text Content変数が戦闘システムのセリフを管理しているのではなく、実際は、どの変数も戦闘システムにおけるセリフは管理していないという事が分かりました。

実際、戦闘システム内の個々のセリフは以下の方法で、その場その場で追加されているだけです。

f:id:kazuhironagai77:20210307234102p:plain

と言う事は、これらのセリフを一括で管理するData SheetもしくはTextのArrayを作成して、そこからこのAppendにセリフをパスするように変更すればいいんです。

そうすれば、戦闘システムのセリフを一括で管理する変数が出来ます。

それを作成しましょう。

5.4 戦闘システムのセリフを一括で管理するData Sheet

NPCのセリフの管理で使用したNPC Conversation Baseを使用して戦闘システムのセリフを一括で管理するData Sheetを作成しましょう。

f:id:kazuhironagai77:20210307234124p:plain

これならどこでどんなセリフをいうのか一発で分かりますし、そのセリフに対しての解答とその解答を選択した結果に対してのセリフの関係性も一瞬で分かります。

試してみましょう。

以下に示した様に最初の部分だけ作成しました。

f:id:kazuhironagai77:20210307234139p:plain

ここからセリフを読み取って戦闘システムのセリフが作成されるようにします。

RPGGameModeBPのBeginPlay関数に以下のコードを追加します。

f:id:kazuhironagai77:20210307234216p:plain

これでCombat Dialogue変数にCombat Dialogue data tableの全ての名前が保持されます。

次にCombat UIウィジェットのEvent Constructに以下のコードを追加します。

f:id:kazuhironagai77:20210307234234p:plain

これでComment Text Comtent変数にCombat Dialogue data tableの最初のセリフである「戦闘が開始されました。」がセットされたはずです。

テストしてみます。

Comment Text Comtent変数に入っていたDefaultを「戦闘が開始されました。」を「Test」に変更します。

f:id:kazuhironagai77:20210307234250p:plain

f:id:kazuhironagai77:20210307234258p:plain

これでCombat Dialogue data tableからセリフが読み込まれた場合は「戦闘が開始されました。」が表示されCombat Dialogue data tableからセリフが読みこまれなかった場合は「Test」が表示されるはずです。

f:id:kazuhironagai77:20210307234318p:plain

「戦闘が開始されました。」が表示されました。

出来ています。

しかし完璧ではありませんでした。

段落が無くなってしまっています。

f:id:kazuhironagai77:20210307234335p:plain

直します。

Combat UIウィジェットのGet Comment Text Box関数を以下に示した様に変更します。

f:id:kazuhironagai77:20210307234351p:plain

これでComment Text Content変数のtextに{nextline}が使用されていた場合、段落に変換されます。

更にCombat Dialogue data tableの最初のセリフである「戦闘が開始されました。」に{nextline}を追加しました。

f:id:kazuhironagai77:20210307234416p:plain

テストします。

f:id:kazuhironagai77:20210307234434p:plain

今度は行替えされて表示されています。

出来ました。

次は以下のセリフの交換になります。

f:id:kazuhironagai77:20210307234450p:plain

ここで気が付いたんですが、Combat Dialogue data tableのセリフが少し間違えていました。

直します。

Playerの操作するキャラとモンスターではNewRow_0のセリフは前半部は同じですが後半は違いました。ので二つに分けました。

f:id:kazuhironagai77:20210307234507p:plain

それでは最初の部分のセリフである「“は次の行動を考えています。」をCombat Dialogue data tableから読み込む事にします。

以下に示した様に改良しました。

f:id:kazuhironagai77:20210307234531p:plain

テストします。

f:id:kazuhironagai77:20210307234551p:plain

出来ています。

今度は「{表示されているボタン(攻撃、逃げるなど)の中から一つ選択してください。}」を変えます。

f:id:kazuhironagai77:20210307234611p:plain

テストします。

f:id:kazuhironagai77:20210307234628p:plain

出来ていますね。

行替えは出来てなかったのでそこだけ直しました。

整理しましたがまだまだスパゲティです。

f:id:kazuhironagai77:20210307234645p:plain

セリフをCombat Dialogue data tableから読み込む部分は関数にしますか。

四角で囲った部分です。

f:id:kazuhironagai77:20210307234704p:plain

関数名はGet Dialogue From Combat Dialogue DTです。

f:id:kazuhironagai77:20210307234720p:plain

かなり見栄えも良くなりました。

他の部分も直していきます。

以下の様に直しました。

Combat Dialogue data tableのセリフは以下の様に成りました。

f:id:kazuhironagai77:20210307234741p:plain

以下に実装部を示します。

f:id:kazuhironagai77:20210307234809p:plain

f:id:kazuhironagai77:20210307234816p:plain

f:id:kazuhironagai77:20210307234825p:plain

f:id:kazuhironagai77:20210307234833p:plain

f:id:kazuhironagai77:20210307234842p:plain

f:id:kazuhironagai77:20210307234859p:plain

f:id:kazuhironagai77:20210307234910p:plain

f:id:kazuhironagai77:20210307234919p:plain

f:id:kazuhironagai77:20210307234927p:plain

f:id:kazuhironagai77:20210307234935p:plain

f:id:kazuhironagai77:20210307234945p:plain

f:id:kazuhironagai77:20210307234955p:plain

f:id:kazuhironagai77:20210307235004p:plain

全ての要素でテストした結果、今までと同じセリフが表示されました。

5.5 Action状態におけるセリフを一括で管理するData Sheet

Comment For Execute Action はCombat UIウィジェット内に作成されたTextタイプの変数です。

調べて見るとEvent Report Begin Execute ActionでPlayerの操作するキャラの場合のみ、それまでComment For Execute Action変数内に足していたセリフをComment Text Content変数に移す事でセリフをCombat UIのコメント欄に表示します。

f:id:kazuhironagai77:20210307235032p:plain

f:id:kazuhironagai77:20210307235039p:plain

f:id:kazuhironagai77:20210307235047p:plain

この変数が管理しているTextも全てData Tableで一括に管理する事にします。

調べたら以下の7か所でのみ使用していました。

f:id:kazuhironagai77:20210307235108p:plain

うーん。思っているより少ないですね。

理由が分かりました。Acition状態でも結構な割合でComment Text Contentが使用されていました。

f:id:kazuhironagai77:20210307235129p:plain

Comment Text Contentに代入されるセリフは全部、Combat Dialogue data tableに移したと思ったんですが、まだ残っていました。これは後で直します。

以下の様にセリフを整理しました。

f:id:kazuhironagai77:20210307235146p:plain

簡単に解説します。

まず(始め)を開きます。

すると以下のような選択肢があります。

f:id:kazuhironagai77:20210307235207p:plain

ここで攻撃を選択したとします。

攻撃のJumpToCommentは1ですので、indexの1にJumpします。

Indexは0から始まるので番号の2がIndexの1に当たります。

f:id:kazuhironagai77:20210307235222p:plain

2の「を攻撃しました{nextline}」を開いて見ます。

f:id:kazuhironagai77:20210307235240p:plain

Answer Choiceに選択がありません。ここで終わりです。

所でAnswerのスペルが間違っていますね。後で直しておきます。

こんな感じでアドベンチャーゲームブックのように全てのセリフが繋がっています。つまりこのData Tableでセリフを一括管理している訳です。

以下に実際の実装を示します。

f:id:kazuhironagai77:20210307235256p:plain

全部載せる意義を感じないので一個だけにします。

テストします。

攻撃、魔法、道具、逃げるのそれぞれの場合で試しましたが特に問題は確認されませんでした。

5.5 Action状態でComment Text Contentに直接追加されるセリフを管理するData Table

Comment Text Content変数に追加すべき残りのセリフを調べると、

  • モンスターのセリフ:1
  • Action時のセリフ:2
  • 戦闘終了後のセリフ:2
  • MPHPの増減に関してのセリフ:4
  • 逃げるを選択した場合に関してのセリフ:2

となって結構バラバラです。

今までのようにこれらのセリフ全てを一つに繋げる事は出来なそうですが、出来るだけ整理します。

f:id:kazuhironagai77:20210307235330p:plain

やっぱり全部のセリフの関係性を繋げるのは不可能ではないですが、今回はパスします。

上二つのData Tableと同じやり方で実装しました。一個だけ例を載せておきます。

f:id:kazuhironagai77:20210307235348p:plain

テストもしました。

Indexで3、番号では4の「は勝利しました。{next line}」は「は勝利しました。{nextline}」が正しかったです。

後、Level Upした時のセリフを忘れていました。

それらを直しました。

5.6 Dialogue System 考察

これで一応、戦闘システムの報連相を担当していた数々のセリフが3つのData Tableにまとめられました。その内2つはそれぞれのセリフの前後関係もはっきり分かりData Tableを見るだけで全体像の把握が出来る仕組みになっています。

最後のData tableはセリフ同士の関係を正しく並べる事が出来なかったので全体像までは分かりません。ただし、今回はやりませんが、これも時間をかけて前の二つのData Tableと組み合わせれば前後関係がはっきり分かる様に作成出来るはずです。

このまとめ方はNPCの対話で発明した方法と全く同じです。

NPCとの対話でも店主との対話だけは教科書で紹介された方法で作成しましたが、勿論、店主との対話も他のNPCと同じ方法で作成する事が出来ます。

つまり、全てのセリフはNPCの対話で作成した形式で書く事が出来るのです。

そしてこのNPCの対話で作成した形式は、一括で全てのセリフを管理出来るだけでなく、セリフの前後関係も会話全体の流れもData Tableを見るだけで理解出来る大変優れた方法です。

NPCの対話で作成した形式を、もっと一般化した言い方をするとアドベンチャーゲームブック形式のまとめ方となります。

これについてはかなり深い考察をする必要があるので「アドベンチャーゲームブック形式によるセリフのまとめ方について」来週考察します。

6.更なるバグだし(1時間はこのゲームで遊ぶ)

先週、10分プレイしただけで大量のバグが見つかってゲームを中断せざるえませんでした。

今週は、1時間はプレイしてバグ出しします。

6.1 兎に角、10分間プレイ

武器屋で武器を買ったら値段が金貨0枚でした。

f:id:kazuhironagai77:20210307235431p:plain

朝になるときにPCが音します。

6.2 モンスターを全部倒す。

2体ほどモンスターを倒した後、隣村の武器屋で最強武器と防具を購入。残りのモンスターはその最強武器で倒した。

所要時間:全部のモンスターを倒すのに、10分程度かかった。

f:id:kazuhironagai77:20210307235452p:plain

見つかったバグ:

  • 勝利のポーズの時に剣が頭に刺さる。
  • モンスターが後ろを向いているとプレイヤーの操作するキャラがすぐ後ろに立っていても気が付かない。

6.3 色々な武器を試す

全部の武器のアニメーションを見てみます。

所要時間:20分程度

見つかったバグ:

弓矢の時、片手剣のモーションになっています。

f:id:kazuhironagai77:20210307235525p:plain

弓と剣のような装備が可能でした。正し、見た目はオカシクない。

f:id:kazuhironagai77:20210307235544p:plain

魔法の杖を装備したら名前が枠からはみ出て表示されました。

f:id:kazuhironagai77:20210307235601p:plain

モーションは両手剣、片手剣、弓、杖など何種類かあった方が良いと思いました。

実際に、以下のアニメーションが提供されているのでそれは可能だと思います。

f:id:kazuhironagai77:20210307235617p:plain

後、結構3D酔いします。1時間プレイしたらきついかもしれません。今週はこれで止めます。

6.4 他に気が付いた事

魔法のeffectが一個しかないのは退屈です。10種類は欲しいです。

7.Levelデザイン

今の所、以下の部分しかありません。

f:id:kazuhironagai77:20210307235655p:plain

因みに作成した地図は以下の様になっています。

f:id:kazuhironagai77:20210307235713p:plain

この地図は結構いい出来ですので、実際のマップも同じに作成するのが良いかもしれません。

ただし、現在ABC、CDEはmap1。JKL、LMNはmap2。そしてYZとXYZはmap3に配置してあります。

この設定は変えたくないです。

ところで、環境でマーケットのフリーを検索すると質の高い完成品があります。

f:id:kazuhironagai77:20210307235731p:plain

これをそのまま使用するのも一つの手であると思います。

普通の箱庭的なゲームのマップのサイズってどれくらいなんでしょうか?

うーん。知らないですね。

やっぱりある程度は自分で作成する事でLevel Designの勉強をすべきですね。

色々考えましたが、このアイデアが一番現実的と思います。

上の地図の海の部分で3分割します。それぞれの陸地を別のマップで作成します。

こんな感じです。

f:id:kazuhironagai77:20210307235749p:plain

ここに今あるmap1、map2、そしてmap3を対応させます。

これで行きましょう。

8.まとめと感想

段々、ジグソーパズルが埋まるような感じでゲームとして成り立つには何をすべきなのかが分かって来ました。

来週は以下の事についてやります。

  • 対話システムとアドベンチャーゲーム本形式によるセリフの整理方法の考察
    • Data Tableを使用する事で、全てのセリフを一括で管理出来る。
    • 全てのセリフの前後関係がData Tableを見るだけで分かる。
  • Game Modeクラスの整理の続き
  • バグの直し
    • 勝利のポーズの時に剣が頭に刺さる。
    • モンスターが後ろを向いているとプレイヤーの操作するキャラがすぐ後ろに立っていても気が付かない。
    • 戦闘時の武器のモーションの追加
    • 魔法の杖を装備したら名前が枠からはみ出て表示されていたのでそれを直す。
    • 魔法のeffectを増やす。
  • Levelデザイン

以上です。

 

 

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する 戦闘中のanimation の直し

f:id:kazuhironagai77:20210228233202p:plain

<前文>

他人の評価と

この前文では、日本の話題についてはあんまり書かないと決めています。その理由は、結構な頻度でその問題の当事者にあったりするからです。他人事で批判していたら、その当事者に会ったりした時、かなり気不味いです。ので基本的に日本の話題については書きません。今回だけは例外です。のでかなりボヤカして書きます。

ある人が社会的にかなり権威のある賞を取ったんですが、その時にその人が「俺を今まで馬鹿にしていた連中め。ザマーミロ。」と言ったんです。思わず呟いちゃったんでしょうね。これって社会人としてはかなり問題発言ですし、常識から考えても「今まで応援してくれた人達のお陰でここまで来れました。」位に留めておくべきでした。

しかし、それ聞いた時、私は逆に感動しちゃったんです。だってそんな賞取る人でも、完全無欠でここまで来た訳じゃなくて、そういう世間や上司などの第三者からの理不尽な低評価に、歯を食い縛りながら戦って来た事が分かったからです。彼も天才じゃなかったんです。見えない所で不断の努力を行っていたんですね。しかもずっと評価されない状態で。

何かを成すためには、第三者からの評価は絶対に必要です。自分勝手になったり、そこまで行かなくても客観的な事実に目を向けられずに、間違ったまま進んでしまったりしている時に、他人からの助言ではっと我に返る事が出来るからです。

しかし多くの第三者の人は理不尽としか思えない低評価しか付けない人が多いです。

他人の評価をする義務なんで無いです。実際ほとんどの人は他人の行為や作品に対しての評価はしてくれません。そんな中で評価したり、敢えて助言したりしてくれる人の意見は大変貴重で大切ですし、どんなに理不尽な評価でも本当にその人が思っている事ならば聞かねばなりません。

しかし本当にその人そう思っているんでしょうか?世間一般がそう言うから低評価しておいた。とか、この部下を低評価しておくと自分が上司から高評価受けるから。とか、あるいは、あからさまな悪意によって低評価している場合も多い様に思えます。

そうなった時に、その意見は聞かないと言う選択肢は有りと私は思っています。

しかし世の中の所謂、仕事が出来る人の多くは、そういう人の意見もかなり真剣に聞く傾向にあります。そういう人達は自分に対する戒めが大変強いので、第三者の評価を非常に大切にします。更に「悪意が在ったとしても助言は助言。」と考える人もいます。

それは私の経験から言うと大変危険です。

悪意のある助言や無能な人の評価を聞くのは、第三者の評価を全く聞かない以上に危ないんです。破滅する可能性まで在る最悪の選択です。

言っては何ですが、こういう仕事が出来る人って恵まれた環境で育っていて、本当に悪意がある人や本当に無能な人に今まで会った事が無いんです。厳しい言い方をすると悪意がある人の意見と真剣に助言してくれる人の区別が付かないし、無能な人でも雰囲気だけで有能そうと判断してしまったりします。

私が個人的に見聞した悪意ある助言は「取りあえずここは謝って」と「今日は家に帰ってゆっくり休んで」です。もし謝ったら「次は誠意が足りない。」と更なる譲歩を要求して、最終的には土下座か一筆書かされるまで追い込むそうです。家に帰すのは、貴方が不在の間に貴方に対しての何かしらの犯罪行為を行うためだそうです。

無能な助言で多いのが「数字の間違い」です。これは日本ではあまり無かったですが、アメリカで何回も経験しました。特に役所で。アメリカの恐ろしい所は、嘘には非常に厳しいのに間違いにはほとんどペナルティーが付かないんです。しかし間違いでも損害が発生しますから。損害を受ける側は大変です。

そこまで行っても「実際、悪意が在るのか、自分が意固地になっていて正しい助言に対して悪意があると思い込んでいるのか、自分では分からないじゃないか。」と反論されるかもしれません。

そこで妥協案です。「一回だけその人の助言を聞いて見たらと。」

絶対に無いでしょうが、それで劇的に改善したら聞き続けるべきです。もし変化が無いのなら、破滅する可能性がある意見は無視すべきでしょう。劇的に悪くなったら、その人に対して今すぐ最大級の警戒をすべきです。

最後にまとめると、馬鹿にしていたり低評価しかしない連中は、悪意を持っていたり無能な人の場合が多いですので、評価や助言は無視すべきです。

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

<本文>

1.今週の予定

戦闘中のアニメーションにPlayAnimation nodeを使用していますが、アニメーションは全部AnimBPで管理すべきでした。今週はここを直します。

  • モンスターのAnimBPを改良して、戦闘のAnimationAnimBPで表示出来る様にします。
  • 同様の方法で、Playerが操作するキャラのアニメーションもAnimBPで表示します。

アニメーションを直した後に時間がまだある場合は、このゲームをプレイしてバグ出しをします。

もしバグ出しした後でも、それでも時間があるならば、Levelデザインでもやってマップを完成させます。

2.PlayAnimation ノードを外して、戦闘中のアニメーションもAnimBPで管理する。(モンスターの場合)

2.1 モンスターのAnimBP の復習

AnimBPでどのようにアニメーションを管理しているのかすっかり忘れてしまいました。まず復習から始めます。

MonsterBPを開いて見ると

f:id:kazuhironagai77:20210228233541p:plain

使用しているAnim ClassはSkelSwordAni_Cとなっています。

f:id:kazuhironagai77:20210228233612p:plain

これですね。

f:id:kazuhironagai77:20210228233634p:plain

開いて見ると、まず変数としてSpeedとDelayがありました。

f:id:kazuhironagai77:20210228233707p:plain

以下に示した様に、開始して1.6秒経つとdelay変数がTrueになります。

f:id:kazuhironagai77:20210228233733p:plain

Speed変数はモンスターの移動スピードを表しています。

f:id:kazuhironagai77:20210228233754p:plain

そうだ。思い出して来ました。Animation BPにはEventGraphとAnimGraphがあったんです。

f:id:kazuhironagai77:20210228233813p:plain

今度はAnimGraphを見て行きます。

f:id:kazuhironagai77:20210228233921p:plain

思い出して来ました。このDefaultの中で色々な条件とそれに沿ったアニメーションを指定したんです。

それはそうと、このAnimGraphではDefaultとOutput Pose以上の作成出来るんですかね。もっとUE4アニメーションに詳しくなったらモット複雑な形のAnimGraphがあるんでしょうか?

Default stateの中を見ます。

f:id:kazuhironagai77:20210228233940p:plain

Spawn stateとIdle/Run stateがありその間に条件節が一個あります。

Spawn stateを見るとSkeleton_Swordman_Spawnのanimationを表示させています。

f:id:kazuhironagai77:20210228233958p:plain

Skeleton_Swordman_Spawnのanimationは以下のヤツです。

f:id:kazuhironagai77:20210228234016p:plain

f:id:kazuhironagai77:20210228234024p:plain

f:id:kazuhironagai77:20210228234032p:plain

f:id:kazuhironagai77:20210228234041p:plain

このアニメーションに掛かる時間が1.6秒でした。

もう分かりました。delay変数がTrueになるのが、Spawn stateからIdle/Run stateに移動出来るため条件ですね。

一応調べて見ます。

f:id:kazuhironagai77:20210228234111p:plain

f:id:kazuhironagai77:20210228234121p:plain

その通りでした。

最後のIdle/Runを見ます。

f:id:kazuhironagai77:20210228234303p:plain

あれ。思っていたより簡単な構造でした。

そうか。歩くのと走るのは、早歩きみたいな歩くと走るの中間があるから、それをBlendspace PlayerクラスであるSkelSword1Dで再現したんでした。

SkelSword1Dの中身も見てみます。

f:id:kazuhironagai77:20210228234322p:plain

Speedが345以上で完全に走り出します。Speedが85までは歩いています。

85?

AIでモンスターが探索する時の最大速度は幾つでしたっけ。

MyBehaivorTreeを見てみると150にセットされています。

f:id:kazuhironagai77:20210228234339p:plain

f:id:kazuhironagai77:20210228234347p:plain

計算式を見直さないと数値が同じ単位なのか分かりませんが、後で数値の確認をする必要がありますね。モンスターが探索している時は、歩くべきですので。

取りあえず、モンスターのAnimBPがどうなっているのかは分かりました。

2.2 戦闘時におけるモンスターのplayAnimationの使用箇所についての復習

RPGGameModeBP内の以下の部分でモンスターの戦闘中のアニメーションを担当していました。

f:id:kazuhironagai77:20210228234414p:plain

あれSetViewTargetwithBlendノードは何をする関数何でしょうか?

覚えていません。調べます。

f:id:kazuhironagai77:20210228234430p:plain

使用するカメラを切り替えるんでした。

あれ。でもこの設定だと、カメラを切り替えるだけですよね。

実際の戦闘では以下の様に、モンスターを正面から撮影しています。

f:id:kazuhironagai77:20210228234446p:plain

この設定だとモンスターの後ろ姿を撮影するんじゃ…。

分かりました。

元々、monsterBPのカメラはモンスターに対して正面を向いた状態で作成していました。

f:id:kazuhironagai77:20210228234509p:plain

戦闘中のカメラの管理については全く忘れていました。今週はやりませんが、このやり方が正しいのかの検証は必要ですね。

そしてPlayAnimationです。

f:id:kazuhironagai77:20210228234533p:plain

Skeleton_Swordman_Default_Attackアニメーションをplayしています。

f:id:kazuhironagai77:20210228234557p:plain

この時にSkelSwordAniからこのアニメーションをplay出来るようにすればいい訳ですね。

大体やり方が分かって来ました。

もう一か所、戦闘中のモンスターのアニメーションでPlayeAnimationを使用している箇所がありました。

f:id:kazuhironagai77:20210228234620p:plain

ここでは、モンスターが死んだアニメーションを流しています。

f:id:kazuhironagai77:20210228234636p:plain

これもSkelSwordAniからPlay出来る様にします。

2.3 SkelSwordAniにSkeleton_Swordman_Default_AttackアニメーションとSkeleton_Swordman_Dieアニメーションを追加する。

それでは、モンスターが使用するAnimation BPであるSkelSwordAniからSkeleton_Swordman_Default_AttackアニメーションとSkeleton_Swordman_Dieアニメーションが使用出来るようにします。

SkelSwordAni内に新しいBoolean変数、Attackを作成します。

f:id:kazuhironagai77:20210228234707p:plain

このAttack変数がTrueの時に、Skeleton_Swordman_Default_Attackアニメーションを流すようにします。

まずDefault StateでIdle/Run stateにAttack Stateを追加します。

f:id:kazuhironagai77:20210228234723p:plain

Attack StateではSkeleton_Swordman_Default_Attackアニメーションを流します。

f:id:kazuhironagai77:20210228234740p:plain

移行する条件は、Attack変数の値がTrueに成る事です。

f:id:kazuhironagai77:20210228234757p:plain

これでSkelSwordAniからSkeleton_Swordman_Default_Attackアニメーションが使用出来るはずです。

同様な方法で、Skeleton_Swordman_DieアニメーションもSkelSwordAniから使用出来るようにします。

f:id:kazuhironagai77:20210228234819p:plain

f:id:kazuhironagai77:20210228234825p:plain

f:id:kazuhironagai77:20210228234834p:plain

f:id:kazuhironagai77:20210228234841p:plain

これでSkelSwordAniからSkeleton_Swordman_Dieアニメーションも使用出来るはずです。

2.4 戦闘中にSkelSwordAniからSkeleton_Swordman_Default_Attackアニメーションを使用する。

RPGGameModeBPのここの部分を変更します。

f:id:kazuhironagai77:20210228234907p:plain

以下の様に実装しました。

f:id:kazuhironagai77:20210228234924p:plain

テストします。

普通に攻撃しています。

f:id:kazuhironagai77:20210228234956p:plain

出来ました。

整理して見やすいようにしました。

f:id:kazuhironagai77:20210228235015p:plain

2.5 戦闘中にSkelSwordAniからSkeleton_Swordman_Dieアニメーションを使用する。

今度は、Skeleton_Swordman_Dieアニメーションの番です。Skeleton_Swordman_Default_Attackアニメーションの場合と全く同じやり方で行います。

このPlayAnimationノードを外します。

f:id:kazuhironagai77:20210228235041p:plain

以下の様に実装しました。

f:id:kazuhironagai77:20210228235108p:plain

テストします。

モンスターを倒した時にこのアニメーションが流れましたが、その後で、モンスターが復活してしまいました。

f:id:kazuhironagai77:20210228235128p:plain

良く考えたら当たり前でした。直します。

f:id:kazuhironagai77:20210228235149p:plain

もう一回テストします。

今度は死ぬモーションをひたすら繰り返しています。

うーん。

もうちょっと考えたら直せそうですが、ここはPlayAnimationを使用した方が適切な気がしてきました。

この後、このモンスターがSkelSwordAniを使用して動く事はないからです。

やっぱりここは元に戻します。

f:id:kazuhironagai77:20210228235209p:plain

f:id:kazuhironagai77:20210228235216p:plain

確認のためのテストをします。

f:id:kazuhironagai77:20210228235234p:plain

元に戻りました。

3.PlayAnimation ノードを外して戦闘中のアニメーションもAnimBPで管理する。(プレイヤーの操作するキャラの場合)

プレイヤーの操作するキャラは、素手、武器、魔法を使用した時にそれぞれ別のアニメーションを使用する必要がありますし、特に魔法を使用した時にParticle Systemも使用します。モンスターの時よりかなり複雑なはずです。気を付けてやって行きましょう。

3.1 プレイヤーの操作するキャラのAnimBP の復習

プレイヤーの操作するキャラのためのCharacter Class ですが、ThirdPersonCharacterをそのまま使用していました。

f:id:kazuhironagai77:20210228235305p:plain

使用されているAnim ClassはMyThirdPerson_AnimBP_Cでした。

f:id:kazuhironagai77:20210228235332p:plain

MyThirdPerson_AnimBPを見て行きます。

まず変数ですが、以下の3つがありました。

f:id:kazuhironagai77:20210228235417p:plain

多分、SpeedとIsInAirは元々あった変数と思われます。WithWeaponは自分で追加した事を覚えています。

EventGraphを見てみます。

f:id:kazuhironagai77:20210228235449p:plain

見にくい。ちょっと整理します。

f:id:kazuhironagai77:20210228235508p:plain

EventGraphでは二つの変数の値を常にUpdateし続けていました。

自画自賛ですが、関数の下にその関数にパスする値を書く私のやり方、先週、発明したReroute  nodeを併用すると非常に見やすくなります。これはBPの書き方における大発明かもしれません。最初、この書き方を始めた時は、自分自身ではもっとも分かり易い書き方であると信じていましたが、誰もやっている人がいなくて内心かなり弱気でした。しかしこれを見れば、私の書き方が見やすい、理解しやすいのは一目瞭然です。

整理する前の書き方だと、TryGetPawnOwnerノードを中心に見てしまいます。しかしTryGetPawnOwnerノードからコードの流れを見ても何をやっているのか曖昧になってしまいます。あれっと最初からコードの流れを見る羽目になります。

所が私の書き方だと、まずこの部分の実装の目的は二つの変数、IsiInAirとSpeedに値をセットする事であると一発で分かります。その後でそれらにセットするための値はどうやって得ているのかを知りたくなったら、その下の関数らを追えば良いだけです。

今度はAnimGraphを見てみます。

f:id:kazuhironagai77:20210228235526p:plain

勿論、Default Stateを開きます。

f:id:kazuhironagai77:20210228235543p:plain

成程、分かりました。Idle/RunとIdle/RunWithWeaponのStateがあるんですね。

後、Jumpの機能もあったんですね。

走ったりするStateの中を開いて見る必要がない事は既に分かっているのでここまでにします。

3.2 戦闘時におけるモンスターのplayAnimationの使用箇所についての復習

戦闘時に攻撃を選択した時、武器を所持している時としていない時のアニメーションの表示にPlayAnimationを使用しています。

f:id:kazuhironagai77:20210228235608p:plain

武器を装備しているかどうかはRPGGameInstanceBPから調べるべきでしょう。何で敢えてAnimInstanceから調べているんでしょうか?

以下に示した様に、武器を装備している時はplayAnimationノードの後で、MyThirdPersonAnimBPをセットし直して更にMyThirdPersonAnimBPの変数WithWeaponをTrueに指定しています。

f:id:kazuhironagai77:20210228235627p:plain

本当に無駄手間をかけています。こここそ、全てのアニメーションをMyThirdPersonAnimBPで直接管理すべき所です。

次は魔法を選択した時のアニメーションです。魔法はParticle Systemを使用していますので複雑な構造になっていると思っていたのですが、実際の実装を見るとplayer の操作するキャラとは別なActorがParticle Systemを担当していました。

f:id:kazuhironagai77:20210228235648p:plain

これなら簡単に直せそうです。

更に戦闘に負けて死んだときと戦闘に勝利した時のアニメーションもPlayAnimationが使用されていました。

f:id:kazuhironagai77:20210228235732p:plain

f:id:kazuhironagai77:20210228235742p:plain

これらに使用されているアニメーションは両方とも武器なしのものだけでした。武器在りのアニメーションも追加したら見栄えがもっと良くなると思って提供されているアニメーションを調べたら武器なしのアニメーションしかなかったです。

f:id:kazuhironagai77:20210228235803p:plain

3.3 MyThirdPerson_AnimBPに武器なしの場合の攻撃アニメーションと武器ありの場合の攻撃アニメーション、そして魔法使用時のアニメーションを追加する。

以下の様に追加しました。

f:id:kazuhironagai77:20210228235828p:plain

新たに作成した変数は、MagicとAttackです。

f:id:kazuhironagai77:20210228235847p:plain

Attack変数の使用方法は「2.3 SkelSwordAniにSkeleton_Swordman_Default_AttackアニメーションとSkeleton_Swordman_Dieアニメーションを追加する。」での使用方法と全く同じです。

Magicを使用した場合は武器を装備している時としていない時で返るstateが違います。以下の様に設定しています。

武器装備なしの場合

f:id:kazuhironagai77:20210228235905p:plain

武器装備在りの場合

f:id:kazuhironagai77:20210228235923p:plain

多分これで上手く行くそれぞれのStateに帰るはずです。

戦闘に負けて死ぬアニメーションと戦闘に勝って処理のポーズを取るアニメーションはPlayAnimationを使用する予定なので、これらのアニメーションはMyThirdPerson_AnimBPには追加しません。

3.4 戦闘中にMyThirdPerson_AnimBPから攻撃アニメーションを使用する。

以下の様に攻撃アニメーションの実装を変更しました。MyThirdPerson_AnimBPの実装が正しくされていれば武器装備ありと武器装備なしで分けてアニメーションを指定する必要はないはずです。

f:id:kazuhironagai77:20210301000005p:plain

テストします。

武器を装備したらいきなり攻撃モーションを繰り返し始めました。

調べたら以下の部分を逆に設定していました。

f:id:kazuhironagai77:20210301000046p:plain

もう一度テストします。

武器なしの場合です。

f:id:kazuhironagai77:20210301000159p:plain

武器なしのモーションが選択されています。

武器在りの場合です。

f:id:kazuhironagai77:20210301000230p:plain

武器在りのモーションが流れました。

出来てますね。

コードを整理します。

f:id:kazuhironagai77:20210301000300p:plain

たったこれだけになりました。

3.5 戦闘中にMyThirdPerson_AnimBPから魔法アニメーションを使用する。

今度は魔法アニメーションです。以下の様に実装し直しました。

f:id:kazuhironagai77:20210301000343p:plain

テストします。

何回も同じモーションを繰り返しています。

魔法のモーションが約0.7秒だったので0.7秒辺りでMyThirdPerson_AnimBP のMagic変数の値をFalseに戻しました。

f:id:kazuhironagai77:20210301000403p:plain

テストします。

まず武器なしで魔法を使用した時です。

f:id:kazuhironagai77:20210301000427p:plain

その後、攻撃を選択します。

f:id:kazuhironagai77:20210301000442p:plain

素手の攻撃モーションを使用しています。

今度は武器ありで魔法を使用します。

f:id:kazuhironagai77:20210301000500p:plain

その後、攻撃を選択します。

f:id:kazuhironagai77:20210301000514p:plain

武器の攻撃モーションを使用しています。

出来ていました。

3.6 戦闘中のコードを整理する。

RPGGameModeBP内の戦闘中の実装が以下の様にぐちゃぐちゃなので整理します。

f:id:kazuhironagai77:20210301000535p:plain

以下の様に整理しました。

f:id:kazuhironagai77:20210301000553p:plain

もう少し整理出来ますが、この状態でテストします。

武器在りの時です。

魔法を使用します。

f:id:kazuhironagai77:20210301000619p:plain

その後、攻撃します。

f:id:kazuhironagai77:20210301000636p:plain

武器で攻撃しています。出来ています。

今度のテストは武器なしの時です。

魔法を使用します。

f:id:kazuhironagai77:20210301000705p:plain

その後攻撃します。

f:id:kazuhironagai77:20210301000722p:plain

武器なしの攻撃をしています。

出来ています。

もう少しだけ整理します。

色んな所でMyThirdPerson_AnimBPにアクセスしているので変数として作成します。

f:id:kazuhironagai77:20210301000740p:plain

RPGGameModeBP内のEventBeginPlay関数で、以下に示した様に変数MyThirdPersonAnimBPを作成しました。

f:id:kazuhironagai77:20210301000802p:plain

まだまだスパゲッティコードですが、前よりは遥かに見やすく成りました。

f:id:kazuhironagai77:20210301000819p:plain

確認のテストをします。

スクリーンショットは取らなかったですが出来ている事は確認出来ました。

4.兎に角、Playしてバグを見つける

4.1 Playします。

まず、プレイ中の音楽が無いです。

夜になると真っ暗で何も見えません。

f:id:kazuhironagai77:20210301000846p:plain

PCが突然、凄い音出し始めました。

f:id:kazuhironagai77:20210301000903p:plain

地面の無い所にいったらフリーズしました。

戦闘から戻った時にカメラの方向が一定です。

戦闘中に「読みましたボタン」が表示されないバグが発生しました。

f:id:kazuhironagai77:20210301000919p:plain

再現性もあります。

Level10に成れないためのバグかもしれません。

f:id:kazuhironagai77:20210301000936p:plain

「読みましたボタン」が表示されないバグではなく勝利シーンに移行できないバグのようです。

たった十分程度Playしただけでこれだけのバグが出て来ました。

でもそれなりに面白かったです。

4.2 バグのまとめ

以下のバグが見つかりました。

  • プレイ中の音楽が無い。
  • 夜になると真っ暗で何も見えない。
  • PCが突然、凄い音出し始めました。(ずっとではない。)
  • 地面の無い所にいったらフリーズした。
  • 戦闘から戻った時にカメラの方向が一定。
  • 戦闘から戻れないバグ発生。

これらのバグを直します。

5. バグの直し

5.1 プレイ中の音楽の追加

以下の方法で、Map1にBGMを追加しました。

f:id:kazuhironagai77:20210301001021p:plain

BGMは場所によって変化させる予定なので音の編集が出来るSpawnSound2Dノードを使用します。

使用するCueは以下のデータです

f:id:kazuhironagai77:20210301001042p:plain

Mega Game Music Collectionから使用しました。

f:id:kazuhironagai77:20210301001119p:plain

BGMをループさせるため、Cueを開いてLoopingにチェックします。

f:id:kazuhironagai77:20210301001143p:plain

モンスターが発生する危険な地域や、町や城でBGMは変更しますが、それは後で作成します。

5.2 夜になると真っ暗で何も見えない。

もう単純にPoint LightをThird Person Characterに追加しました。

f:id:kazuhironagai77:20210301001207p:plain

Lightが無い状態です。

f:id:kazuhironagai77:20210301001224p:plain

Lightがある状態です。

f:id:kazuhironagai77:20210301001239p:plain

かなり見やすく成っていますが、回りを見渡すまでは明るくはないです。今回はこれ位にしておきます。

5.3 PCが突然、凄い音出し始めました(ずっとではない)。

再現したくて何回もゲームをPlayしたがその後、一回もそんな事態にはならなかったです。

F5でShaderの負担をチェックしたが特に特別に重そうな所は見つからなかったです。

f:id:kazuhironagai77:20210301001302p:plain

f:id:kazuhironagai77:20210301001310p:plain

原因が不明なのでこのバグの修復はパスします。

5.4 地面の無い所にいったらフリーズした。

落ちたら死ねば良いだけですが、どうやって実装しましょうか?

思い付きました。

この方法はベストではないでしょうが、これでも出来るはずです。

MyThirdPerson_AnimBPに二つの変数、IsInAirとSpeedがあります。

f:id:kazuhironagai77:20210301001337p:plain

更に、ThirdPersonCharacterのMax Walk Speedを見ると600になっています。

f:id:kazuhironagai77:20210301001356p:plain

この二つから推測すると、IsInAirがtrueでSpeedが600以上だった場合、プレイヤーが操作するキャラが墜落していると考えて良いのではないでしょうか?

RPGGameModeBPのTick関数に以下のコードを追加しました。

f:id:kazuhironagai77:20210301001417p:plain

これでPlayerが操作するキャラが墜落した時が分かるはずです。

墜落した場合は墜落死を知らせる新しいmapを開きます。

f:id:kazuhironagai77:20210301001438p:plain

そのためのWidgetも作成しました。

f:id:kazuhironagai77:20210301001456p:plain

テストします。

f:id:kazuhironagai77:20210301001513p:plain

落ちます。

一瞬でGame Overに成りました。

f:id:kazuhironagai77:20210301001536p:plain

もっと発展させる必要はありますが、最低限の機能は出来ました。

5.5 戦闘から戻った時にカメラの方向が一定

これが何で成るのかが分からないです。

考えられるのはRootCompomentとして指定されているCapsuleComponentにScaleしか指定出来ないため指定出来ないのかもしれません。

f:id:kazuhironagai77:20210301002120p:plain

f:id:kazuhironagai77:20210301002128p:plain

色々試したのですが、結局原因は不明でした。

カメラとCameraBoomの関係をもう一度勉強しないと理解出来なそうです。

色々調べたら、PlayerStartの向きとカメラの向きが同じになっているみたいです。

それならPlayerStart を消してPlayerが操作するキャラを動的に作成すれば向きも自然になると思いその方法を調べました。

以下の方法でPlayerが操作するキャラを動的に作成出来るそうです。

f:id:kazuhironagai77:20210301002153p:plain

この方法を試してみます。

以下の方法でMap1、LevelBP内に実装しました。

f:id:kazuhironagai77:20210301002217p:plain

テストします。

f:id:kazuhironagai77:20210301002233p:plain

後ろ向きで戻って来ました。

ただし。大量のエラー報告と一緒にですが…

f:id:kazuhironagai77:20210301002256p:plain

直していきます。

最初のエラーの原因はRPGGameModeBPのMyThirdPersonAnimBPがnullだからでした。その原因はRPGGameModeBPのEventBeginPlayの時にはまだPlayerが操作するキャラを作成していないからです。

Playerが操作するキャラはRPGGameModeBPで作成する事にします。

f:id:kazuhironagai77:20210301002319p:plain

更に戦闘時の場合はPlayerが操作するキャラの生成される場所が違うのでそれに対応した別のSpawnPlayerCharacterBattleFiledも作成します。

f:id:kazuhironagai77:20210301002343p:plain

Levelによってそれぞれのイベントを呼び出します。

f:id:kazuhironagai77:20210301002419p:plain

テストします。

素手の戦闘では上手く行きましたが、武器を持った場合、元に戻った時、以下の様なエラーが発生しました。

f:id:kazuhironagai77:20210301002439p:plain

原因はRPGGameModeBPでPlayerが操作するキャラを作成して更に、Map1でも作成してたからと思われます。

Map1の作成は外します。

f:id:kazuhironagai77:20210301002459p:plain

今度は大丈夫でした。

f:id:kazuhironagai77:20210301002524p:plain

以下のWarningが出ています。何なんでしょうか?

f:id:kazuhironagai77:20210301002649p:plain

分かりませんね。

ThirdPersonCharacterを(0,0,0)の位置にSpawnせよという命令はしていないはずですが。

5.6 戦闘から戻れないバグ発生

レベルが10になった時、以下のif節に入ってエラーになっていました。

f:id:kazuhironagai77:20210301002714p:plain

色々検討した結果、このIf節はrowがNullの全ての場合に対応しているので、消す訳にはいかないので以下のような変更にしました。

f:id:kazuhironagai77:20210301002733p:plain

テストします。
Levelが10に上がってゲームが途中で止まる事も無くなりました。

f:id:kazuhironagai77:20210301002753p:plain

ここは直ったんですが、以下のコードがLevel10になった時にゲームを停止させてしまう事に気が付きました。

f:id:kazuhironagai77:20210301002811p:plain

取りあえず試してみます。

やっぱり停止しました。

f:id:kazuhironagai77:20210301002833p:plain

以下のコードを追加してそもそもLevelが10になった時はLevelUpの検査を受けないようにしました。

f:id:kazuhironagai77:20210301002851p:plain

テストします。

普通に戦闘が終了しました。

直りました。

6.バグ出しもう一回

「5.5 戦闘から戻った時にカメラの方向が一定」でPlayerが操作するキャラをLevel上に配置したPlayerStartから静的に生成するのではなく、動的にBPから生成する事にしました。

この大改造により、バグが大量に生成されていると思われます。

現状把握しているだけでも、以下のWarningは出続けていますし。

f:id:kazuhironagai77:20210301002921p:plain

もう一回バグ出しをやって行きます。

6.1 スタート画面でエラー、更に墜落死画面でもエラー

直します。

以下の様にしました。

f:id:kazuhironagai77:20210301003003p:plain

ここで動的にPlayerの操作するキャラを作成しなければ勝手にPlayerStartの位置にThirdPersonCharacterが作成されるはずです。

試してみます。

出来ました。

出来ましたが、またエラーが大量に発生しました。

f:id:kazuhironagai77:20210301003022p:plain

直します。

ここでエラーが発生しています。

f:id:kazuhironagai77:20210301003040p:plain

成程。このやり方だとMyThirdPersonAnimがnullになってしまうんですね。

直します。

以下の様に直しました。

f:id:kazuhironagai77:20210301003058p:plain

まずEventを止めました。基本的にEventは電話と同じ機能をProgrammingで実装したものです。全ての作業を中止しても対応しなければならない、突然発生する事態(つまり電話の対応)のための機能です。

Playerのキャラを作成する事は突然発生しません。毎回、Levelを開く時にする必要があります。のでEventは使用しません。

代わりに関数、SpawnPlayerCharを作成しました。

f:id:kazuhironagai77:20210301003118p:plain

キャラの生成する位置と角度を指定するとキャラを動的にSpawnしてくれます。

それぞれのLevelや戦闘が発生した場所で、キャラの生成する位置と角度は変わりますので、そこに対応出来る様にします。

f:id:kazuhironagai77:20210301003138p:plain

この部分はもっとマシな書き方があると思います。後で、少し考えます。今回はこれで行きます。

最後に、エラーが出た変数に値をパスします。これでエラー出ないはずです。

f:id:kazuhironagai77:20210301003202p:plain

テストします。

まずスタート画面です。

エラーの表示はありません。出来ています。

f:id:kazuhironagai77:20210301003228p:plain

出来ていますが、何か画面が異常に明るいです。

そうだ。ThirdPersonCharacterにPointLightを追加した事を忘れていました。ここではそのPointLightは切っておくべきですね。

「新しく始める」をクリックしました。

Map1が作成されました。

f:id:kazuhironagai77:20210301003250p:plain

特にエラーの発生はありません。落下してみます。

ゲームオーバーになりました。

f:id:kazuhironagai77:20210301003313p:plain

エラーの表示もないのでバグは直りました。

6.2 スタート画面の修正

スタート画面でPlayerの操作するキャラが明るすぎるのでそれを修正します。

以下の様に直しました。

f:id:kazuhironagai77:20210301003353p:plain

テストします。

f:id:kazuhironagai77:20210301003410p:plain

暗くなっています。

Map1に入ると今度はPlayerの操作するキャラは明るくなっています。

出来ています。

あれ?

Monsterが動いていません。

f:id:kazuhironagai77:20210301003430p:plain

新たなバグの発見です。

6.3 モンスターを動かす

何で動いてないんでしょうか?

AIが機能していないんでしょうか?

Behavior TreeのDebug機能で調べて見ると、Blackboard Decorator ノードであるIsRandomPlace?がFalseを返している事が分かりました。

f:id:kazuhironagai77:20210301003454p:plain

IsRandomPlaceノードの設定を見ると、以下に示した様に、Blackboardの変数、RandomPlaceの値をチェックしています。

f:id:kazuhironagai77:20210301003512p:plain

この値に問題があるみたいです。

BlackboardのRandomPlaceの値は、ServiceクラスであるFindRandomPlace内で指定されています。

FindRandomPlaceを見るとGetRandomReachablePointInRadiusがFalseを返してる事が分かります。

f:id:kazuhironagai77:20210301003531p:plain

更に、GetActorLocationはあるActorの位置情報は返してる事も分かりました。

と言う事は、GetRandomReachablePointInRadiusが適切な場所が見つけられないからモンスターは動いていないと言う事になります。

確認のために他のモンスターの場合も確認しましたが、以下に示した様に全く同じ問題が発生していました。

f:id:kazuhironagai77:20210301003558p:plain

何で、GetRandomReachablePointInRadiuが機能しないのでしょうか?

一番考えられるのは、NavMeshBoundVolumeが機能していない事です。

確認します。

NavMeshBoundVolumeは効いていませんでした。

取りあえず、左側のNavを動かしたらそれだけは効くようになりました。

f:id:kazuhironagai77:20210301003617p:plain

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

以下に示すように凄い勢いで追いかけて来ました。

f:id:kazuhironagai77:20210301003634p:plain

直りました。

6.4 以下のWarningについて

今週最後は、以下のWarningについて調べます。

f:id:kazuhironagai77:20210301003654p:plain

Output Logを読み直したらすぐ分かったのですが、これはPlayerStartを消した事から起きるみたいです。

f:id:kazuhironagai77:20210301003710p:plain

となると、直せませんね。もしPlayerStartを追加したら、戦闘マップから戻って来た時に、playerの操作するキャラの向きが一定になってしまいます。

Packagingの時とかに問題にならないと良いですね。

7.来週以降にやらないといけない事

今週中にバグ出しを終えたかったんですが、無理でした。以下に来週以降やらないといけない事をまとめておきます。

  • モンスターの歩く速度の設定がアニメーションとAIで全然違う
  • BGMの変更、町や村、モンスターが居る廃墟で別々のBGMにする。
  • BPの整理。RPGGameModeBPを整理します。
    • そこら中でThirdPersonCharacter変数に値をセットしている。過去、EventBeginPlay関数がBPから呼べなかったので毎回呼び出していた。これを直す。
    • 戦闘システムから呼ばれるEventを整理してまとめる。
  • セリフシステムの整理。確認出来るだけで3種類以上の方法でセリフを管理している。これを統一する。
  • 更なるバグだし。せめて1時間はこのゲームで遊んでバグ出しする。(今週は10分だった。)
  • Levelデザイン。

これらの事をやっていきます。

8.まとめと感想

今週はここまでです。戦闘マップから戻って来た時に、向きが変わってしまう問題は一応解決できました。Levelが10になるとゲームが止まってしまうバグは予想外でした。まだまだ、先は長いですが、ジグソーバズルが埋まって行く時に感じるような手ごたえを感じれるようになって来ました。

 

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する Saveの改良 Part 2

f:id:kazuhironagai77:20210221234157p:plain

<前文>

Genocideそして「進撃の巨人 Final SeasonPart 2

先週、GenocideとMassacreは日本語に訳すとどちらも虐殺になりますが、英語でGenocideはある民族を完全に消滅する事で単なる虐殺であるMassacreとは全く違う日本には存在しない概念であり、更に世界ではGenocideは身近であると話しました。

そして「進撃の巨人 Final Season」の隠れたテーマがGenocideであるが故に日本ではあまりヒットしないにも関わらず日本以外の世界中で大ヒットしているとの話を今週はします。

ここからは「進撃の巨人 Final Season」のGenocideを軸としての私の個人的な解説をしますが、詳細は間違っているかもしれません。忙しいのであんまりアニメを見る時間がないのでその辺はご了承下さい。

進撃の巨人 Final Season」では世界中の民族が主人公の民族をGenocideしようとしています。所が数年の間だけですが、主人公の民族は強力な兵器を所持していてそれを使用すれば逆に世界の主人公以外の民族を滅ぼす事が出来ます。

ここで3つの選択が出来ます。

  1. 今すぐ戦争を仕掛けて、世界を滅ばして自分たちだけ助かる
  2. 世界と話し合って、お互いにGenocideしないよう約束して共存共栄する。
  3. 自分たちだけ助かるために自分たち以外の全ての民族を滅ぼすのは罪が重すぎるので、自分たちが絶滅するのを受け入れる。

1の選択は最も現実的ですが、最もモラルに反しています。簡便に検証しても以下の道徳に反しています。

  • 自分たちの民族がGenocideされるのを避けるために、他の民族はGenocideしても構わないと言う矛盾。
  • 更に現状では、奴隷みたいな立場ですが、一応服従を誓えば、主人公の民族もギリギリGenocideされないで生きていける選択もあります。それを捨てて自分たちの民族がGenocideされる前に自分たち以外の全ての民族をGenocideしてしまおうという自分勝手な選択。

2の選択は最も理想的ですが、現実に達成可能なのか?常に疑問が付きまといます。例えば、

  • 数年経った後で、世界の軍事力が主人公達の民族より強大になってから約束を反故にされる可能性。
  • 主人公達の民族が持つ強大な軍事力を放棄する事を条件に、お互いにGenocideしないよう約束するが、放棄した途端に攻めて来られる可能性。

3の選択は人間としての道義を守り、かつ実現可能でもありますが、主人公達の民族にとっては悲惨過ぎる結末しか待っていません。

平均以上の常識と知性がある人なら、全ての人にとって受け入れられる選択は2しかないと直ぐに分かります。そして2を選択しつつ最悪3になったらそれも受け入れるしかない位の結論に到達するはずです。

所が、このアニメの主人公、何のためらいもなく1を選択します。

この主人公は世界を弱肉強食、勝者のみが自由を手に入れる事が出来る残酷なものと見なしているからです。主人公にとってはGenocideするかされるか以外の選択は存在しないんです。そしてその戦いに勝ったものだけが自由を手に入れられると心の底から思っています。

この主人公の選択を骨董無形で自分勝手と断ずるのは簡単ですが、世界の視聴者はこの主人公の決断を自分たちの環境に置き換えるとGenocideが身近に在るが故に真剣に受け止めざる得なくなるのです。

その結果「進撃の巨人 Final Season」は世界で大ヒットをし続けるのです。

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

<本文>

1.今週の予定

今週はセーブ機能を完成させます。

  • Game Instance BPクラス内のセーブに必要な変数の値をsave出来る様にする。
  • UE4C++でのGame Instanceクラス内の変数の値をsave出来る様にする。
  • 神官のSave機能を実装する

これらをやって行きます。

2.Game Instance BPクラス内のセーブに必要な変数の値をsave出来る様にする。

2.1 先週の調査の復習

先週の調査でRPGGameInstanceBP内の変数でSave/Loadする時に必要な変数は以下の3つでした。

f:id:kazuhironagai77:20210221234349p:plain

ただしMonster Spawn Dataはやっぱり無くても良い事になったので結局はMap変数とItem Spawn Data 変数の値だけです。

これらの変数の値がセーブ出来るようにします。

2.2 Map変数の値がセーブされるようにする

Map変数は以下に示した様にEnumクラスから作成されています。

f:id:kazuhironagai77:20210221234414p:plain

まずこの変数をSave Gameクラスから派生して作成したクラスに追加してみます。

あれ、SaveGameクラスから派生したBPクラスが見つかりません。

うーん。

調べたら前回は以下の方法でSave機能を作成していました。

ポーズ画面でセーブボタンを押すと、

f:id:kazuhironagai77:20210221234431p:plain

GameInstanceの関数、SaveMyGameを呼び出します。

f:id:kazuhironagai77:20210221234448p:plain

SaveMyGameはUE4C++のRPGGameInstanceで作成されています。

f:id:kazuhironagai77:20210221234504p:plain

実装部を見ると、SaveGameクラスから作成したMySaveGameクラスが使用されています。

f:id:kazuhironagai77:20210221234522p:plain

MySaveGameクラスは以下のように作成しています。

f:id:kazuhironagai77:20210221234543p:plain

f:id:kazuhironagai77:20210221234551p:plain

うーん。このやり方だとBPにある変数はSave出来ないですね。

この関数は取りあえず維持したまま、MySaveGameクラスからBPを作成してそこに変数を追加していきます。

f:id:kazuhironagai77:20210221234611p:plain

Enum、MapクラスからMap変数を作成します。

f:id:kazuhironagai77:20210221234824p:plain

Enumの名前をMapにしてしまったために、Map(dictionary)と紛らわしくなってしまいました。LevelNameとかにすれば良かったです。

後、Pauseメニューからはsaveが出来る仕様にはしない事にしたので、この機能は後で消します。代わりに魔法ボタンを追加します。

先週作成した、神官の会話UIのセーブボタンに

f:id:kazuhironagai77:20210221234855p:plain

以下の実装を追加します。これでSlotにsaveされるはずです。

f:id:kazuhironagai77:20210221234914p:plain

Slot名は前に作成したSave機能と間違えないために新しい名前のMySavedGameにします。

Load機能は以下のスタート画面のロードするボタンをクリックした時に実行するようにします。

f:id:kazuhironagai77:20210221234954p:plain

現状は以下の様になっています。

f:id:kazuhironagai77:20210221235010p:plain

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

f:id:kazuhironagai77:20210221235027p:plain

何で、GameGold変数の値がBPから変更出来るのか?

UPROPERTYでBlueprintReadWriteに指定されていました。

f:id:kazuhironagai77:20210221235043p:plain

後、EditAnywhereに指定されていますが、このコードを書いたとき、今一EditAnywhereの意味が分かってなかったのでしょうね。

以下に示した公式のこのサイト

f:id:kazuhironagai77:20210221235059p:plain

EditAnywhere、EditDefaultOnly、そしてEditInstanceOnlyの違いが説明されていますが、

f:id:kazuhironagai77:20210221235115p:plain

f:id:kazuhironagai77:20210221235122p:plain

ここで大切なのは、ArchTypesとInstanceの違いです。ArchTypesとは簡単に言えばクラスの事で、EditDefaultOnlyにした場合、その変数はそのクラスで一個の値しか持てません。EditInstanceOnlyはその逆で、その変数の値はそれぞれのInsntanceによってバラバラの値になります。EditAnywhereは、ClassからでもそれぞれのInstanceでも値の指定が出来ます。

と言う事です。

Game Instanceは一個しか作成されないのにそのクラスの変数がEditAnywhereを持つ必要はないはずです。EditDefaultOnlyに変更しました。

f:id:kazuhironagai77:20210221235242p:plain

テストします。

Saveは普通に出来たみたいです。

f:id:kazuhironagai77:20210221235256p:plain

Loadします。

エラーになってゲームが強制終了になってしまいました。

f:id:kazuhironagai77:20210221235348p:plain

デバックして調べて見ると、Loadの実装の最後のOpenLevelでエラーになっていました。

f:id:kazuhironagai77:20210221235445p:plain

何で?とLevelNameにパスされている値を見たら、以下の様に成っていました。

f:id:kazuhironagai77:20210221235502p:plain

理屈は良く分からないのですが。LoadしたGameInstanceの変数から参照したらmap1と表示されたのでこっちを使用します。

f:id:kazuhironagai77:20210221235517p:plain

テストします。

またエラーになってしまいました。

調べたらMapからStringに変換した時はmap1が表示されますが、MapからNameに変換した時は、Map::NewSameEnumerator1が表示されました。

f:id:kazuhironagai77:20210221235609p:plain

じゃ、一端Stringに変換してNameに直してみます。

f:id:kazuhironagai77:20210221235626p:plain

テストします。

今度は出来ました。

f:id:kazuhironagai77:20210221235643p:plain

所持金も金貨95枚になっているので、Saveした時に神官に支払った金貨5枚が減った状態できちんとセーブされていました。

f:id:kazuhironagai77:20210221235659p:plain

やっと出来ました。

2.3 Item Spawn Data 変数の値がセーブされるようにする

ItemSpawnDataはクラスItemSpawnDataのarrayですね。

f:id:kazuhironagai77:20210221235802p:plain

まずItemSpawnDataクラスを調べます。

調べたらItemSpawnDataはStructでした。

f:id:kazuhironagai77:20210221235823p:plain

これはShallow copyになりそうですね。

取りあえず、MySaveGameBPにItemSpawnDataクラスのarrayを作成します。

f:id:kazuhironagai77:20210221235843p:plain

ここにRPGGameInstanceBPの値をセットします。

ここまでやってあれなんですがArrayの前にStructのそれぞれの値がSaveGameクラスでどのように保持されるのか知りませんでした。こっちから調べます。

2.3.1 StructのデータとSaveGameクラス

StructのデータがどのようにSaveGameクラスに保持されるのかを調べます。

先程作成した、ItemSpawnDataのarrayを変数にします。

f:id:kazuhironagai77:20210221235912p:plain

以下の方法でRPGGameInstanceBPの最初の要素の値をMySaveGameBPのItemSpawnDataにセットしました。

f:id:kazuhironagai77:20210222000005p:plain

勿論、Get はCopyを使用しています。

以下の方法で、Slotから読み込んだMySaveGameBPのItemSpawnDataの値を見てみます。

f:id:kazuhironagai77:20210222000022p:plain

普通に全部正しい値でセーブされていました。

f:id:kazuhironagai77:20210222000039p:plain

おお。

これGetをreferenceにした場合はどうなんでしょうか?

f:id:kazuhironagai77:20210222000100p:plain

GetがReferenceでも出来ていますね。

ここまでやって気が付いたんですが、Saveに必要な情報ってItemをSpawnするかどうかだけでした。Boolean変数のArrayを作成すれば良いだけでした。

もう一回やり直します。

MySaveGameBPの変数をItemSpawnBoolとしてBoolタイプのArrayにします。

f:id:kazuhironagai77:20210222000125p:plain

以下の方法でRPGGameInstanceBPのItemSpawnDataの要素、Spawnの値をMySaveGameBPのItemSpawnBoolに渡します。

f:id:kazuhironagai77:20210222000222p:plain

前にやったのと同じ方法で、Slotセーブしたデータから作成したMySaveGameBPのItemSpawnBoolの値を調べます。

f:id:kazuhironagai77:20210222000239p:plain

MySaveGameBPのItemSpawnBoolの値はslotに正しくセーブされていました。

f:id:kazuhironagai77:20210222000301p:plain

もう一度確認します。

こんどはアイテムを一個だけ回収した後で、セーブしました。

f:id:kazuhironagai77:20210222000316p:plain

出来ていますね。

ではLoad時にこのArrayの値を元にRPGGameInstanceBPのItemSpawnDataの要素、Spawnの値を変更するようにします。

スパゲッティコードになりそうなので関数で実装しました。

f:id:kazuhironagai77:20210222000332p:plain

中身は以下の様に成っています。

f:id:kazuhironagai77:20210222000351p:plain

Arrayの関数をしっかり勉強したら、この方法より簡単な実装方法がありそうなんですが、とりあえずこれで行きます。一応これでもArrayの要素の一部の値の変更が可能なはずです。

テストします。

マップ内に配置されているアイテムの内、盾だけを回収しました。

f:id:kazuhironagai77:20210222000407p:plain

Saveしてloadします。

盾だけ無くなっていました。

f:id:kazuhironagai77:20210222000448p:plain

出来ました。

3.UE4C++RPGGameInstanceクラス内の変数の値をsave出来る様にする。

UE4C++のRPGGameInstanceクラス内の変数でsaveする必要がある変数をSave/Load出来るようにします。

以下の変数がSaveする必要がある変数です。

f:id:kazuhironagai77:20210222000543p:plain

上に行くほど、Save/Loadの作成が難しくなっているので下から作成していきます。

3.1 IsArmorEquippedの値をSave/Load出来る様にする。

この変数は、BlueprintReadWriteで指定されているし、単なるBoolean変数なので今までの知識で簡単にSave/Load出来るはずです。

MySaveGameBPに新しい変数、IsArmorEquippedを作成します。

f:id:kazuhironagai77:20210222000628p:plain

最初はBPのMySaveGameBPでなくUE4C++のMySaveGameに作成した方が良いかと思いましたが特にUE4C++側で作成しなければならないメリットはないのでBP側で作成します。

以下の方法で実装しました。特に新しい事はしていません。

f:id:kazuhironagai77:20210222000650p:plain

f:id:kazuhironagai77:20210222000657p:plain

ここでテストしようと思ったのですが、以下に示した通り、RPGGameInstanceのArmorEquipped変数の値もセットされていないとエラーになってしまうのでこれをセットしてからテストします。

f:id:kazuhironagai77:20210222000715p:plain

3.2 ArmorEquippedの値をSave/Load出来る様にする。

そういうわけでArmorEquippedの値を次にやります。

f:id:kazuhironagai77:20210222000747p:plain

BlueprintReadWriteですし、やった事ないのはFStringだけです。

以下に示した様に、今までと全く同じやり方でやりました。

f:id:kazuhironagai77:20210222000821p:plain

f:id:kazuhironagai77:20210222000828p:plain

これでテスト出来るはずです。

木の盾を装備した状態でSaveしました。

Loadします。

以下に示した様に木の盾を装備して登場しました。

f:id:kazuhironagai77:20210222000851p:plain

出来ました。

3.3 IsWeaponEquippedとWeaponEquippedをSave/Load出来るようにする。

3.1と3.2でした事と全く同じ事をIsWeaponEquippedとWeaponEquippedにします。

f:id:kazuhironagai77:20210222000929p:plain

f:id:kazuhironagai77:20210222000936p:plain

テストします。

剣を装備した状態でSaveし、その後、Loadしてゲームを開始しました。

剣を装備した状態から始まりました。

f:id:kazuhironagai77:20210222001049p:plain

出来ています。

3.4 ItemsをSave/Load出来るようにする。

WeaponよりもItemの方が値段が安いのでテストしやすいと思い、Itemsを先にやる事にしました。

f:id:kazuhironagai77:20210222001119p:plain

まずBlueprintReadOnlyで指定していますが、Arrayなんです。Loadする時Add関数を使用すればBPからでもこの変数に要素の追加が出来たはずです。となると敢えてBlueprintReadWriteに直す必要もない事になります。

UE4C++のRPGGameInstanceはそのままでやって行きます。

以下の方法でMySaveGameBPのItems変数にRPGGameInstanceのItem変数の値を移します。

f:id:kazuhironagai77:20210222001137p:plain

Load時には、以下の方法でMySaveGameBPの値をRPGGameInstanceに移します。

f:id:kazuhironagai77:20210222001156p:plain

見やすいように関数にしました。

f:id:kazuhironagai77:20210222001212p:plain

テストします。

回復薬、イーサー、ダークイーサーを2個ずつ買いSaveします。

f:id:kazuhironagai77:20210222001235p:plain

一端ゲームを終了してloadします。

道具袋を開いて見ると

f:id:kazuhironagai77:20210222001311p:plain

買ったitemはきちんとSaveされていました。

出来てますね。

3.5 WeaponsをSave/Load出来るようにする。

Itemsと同じやり方でやります。

f:id:kazuhironagai77:20210222001336p:plain

f:id:kazuhironagai77:20210222001344p:plain

f:id:kazuhironagai77:20210222001351p:plain

テストします。

武器屋は隣町にしか作っていませんでした。ので配置している武器を拾ってセーブしました。

f:id:kazuhironagai77:20210222001614p:plain

Loadして装備品を開くと所持している武器が表示されていました。

f:id:kazuhironagai77:20210222001631p:plain

出来ました。

3.6 MyYourHeroをSave/Load出来るようにする

最も大変な変数の番が来ました。

GameCharacterクラスである MyYourHero変数をSave/Load出来る様にします。

まずこの変数はObjectなので単純にSave/Loadしたらshallow copyに成ってしまうのかどうかが分かりません。

次に、BlueprintReadOnlyに指定しているので、BP側から書き込みが出来ません。

f:id:kazuhironagai77:20210222001653p:plain

この辺の問題を解決しつつMyYourHero変数をSave/Load出来る様にします。

3.6.1 MyYourHero変数とBlueprintReadOnlyについての調査

今、考えてみると、Levelが上がった時や、戦闘でダメ―ジを貰った時にMyYourHero変数の中の変数であるLevelやHPの値はBPで変更しているはずです。どうやっているのか調べて見ます。

因みにMyYourHero変数のタイプであるGameCharacterクラスを見ると

f:id:kazuhironagai77:20210222001724p:plain

となっていて、BP側から値を変更出来るのかもしれません。

調べて見たら、全部UE4C++側で操作していました。

f:id:kazuhironagai77:20210222001856p:plain

折角ここまでMyYourHero変数はUE4C++側で操作していたので、LoadもUE4C++側でやりたいですね。その方向でがんばってみます。

3.6.2 MyYourHero変数のSave

GameCharacterクラスをMySaveGameBP内に作成してSave出来るのかをテストする所から始めます。

f:id:kazuhironagai77:20210222001936p:plain

以下の方法でRPGGameInstanceのMyYourHero変数をMySaveGameBPのMyYourHero変数にセットします。

f:id:kazuhironagai77:20210222001953p:plain

Load時にMySaveGameBPのMyYourHeroオブジェクトがその変数であるPlayer Nameの値を保持しているのか以下の方法で確認します。

f:id:kazuhironagai77:20210222002023p:plain

テストします。

思いっきりエラーになりました。

f:id:kazuhironagai77:20210222002040p:plain

やっぱりSaveGameクラスは Objectの値は保持してくれないみたいですね。

もしくは、普通のC++のようにGameCharacterクラスにSet()関数をOverrideして書く必要があるのかもしれません。

それともArrayでやったように一個、一個の変数に値をSetすれば出来るのかもしれません。

簡単に出来る方法から試してみます。

3.6.3 MyYourHero Objectの変数の値を一個ずつsetする。

以下の方法で試してみます。

f:id:kazuhironagai77:20210222002114p:plain

駄目でした。

f:id:kazuhironagai77:20210222002132p:plain

3.6.4 MyYourHero Objectの全ての変数をMySaveGameBP内に作成する

このやり方なら絶対Save出来るのは分かっています。

一応試してみます。

f:id:kazuhironagai77:20210222002158p:plain

f:id:kazuhironagai77:20210222002207p:plain

f:id:kazuhironagai77:20210222002216p:plain

テストします。

f:id:kazuhironagai77:20210222002237p:plain

出来ています。

このやり方でsaveするとGameCharacterの内、以下の変数をコピーする必要があります。

ぱっと見た限りでは全部出来そうです。

f:id:kazuhironagai77:20210222002253p:plain

このやり方でやってみます。

よく考えたらPlayerNameはセーブする必要なかったです。Occupationに変更してしまいます。

MHPをsaveします。

この変数、UE4C++側はInt32をタイプに使用しているのですが、BPにはIntegerとInteger64しかありません。Integerをそのまま使用してみます。

f:id:kazuhironagai77:20210222002717p:plain

f:id:kazuhironagai77:20210222002725p:plain

f:id:kazuhironagai77:20210222002732p:plain

これでテストします。

f:id:kazuhironagai77:20210222002750p:plain

出来ていました。

残りのMMP、ATK、DEF、LUCK、XP、LV、そしてMagicsを作成します。

テストします。

f:id:kazuhironagai77:20210222002814p:plain

f:id:kazuhironagai77:20210222002821p:plain

XPとLVは0と1なので出来ています。

こんどは魔法を覚えた状態でSaveしました。

f:id:kazuhironagai77:20210222002838p:plain

魔法もsaveされています。

3.6.5 MyYourHero Objectの変数の値をLoadする。

RPGGameInstanceのMyYourHero のBlueprintReadOnlyの設定は今更変えたくないので、RPGGameInstanceにあるLoad関数を改良して値をパスしようと思ったのですが、MyYourHero変数のクラスであるGameCharacterのそれぞれの変数が、BlueprintReadWriteに設定されています。

それなのにRPGGameInstanceのMyYourHeroがBlueprintReadOnlyである意味はないと思われるので、RPGGameInstanceのMyYourHero の設定をBlueprintReadWriteに変更します。

そしてBP内でLoad時のRPGGameInstanceのMyYourHeroの値を変更します。

f:id:kazuhironagai77:20210222002912p:plain

以下の状態でsaveします。

やっている事は今までと全く同じです。

f:id:kazuhironagai77:20210222002930p:plain

f:id:kazuhironagai77:20210222002936p:plain

f:id:kazuhironagai77:20210222002945p:plain

テストします。

f:id:kazuhironagai77:20210222003009p:plain

f:id:kazuhironagai77:20210222003017p:plain

結果です。

f:id:kazuhironagai77:20210222003033p:plain

f:id:kazuhironagai77:20210222003041p:plain

Loadする時にHPとMPの値をMHPとMMPと同じ値にする必要がありました。

直します。

f:id:kazuhironagai77:20210222003059p:plain

もう一度テストします。

f:id:kazuhironagai77:20210222003117p:plain

出来ました。

正し、2つバグが出ました。

一つ目のバグはトラップに侵入すると発生するモンスターを倒してみたら出ました。

f:id:kazuhironagai77:20210222003134p:plain

二つ目のバグはLevelが上がって魔法を覚えたら同じ魔法を二つ覚えていました。

f:id:kazuhironagai77:20210222003153p:plain

これらのバグを直します。

4.神官のSave機能を実装する

この機能は既に3で作成したのでスキップします。

5.バグの直し

「3.6.5 MyYourHero Objectの変数の値をLoadする。」で発生したバグを直します。

5.1 トラップに侵入すると発生するモンスターを倒すと発生するエラー

これはもうDestroyActorが単純に要らないです。

と思ったら戦闘しないで逃げた時には必要でした。

以下の様に直しました。

f:id:kazuhironagai77:20210222003302p:plain

f:id:kazuhironagai77:20210222003302p:plain

テストします。

f:id:kazuhironagai77:20210222003319p:plain

モンスターの縄張りから出ます。

f:id:kazuhironagai77:20210222003336p:plain

一瞬でモンスターが消えました。これは素晴らしいですが、アニメーションがあった方が更に素晴らしいですね。

今度は戦闘をしてみます。

勝ちました。

元のマップに戻って来てもエラーは出ていません。正し、魔法を使用した後のモーションが素手の攻撃になっていました。前にこのバグは直したはずですが?

このバグも直します。

5.2 同じ魔法を二つ覚えるバグ

これは魔法を追加する時にAddUniqueを使用すれば直るはずです。

直しました。

f:id:kazuhironagai77:20210222003412p:plain

テストします。

f:id:kazuhironagai77:20210222003440p:plain

直っていました。

5.3 魔法を使用した後のモーションが素手の攻撃になるバグ

オカシイですね。このバグは前に直しておいたはずですが。

もう一度確認します。

直っていません。

Blogを確認したのですが、どこでこのバグを直したのか分かりません。

取りあえず以下の方法で直しました。

f:id:kazuhironagai77:20210222003514p:plain

戦闘中のアニメーションをplay animationノードで実装しているため、animationが終わった後でMy Third Person_AnimBPをセットし直す必要があります。

f:id:kazuhironagai77:20210222003542p:plain

この時にMy Third Person_AnimBPの設定が初期化されてしまうため、もう一度、My Third Person_AnimBPの変数with Weaponを設定し直す必要があります。

これがバグの原因でした。

このPlayAnimationを使用するのは、あまりよろしくないです。全てのアニメーションはMy Third Person_AnimBPで管理すべきです。これも後で直します。

5.4 ポーズ画面のセーブボタン

ポーズ画面にまだセーブボタンが残っていました。

f:id:kazuhironagai77:20210222003607p:plain

魔法でも追加しようと思いましたが、更に複雑にしても管理出来ないのでオプションにします。

f:id:kazuhironagai77:20210222003623p:plain

オプションの内容は後で考えます。

5.5 罠モンスターが消える時のアニメーション

一瞬で消えるのは変なのでアニメーションを追加しました。

f:id:kazuhironagai77:20210222003649p:plain

本当はモンスターが発生するアニメーションを逆再生したのを追加したかったのですが、PlayAnimationにその機能がないのでモンスターが倒された時のアニメーションを追加しました。

テストします。

f:id:kazuhironagai77:20210222003729p:plain

縄張りから出ると見事に倒れました。

出来てます。

6.Save/LoadBPの実装部の整理

Save/Loadの実装をBPで作成しましたが、結構ぐちゃぐちゃしています。整理します。更にRPGGameInstanceクラスのSave関数のような必要のない関数や変数を消します。

6.1 BP内のSave/Loadの実装を整理します

どちらが見やすいでしょうか?

f:id:kazuhironagai77:20210222003801p:plain

f:id:kazuhironagai77:20210222003808p:plain

見た目の綺麗さは同じ位だが、Reroute node を使用している方が同じクラスの変数に値をセットしている事が理解しやすい。

Reroute nodeを使用した形に書き直してみます。

f:id:kazuhironagai77:20210222003826p:plain

この部分だと見やすさはそんなに変わらないですが、

f:id:kazuhironagai77:20210222003841p:plain

この辺は段違いに見やすいです。

正し、Arrayの要素に値をパスする所は見にくくなっています。関数を作成して見やすくします。

f:id:kazuhironagai77:20210222003940p:plain

関数にしたら見た目はすっきりしたのですが、Errorが出てしまいました。

f:id:kazuhironagai77:20210222004012p:plain

f:id:kazuhironagai77:20210222004020p:plain

だそうです。

f:id:kazuhironagai77:20210222004037p:plain

もうこの二つのarrayをBlueprintReadOnlyにして置く理由が全くないのでBlueprintReadWriteに変更します。

f:id:kazuhironagai77:20210222004056p:plain

はい。直りました。

f:id:kazuhironagai77:20210222004317p:plain

次はMyYourHeroオブジェクトのそれぞれの変数の値をSaveGameクラスの変数にセットしている所ですが、コレも一つの関数にします。

f:id:kazuhironagai77:20210222004340p:plain

しました。

f:id:kazuhironagai77:20210222004356p:plain

大分すっきりしました。

f:id:kazuhironagai77:20210222004413p:plain

このノードらのしている事はGameInstanceの値をSaveGameに移しているだけなので一個の関数にまとめます。

実際のsaveのノードは以下の3つになりました。

f:id:kazuhironagai77:20210222004430p:plain

非常に見やすいです。コメントも付けておきます。

f:id:kazuhironagai77:20210222004447p:plain

Loadの実装も同様に整理します。

関数Load Gameに全てまとめました。

f:id:kazuhironagai77:20210222004504p:plain

Load Gameの中身は、

f:id:kazuhironagai77:20210222004520p:plain

f:id:kazuhironagai77:20210222004527p:plain

f:id:kazuhironagai77:20210222004543p:plain

となっています。SetMyYourHeroノードの中身は

f:id:kazuhironagai77:20210222004607p:plain

となっています。

綺麗且つ読みやすいような整理は出来ましたが、ひょっとすると何処かに手違いがあって正しくsave/load出来ないかもしれません。確認します。

f:id:kazuhironagai77:20210222004626p:plain

f:id:kazuhironagai77:20210222004633p:plain

f:id:kazuhironagai77:20210222004641p:plain

この状態でSaveします。ここには表示されませんが、魔法は炎(小)、炎(大)が使用出来ます。

Loadします。

f:id:kazuhironagai77:20210222004717p:plain

HP102、MP22、経験値100、レベル3とセーブした時と同じになっています。所持している金貨は70枚から65枚に減っていますが、セーブした時に神官に金貨5枚払っているのでこれで正しいです。

f:id:kazuhironagai77:20210222004734p:plain

AP52、DP32、LK2全て合っています。所持している道具は、回復薬、イーサー、ダークイーサーがそれぞれ一つずつでこれも合っています。

f:id:kazuhironagai77:20210222004807p:plain

装備されている武器、盾は短剣(小)と木の盾(小)で合っています。

戦闘をして使用出来る魔法の確認をします。

f:id:kazuhironagai77:20210222004826p:plain

はい。出来ています。

6.2 RPGGameInstanceクラスのSave/Load関数を消去します

以下の関数を消します。

f:id:kazuhironagai77:20210222004857p:plain

f:id:kazuhironagai77:20210222004906p:plain

ただ完全に消去してしまうと、後でUE4C++側でsave/loadしたい時にどうやるのか分からなくなってしまうので、Comment outするだけにします。

f:id:kazuhironagai77:20210222004923p:plain

f:id:kazuhironagai77:20210222004930p:plain

Buildします。

出来ました。

f:id:kazuhironagai77:20210222004946p:plain

これらの関数を使用してる箇所はないはずなのでこれで大丈夫なはずです。

7.ObjectSaveGameクラスに保持させられないのかの検証

まず全てBPで実験してみます。

新たにProjectを作成するのも勿体ないので、先々週、AIの勉強に使用したProjectで検証します。

以下の方法で試します。

まずActorクラスからTestActorを作成し変数HPを追加します。

f:id:kazuhironagai77:20210222005013p:plain

f:id:kazuhironagai77:20210222005019p:plain

このTestActorのinstanceを一個Level上に配置します。

f:id:kazuhironagai77:20210222005049p:plain

配置したTestActorのHPの値がI をクリックするたびに1だけ減るようにセットします。

f:id:kazuhironagai77:20210222005105p:plain

Save/LoadそしてHPの値の表示様にWidgetを一個作成します。

f:id:kazuhironagai77:20210222005136p:plain

配置しているTestActorをParameterとしてパスする設定にします。

f:id:kazuhironagai77:20210222005202p:plain

HP、Saveボタン、Loadボタンを作成します。

f:id:kazuhironagai77:20210222005223p:plain

HPの値、Saveボタン、Loadボタンの機能を実装します。

f:id:kazuhironagai77:20210222005244p:plain

f:id:kazuhironagai77:20210222005252p:plain

f:id:kazuhironagai77:20210222005259p:plain

ここでSave/LoadするのはTestActorのInstanceそのものです。

このやり方でHPの値がSave/Load出来るのなら、少なくともBP内で完結すればObjectのSave/Loadは出来ると言う事になります。

Widgetを表示させます。

f:id:kazuhironagai77:20210222005323p:plain

GameModeBaseBPクラスからWidgetを作成しようと思ったのですが、これだけのために新しいGameModeBaseBPクラスを作成するのも面倒なのでLevelBP内に作成しました。

これでテストします。

配置しているTestActorのHPを100から91に下げました。

f:id:kazuhironagai77:20210222005346p:plain

Saveします。

f:id:kazuhironagai77:20210222005405p:plain

SaveしたTestActorのHPの値が91である事は画面左上にプリントされた値からも確認出来ます。

一端ゲームを終了してLoadします。

f:id:kazuhironagai77:20210222005421p:plain

Loadしましたが、前のHPの値はSaveされていませんでした。

出来ませんでした。

つまりSave/Load出来るのはその変数が保持している値のみのようです。その変数が保持している値が、アドレスの場合は、そのアドレスの値をセーブすると思われます。

となるとUE4C++でcopy constructorをoverrideして作成すればObjectでもSave/Load出来るのでしょうか?

そんな気はしますが、それを今試す必要はあまり感じないので、今回のSaveGameの検証はここまでで終了にします。

今回の検証だけではSaveGameクラスでObjectはsave/load出来ないと断言は出来ませんが、SaveGameクラスを使用する時、その変数の値が正しくSave/Loadされているかの確認は必ずすべきである。位は言えると思います。

8.まとめと感想

今週はこれで終わりです。一応Save機能も完成しました。来週は戦闘中に使用しているPlayAnimationの箇所でも直そうと思います。

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する Saveの改良

f:id:kazuhironagai77:20210214215522p:plain

<前文>

Genocide そして進撃の巨人 Final Season

日本で有名なYouTuberの方が海外に移住するらしいですが、逆に英語圏で有名なYouTuberが日本に移住したりもしていて「Internetで世界は一つの村に成る。」との予言は正しかったんだなと思いました。

それはともかく、日本に移住した英語圏の人達の最初の難関は、市役所への転入の書類の提出だそうです。これに保険証の申請やら何やらで、全部自分でしなければならない場合は、ほとんど不可能らしいです。そしてこれは日本人の間でも有名ですが、外国人が日本でアパートを借りるのは相当大変みたいですね。アメリカ人の間では、一軒家を買ってしまう方が遥かに簡単と、一軒家購入の進めみたいなのまであります。

そんな大変な思いをして日本に移住したのに、2年も経つと自分の期待していた日本と現実の日本の違いに絶望するらしいです。そんなに日本の暮らしって悪いのかと話しを聞くと「2年も日本に住んでいたのに日本人の友達、一人も出来なかった。」とか「日本で暮らした時の最高の思い出は、スタミナ太郎の食い放題。」とか、それは日本人でも絶望するでしょう。みたいな悲惨な経験を語って来ます。

でもこれ日本人が海外に行っても同じ事です。特に、親戚もいない、その国の言語もしゃべれない。ましてや風習や宗教も知らない国に行ったら、相当悲惨な目に合うのは間違いないでしょう。

それでも私は、日本人は海外に一時でも暮らしてみるべきだと非常に強く思っています。

その理由は、幾つかありますが、そのうちの一つは世界の常識と日本の常識があまりに乖離している事が実感出来るからです。

その例の一つがGenocide です。

世界にはGenocideが普通に存在しています。Genocideは日本語に訳すと虐殺で、英語のMassacreと同じ意味に成ってしまいます。しかし、この二つの単語の意味は、全く違います。Massacreは沢山の人を殺す事です。それは酷い事ですが、Genocideに比べるとその悪意は、たかが知れるレベルで、人類史の前では取るに足らない軽犯罪です。

しかしGenocideは全く違います。Genocideとは、ある民族全体を絶滅させる事です。海外に出ると直ぐに分かるのですが、日本民族を絶滅させたいと思っている人々は沢山います。とてつもない悪意をもって日本民族を絶滅させたいと思っている人達がですよ。実際にいるんです。それも一つの思想として根付いているんです。こういう人達に実際に出会うだけでも、海外に住む価値があると思うんです。

アメリカに長く暮らしていた私は、一部のアメリカ人が日本民族に対してGenocideをほのめかす時、冗談で言ってない事に直ぐにピンと来ます。

彼らにとって日本民族が消滅してほしい理由は大きく二つあります。

勿論、全部のアメリカ人がそう思っている訳ではないですが、概念としてのGenocideは普遍的に存在していています。何かあると必ずその邪魔な民族、全滅出来ないかと考えるのは日本人以外にとっては、普通の事なんです。

最近、Capitol Riotを含む国内テロリズムの原因として、〇ox News のAnchorを含む保守派のコメンテーターらが超高額の訴訟を起こされています。その事自体はザマァーですが、その起訴の内容は、昨年の大統領選挙の投票結果に対して嘘を広めた事です。

所が、彼らが嘘をついていたのは昔からなのです。みんな彼らが嘘を言っているのは知っていたんです。その彼らの嘘を元に、メキシコとの国境に壁を立てたり、中国からの輸入品にとてつもない関税を付けたり、日本にアメリカ国内では使用が禁止されている発癌性の農薬を使用した果物を輸出したりした時は、今、彼らに対して激怒しているアメリカ人も一緒になってゲラゲラ笑っていたんですよ。

日本を含めて、これらの国はその生存をアメリカとの貿易に依存しています。それらの国に対して嘘を元に貿易戦争を仕掛けるのは、その国の民族に対して、暗にGenocideを実行しているのと同じ事です。

しかし他民族がGenocideされる分には、ほとんどのアメリカ人にとっては笑い事で済んでしまう事なんです。今、アメリカ人が彼らに対して激怒しているのは、自分たちが彼らの嘘のせいでコロナによって死ぬ可能性が出て来たからです。

こういう事を肌で感じれる様になる経験をする事は全ての日本人にとって大切だと思います。

そして進撃の巨人Final seasonです。

進撃の巨人Final seasonはまさしくGenocideがテーマの作品なんです。だからGenocideの思想がない日本ではそんなにヒットしていないですが、それが普遍的に存在している海外では、あんなに受けているです。

今週は、これについて語ろうと思ったのですが前文が既に長くなりすぎてしまったので、残りは来週にします。

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

<本文>

1.今週の予定

やっとですが、今週からSave機能をもう一度作り直します。予定としては以下の順番でやろうと思っています。

  1. Blogなどで今まで作成したSave機能の復習をする。
  2. Saveしなければならないデータの確認。
  3. データ以外のSaveに必要な要素の確認 (ゲーム内)
    • セーブ出来る場所の決定
    • Loadした後に倒したモンスターは復活するのか?
  4. データ以外のSaveに必要な要素の確認 Part 2 (ゲーム外)
    • セーブデータの保存場所
    • セーブデータの暗号化、
    • Serverに保存する場合の方法について
  5. Save機能のTutorialを復習する。
  6. 実際の作成

2.今まで作成したSave機能の復習

以下のブログでSaveについて書いてありました。

それ以外のブログはSave機能について勉強するといってやってませんでした。Save機能を作成する前に、Saveするデータが揃う必要があったので、実際はあんまり勉強しなかったんです。思い出しました。

2.1 2019-09-22のブログの復習

本文と関係ないですけど、前文でこの統計学の本読んだら、今までよく意味が分からなかった統計学の意味が分かった。と書いていますが、今考えるとこの本読んでも、統計学の本質は理解していませんでした。

今は理解しています。

統計学は確率の裏返しです。

これだけ分かれば後は計算方法だけ理解すれば良いんです。

UE4のSave機能の作成部分ですが、教科書(「Unreal Engine 4.xを使用してRPGを作成する」)の10章を勉強していて、その中でSave機能の作成方法を勉強したようです。

具体的には、

Save機能の作成方法

  1. SaveGameクラスから派生したクラスを作成。
  2. そのクラスにsaveしたいデータが保持出来る変数を作成。
  3. Saveボタンを押したらそのクラスのオブジェクトを作成してその変数に値を追加。
  4. SaveGameToSlotノードを使用してそのオブジェクトをSlotに保存。

を行っています。更に

Load機能の作成方法

  1. DoesSaveGameExitノードでSlot内にSaveしたGameDataがあるのか調べる。
  2. 在る場合はそのデータを、LoadFromSlotノードでゲーム内にロード。
  3. ロードしたデータをGameInstanceなどの変数にセット。

を行っていました。

やり方自体に特にコメントはありません。正攻法だと思います。

一個だけちょっとおかしいかもと思う箇所が以下の部分ですが、

f:id:kazuhironagai77:20210214215756p:plain

Slotのobjectを保持する必要はあんまりない気がします。もし保持したいならその後のNewSameGameにCastしたobjectを保持した方が良い気がします。

2.2 2019-10-20のブログの復習

この回のブログでは、2019-09-22でSaveの作成方法を初めて勉強したので、その時に学習したSaveGameBPノードから一般的なUE4のSaveの作成方法を調査しています。

更に

  • Loadのやり方が分からない。
  • 複数のSaveDataの保存方法が分からない。

と書かれていて、その解答を見つけるためにSaveGameBPノードの調査をすると書かれていました。

Loadのやり方は、2019-09-22で既に解説されていましたが、この時は良く分かっていなかったのでしょうね。

複数のSaveDataの作成は単にSaveGameBPのobjectを沢山作成してそれぞれSlotにSaveすれば良いと思います。ただ今の私の考えでは複数のSave Dataを作成出来るとゲームが面白く無くなるので、Saveは一回しか出来ない様にするつもりです。

以下の二つのサイトで勉強していました。

これらのサイトは今週、もう一度勉強し直します。

2.3 2020-07-26のブログの復習

ここでは、ほぼ現状のSave機能の作成内容について説明しています。流石に半年前のブログだと今読んでもかなり勉強になる内容が書かれていて読んでて唸ってしまいました。

大切だと思う事だけここに抜き出します。

  • SaveGameクラスにGameInstanceの変数を作成してGameInstanceそのものをSlotに保存しようとしたら、load出来なかった。Shallow Copyになっているみたい。
  • GameInstanceクラスにある変数を一つ一つSaveGameクラスの変数として作成して保存しようとしたら、GameInstanceクラスにある変数は全て、BlueprintReadOnlyで指定されていてLoad時に値を代入する事が出来なかった。
  • UEC++からSaveGameクラスの派生クラスを作成してそこから、GameInstanceクラスにある変数の値をSaveGameクラスの変数に保存したり、SaveGameクラスの変数の値をGameInstanceクラスにある変数に代入したり出来るようにした。

SaveGameに作成する変数の値が、Shallow copyに成っていないかの確認が必要である事。そしてGameInstanceクラスにある変数は全てBlueprintReadOnlyで指定したためにUE4C++側にSave, Load機能を作成する必要がある事。この二つは今回、新たにSave機能を作成する時にも気を付けなければならない要素です。

一つだけ問題だと思うのがSaveGameクラスの変数の値をGameInstanceクラスにある変数に代入するための関数ですが、

f:id:kazuhironagai77:20210214215838p:plain

このやり方だと、mySlotと言う名前のSlotが存在しない時はErrorになる気がします。実際のコードを改良する時、この個所はしっかり考えて直します。

2.4 ブログの復習のまとめ

大体、今までやった内容は理解出来ました。GameInstanceクラスにある変数を全てBlueprintReadOnlyで指定したためにUE4C++側でSave、Loadの処理をしなければならない以外は正攻法でSave、Load機能を作成していると思います。

Slotに保持出来る変数がShallow copyになっていないかの確認が必要な事はすっかり忘れていました。

昔書いたブログを読み直すのは恥ずかしいですが、こうやって記録を付けておくと後で簡単に復習出来るので、大変便利です。ブログを書く事の大切さをもう一回確認出来ました。

3. Saveしなければならないデータの確認

RPGGameInstanceクラス内の全ての変数をSaveすれば良いはずです。もしかしたら漏れがあるかもしれませんが、取りあえずそれからやって見ます。

3.1 RPGGameInstanceBP内の変数

BP内だけで以下の変数が作成されていました。どの変数を何のために使用したのか全く覚えていません。

f:id:kazuhironagai77:20210214215922p:plain

一個ずつ確認していきます。

<Map>

クラス:Map(調べたら自作したEnumクラスでした。)

f:id:kazuhironagai77:20210214220012p:plain

ワープして別のMapに移動するために使用されるはずです。

Loadする時にどのMapから始めるのかを記録しておく必要があるので保存する必要がある変数と思われます。

<Zone>

クラス:Zone(Enumクラス)

f:id:kazuhironagai77:20210214220047p:plain

ワープした時にどの地点に出現するかを指定する関数です。この場所を敢えてSaveする必要なないと思われます。

<warped

クラス:Bool

ワープした時に使用される関数であるのは確かですが、何のために使用するのか全く覚えていません。調べます。

検索したらウィジェットであるw_map, とLevelであるmap1, map2, map3で使用されていました。

f:id:kazuhironagai77:20210214220116p:plain

この感じからするとLevelであるmap1, map2, map3は全く同じ使用をされていると思われます。

まずウィジェットであるw_mapを見てみます。

まずデザインですが、世界地図の上にボタンが散りばめられています。

f:id:kazuhironagai77:20210214220138p:plain

それらのボタンを押すと以下に示した様に関数、ChangeMapが呼ばれます。

f:id:kazuhironagai77:20210214220155p:plain

ChangeMap関数内の実装ですが、その中で、warpedがTrueにセットされました。

f:id:kazuhironagai77:20210214220213p:plain

このノードにChangeMap関数があります。その関数は呼ばれると指定されたMapを新たに開きます。

ので、Map1が開かれたと仮定してMap1のLevel BPを見てみます。

Event Begin Play関数の一番最後で、WarpedをFalseにセットしているだけでした。

f:id:kazuhironagai77:20210214220232p:plain

この変数は、全くSaveする必要はないですね。そればかりがワープ機能にも要らない可能性もある気がしています。後でこの変数が本当に必要か調べます。

<DroppedItem

クラス:Bool

何に使用している関数なのか全く覚えていません。調べます。

検索したら沢山の似た名前の変数が引っかかってしまいました。

検索の最初に出て来たDroppedItemBaseクラスから調べて見ます。

f:id:kazuhironagai77:20210214220254p:plain

DroppedItemBaseクラスを以下に示します。

f:id:kazuhironagai77:20210214220311p:plain

分かりました。マップに配置されているアイテムを表示するためのActorクラスです。

Box内にplayerが操作するキャラが侵入するとTrue、出るとFalseにセットしています。

f:id:kazuhironagai77:20210214220327p:plain

これだけでは何のための変数か分かりませんね。もう少し調べます。

PickUpItemクラスでも使用されています。

f:id:kazuhironagai77:20210214220703p:plain

PickUpItemクラスの方も調べて見ます。

まず、PickUpItemクラスはウィジェットでした。

f:id:kazuhironagai77:20210214220726p:plain

これの拾うボタンと戻るボタンを押した時に呼ばれていますが、マジで意味不明です。

f:id:kazuhironagai77:20210214220744p:plain

これ以外には使用されていませんでした。

ひっとすると昔は何かに使用していたのですが、Itemを拾う機能を改善している内に要らなくなった変数かもしれません。

後でこの変数が本当に必要か調べ要らないようなら削除します。

勿論、Saveする必要はありません。

<Dropped Item Content

クラス:Dropped Item Base

以下のクラスで使用されています。

f:id:kazuhironagai77:20210214220811p:plain

ちょっと不思議な点があります。

まずDroppedItemBaseクラスがDroppedItemBaseクラス内で使用されています。更にSetばかりでGetに使用しているのがThirdPersonCharacterBPのみです。

DroppedItemBaseクラスから見てみます。

Box内にPlayerのキャラが侵入したら自身をセットしています。

f:id:kazuhironagai77:20210214220849p:plain

Box内にPlayerのキャラが去ったら自身のセットを解除しています。

f:id:kazuhironagai77:20210214220910p:plain

何故か全く同じ名前の変数がGameModeBaseBPにもあってそれにも同様の事をしています。

f:id:kazuhironagai77:20210214220933p:plain

次はThirdPersonCharacterBPを見てみます。

f:id:kazuhironagai77:20210214221000p:plain

これはDroppedItemBaseのボックス内にPlayerのキャラが侵入したらPickUpItemウィジェットを作成して表示させるための実装ですね。そのためにDropped Item Contentをパスしています。

分かりました。これはどのアイテムがそのボックス内に存在しているかをPickUpItemウィジェットに伝えるために使用される変数です。

確認のためにPickUpItemウィジェットも見てみます。

まず、Dropped Item ContentはMy Dropped Item と言う変数名でPickUpItemウィジェットにセットされていますので、My Dropped Itemを調べます。

アイテムを「拾う」を選択した場合です。

f:id:kazuhironagai77:20210214221029p:plain

黄色で囲った部分がMy Dropped Itemが使用されている箇所です。緑は間接的に使用されている箇所です。

一個ずつ見て行きます。

My Dropped ItemがWeaponかどうかを聞いています。

f:id:kazuhironagai77:20210214221054p:plain

Playerの保有するWeaponとItemは別なArrayで管理しているので、拾ったItemが本当のItemなのか武器なのかの確認が必要です。それをここで行っています。

しかしItemであった場合に実行すべきコードはまだ実装されていませんでした。後で作成します。

次にMy Dropped Itemの名前が本当に武器名なのか確認しています。

f:id:kazuhironagai77:20210214221113p:plain

無くてもいいかもしれませんが、念のために確認しても良い気もします。

その名前をRPGGameInstanceBPのWeapon変数に追加します。

f:id:kazuhironagai77:20210214221128p:plain

覚えていませんが、このWeapon変数はplayerの操作するキャラが保持する武器を管理する変数のようです。

確認のためにRPGGameInstanceBPの変数を見ましたが載っていません。UE4C++のRPGGameInstanceクラスで作成した様です。

f:id:kazuhironagai77:20210214221145p:plain

ありました。

f:id:kazuhironagai77:20210214221201p:plain

思い出して来ました。

この変数、BlueprintReadOnlyなんですが、Arrayだからか、Add関数を使用するとBP側からでも要素を追加出来たんで、BP内のみで新しい値を追加したんです。

このやり方が正しいのかは分かっていません。後でバグが出るかもしれないのでこのやり方でも正しいのかについても後で調べる事にします。

f:id:kazuhironagai77:20210214221229p:plain

次は以下の関数が呼ばれています。

f:id:kazuhironagai77:20210214221245p:plain

このPickUpItemウィジェット内で自作した関数でした。

簡単に説明するとGameInstance内の変数、ItemSpawnDataはそのレベル上で、何処にどんなItemをSpawnさせるのかを管理しています。

そのItemSpawnDataにこのItemはPlayerがもう獲得したのでこれからはSpawnさせないで下さいとお願いしています。

f:id:kazuhironagai77:20210214221309p:plain

このやり方よりマシな方法は絶対あると思いますが、取りあえずは望んだとおりに動くので、今回はこのままにしておきます。

後にもっとUE4のArrayの関数に詳しくなった時に復習します。

f:id:kazuhironagai77:20210214221344p:plain

My Dropped ItemをDestroy Actor関数で破壊しています。

これは今Level上に表示されているItemがこれ以上表示されないためにしたのでしょうか?多分そうですね。

最後に、My Dropped Itemではありませんが、My Dropped Item にパスされているDropped Item Content(ただしGame Instanceに保持されている方)が開放されています。

f:id:kazuhironagai77:20210214221436p:plain

正直、このやり方で開放するのが正しいのかも分かっていないですが、RPGGameInstanceのDropped Item Contentが何をしているのかは理解出来ました。

まず、この変数はSaveする必要はありません。この変数はItemを拾う時のみに必要になる変数でItemを拾う途中でSaveする事は出来ないからです。

更にGameInstanceにある必要は全くないです。RPGGameModeBaseBPにある全く同じ変数が全部対応していますのでGameInstanceのDropped Item Contentは消して良いです。

GameInstanceのDropped Item Contentは後で、もう一度、要らない事を確認したら消去します。

<Time Spend

この変数は最近作成したので覚えています。一日の内どれくらいの時間が経過したのかを記録する変数です。

一応確認だけします。

f:id:kazuhironagai77:20210214221502p:plain

Set Time Spend 0はEvent Despatcher で、Level BPと他のBPのデータのやり取りをする為に作成したものです。

Inn_Welcomeウィジェットは宿屋の管理をするウィジェットですので泊まった時は必ず朝になる必要があります。

f:id:kazuhironagai77:20210214221528p:plain

この変数をsaveする必要はありません。なぜならLoadから始める場合は、いつでも朝から始まってもオカシクないからです。

<Third Person Character Location

この辺の変数は最近作成したのでまだ覚えています。

この変数は、戦闘で別なマップに移動したプレイヤーの操作するキャラを戦闘後に元の位置に戻すための変数です。

Saveする位置がまだ決まっていませんが多分この変数をsaveする必要ないでしょう。

<Monster Spawn Data

この変数はそのレベル上で倒されたモンスターのデータも管理しています。

Loadで戻った時、倒したモンスターは再生されるのか、どうかを決める必要があります。

再生されないのならSaveする必要があります。

<Monster Name In Fighting

戦闘中に表示されるモンスターの種類を決定するための変数です。

Saveする必要はありません。

<Third Person Character Rotation

この変数も、戦闘で別なマップに移動したプレイヤーの操作するキャラを戦闘後に元の位置に戻すための変数です。

この変数はキャラの向きを保存します。

思い出しました。この変数、Meshの向きだけ変えてカメラの位置はそのまま。というバグが残っていました。

Saveする必要は多分ないでしょうね。

<Item Spawn Data

Level上に配置するItemのデータを保持しています。Playerが既に収得したアイテムについてもこの変数が管理しているので、絶対にsaveする必要があります。

RPG Game Instance BP内の変数まとめ>

Saveする必要のある変数は、

  • Map
  • Monster Spawn Data(仮)
  • Item Spawn Data

の3つだけでした。

3.2 RPGGameInstance内の変数

以下の変数があります。

f:id:kazuhironagai77:20210214221652p:plain

Talk Shop、Talk Weapon Shop、そしてTalk Inn以外の変数は全部Saveする必要があります。

Game CharacterクラスであるMyYourHeroやArrayであるItemsやWeaponsはそのままSaveしてもShallow copyになってしまうんでしょうか?その辺の確認が必要ですね。

4.データ以外のSaveに必要な要素の確認 (ゲーム内)

4 .1 セーブ出来る場所の決定

セーブ出来る場所を作成します。

NPC、神官を作成して神官のいる場所でSave出来るようにします。

NPCの作成方法を忘れてしまったので復習します。

4.1.1 NPCの作成方法

2020-10-11のブログでNPCを作成していました。これを読むとこれ以前に、NPC_Oldmanの原型を作成していますね。2020-08-23のブログで最初のNPCを作成しています。こっちから読んでいきます。

2020-08-23のブログを読むとまず、

f:id:kazuhironagai77:20210214221744p:plain

を作成しています。

今のProjectでは、

f:id:kazuhironagai77:20210214221802p:plain

と名前が違っていますが、多分これですね。

開いて見ると、Box内にplayerが侵入した時、RPGGameModeBaseBPのMyPlaceForEvents変数にNPC_Personの変数Occupationをセットしています。

f:id:kazuhironagai77:20210214221822p:plain

MyPlaceForEvents変数はRPGGameModeBaseクラスの変数で、EPlaceForEventsから作成されていました。

f:id:kazuhironagai77:20210214221852p:plain

これがEPlaceForEvents です。

f:id:kazuhironagai77:20210214221910p:plain

ここに神官を追加する必要がありますね。

この後で、ThirdPersonCharacterBPのEvent Do Somethingに行くのは覚えているのですが、どうやってここに飛ぶのかが分かりません。

f:id:kazuhironagai77:20210214221934p:plain

分かりました。Eボタンを押すんでした。

f:id:kazuhironagai77:20210214222004p:plain

f:id:kazuhironagai77:20210214222011p:plain

これは何処かに解説しておかないとUserには分からないですね。

NPCとの会話はEを押す。」を後で追加します。

Event Do SomethingではEPlaceForEvnetsのそれぞれの要素によって分岐しています。

ここに神官の場合を作成する必要があります。

f:id:kazuhironagai77:20210214222030p:plain

分岐の先はほとんど同じで作成するwidgetなどが違うだけです。

f:id:kazuhironagai77:20210214222100p:plain

神官用のWidgetの作成も必要ですね。

老人と魔法研究家のWidgetを以下に示します。

f:id:kazuhironagai77:20210214222118p:plain

f:id:kazuhironagai77:20210214222153p:plain

思い出しました。

単に別のNPCをコピーしてちょっとだけ変更して新しいNPCウィジェットを作成したんです。

後はここに表示する会話のシステムを理解すれば良いだけですね。

ここまでNPCの作成方法を復習してアレ何ですけど、神官は普通のNPCから作成するんじゃなくて武器屋、道具屋、宿屋と同じ方法で作成すべきな気がして来ました。

ここまでやっちゃったので普通のNPCの作成方法、最後まで勉強します。

会話のシステムですが、DataTableから読み込んでいます。

f:id:kazuhironagai77:20210214222215p:plain

以下に示した様なDataTableが作成されています。

f:id:kazuhironagai77:20210214222237p:plain

開いて見るとこんな感じです。

f:id:kazuhironagai77:20210214222255p:plain

思い出しました。

このEnumクラスから作成したDataTableらです。

f:id:kazuhironagai77:20210214222310p:plain

NPCのコメントをConversationに書き込み、その解答をAnswer Choiceに書き込みます。答えによって次のNPCの会話が決まるので、どのコメントを読むのか番号(Jump To Comment)で指定します。

最後に、選択するために表示されるNPCに対する返事が書かれたボタンがNPCの分だけ別なクラスで作成しないとエラーになる事を思い出しました。

f:id:kazuhironagai77:20210214222327p:plain

f:id:kazuhironagai77:20210214222336p:plain

メンドクサイですが現状直し方が分からないです。

これでほとんど完全にNPCの作成方法を思い出しました。念のためにブログをもう一回読み直します。

読み直したら、2020-10-18のブログでNPCの作成方法について逐一記録してありました。これに沿って作成すれば何も復習する必要なかったです。

4.1.2 武器屋、道具屋、宿屋の作成方法

ここまでNPCの作成方法について復習してあれですが、神官は武器屋、道具屋、宿屋と同じ方法で作成した方が良い気がしています。

こちらの作成方法についても調べます。

2020-10-04のブログに宿屋の主人の作成方法についての細かい手順が全て書かれていました。これに沿って作成すれば簡単に作れそうです。

関係ないですが、Vtuberについて前文で触れていました。

ある時からRedditでこのVtuberをアンチから擁護しようと主張するコメントは即ブロックされるので不思議に思っていたら、そのSub Reddit、そのVTuberが属している会社が運営していました。もうVtuberを見るのは止めてしまいましたが、この会社の名前を見ると反吐が出ます。表ではこのVtuberを守ると宣言してファンの信頼を獲得して、裏ではアンチと組んでこのVtuberをイジメているなんで卑劣過ぎます。

4.1.3 神官の作成

色々考えましたが、神官は宿屋の主人と同じ方法で作成する事にしました。

NPC_ItemShopOwnerをduplicateしてNPC_Priestと名付けました。

f:id:kazuhironagai77:20210214222413p:plain

NPC_Priest を開いたらRPGGameInstanceクラスのboolean変数であるTalkInnをTrueにセットしています。

f:id:kazuhironagai77:20210214222437p:plain

このTalkInnに変わる変数をRPGGameInstanceクラスに作成する必要があります。作ります。

この関数が何故必要なのか今一分からないのですが、今回は宿屋と全く同じ作成方法で神官を作る事にします。これらのNPCの作成方法の最適化は後で考えます。

作りました。名前はTalkPriestにしました。

f:id:kazuhironagai77:20210214222504p:plain

NPC_PriestのTalkInnをTalkPriestに変更します。

f:id:kazuhironagai77:20210214222531p:plain

f:id:kazuhironagai77:20210214222541p:plain

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

f:id:kazuhironagai77:20210214222601p:plain

Buildします。

UE4 editorを一回閉じて開きます。

My Place for EventsにPEPriestを選択します。

f:id:kazuhironagai77:20210214222617p:plain

ThirdPersonCharacterBP内にPE_Priestの選択先を実装します。

f:id:kazuhironagai77:20210214222633p:plain

実装内容はPE_Innと全く同じです。

f:id:kazuhironagai77:20210214222648p:plain

Priest用のWelcomeウィジェットが無いので作成します。

Inn_Welcomeを元にPriest_Welcomeを作成します。

f:id:kazuhironagai77:20210214222705p:plain

以下の様にデザインしました。

f:id:kazuhironagai77:20210214222719p:plain

Inn_Welcomeの場合会話ボタンを押すとInn_Talkウィジェットが開かれます。

f:id:kazuhironagai77:20210214222746p:plain

同様のwidgetをPriest用に作成します。Priest_Talkと名付けます。

f:id:kazuhironagai77:20210214222813p:plain

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

f:id:kazuhironagai77:20210214222831p:plain

セリフの内容はInnとほとんど同じです。後で、神官にふさわしいコメントに改善します。一応意味は通っているはずです。

f:id:kazuhironagai77:20210214222848p:plain

これらのセリフがPriest_WelcomeとPriest_Talkのコメント欄に表示されるように設定を変更します。

Priest_Welcomeのコメント欄に表示するテキストは以下の様な設定になっています。

f:id:kazuhironagai77:20210214222906p:plain

参照するテキストはNPCPriestDialogに変更しました。

Dialog Numberは

f:id:kazuhironagai77:20210214222937p:plain

にセットされています。

更にセーブボタンを押した時はDialog Numberが2または3にセットされています。

f:id:kazuhironagai77:20210214222958p:plain

ThirdPersonCharacterBPのCreate widgetの設定をPriest Welcomeに変更します。

f:id:kazuhironagai77:20210214223015p:plain

会話ボタンを押した時に開く、WidgetをPriest_Talkに変えます。

f:id:kazuhironagai77:20210214223032p:plain

Priest_Talkのコメント表示の参照にするテキストをNPCPriestDialogに変更します。

f:id:kazuhironagai77:20210214223049p:plain

これで一回テストしてみます。

f:id:kazuhironagai77:20210214223108p:plain

普通に表示されています。

f:id:kazuhironagai77:20210214223123p:plain

「出る」を押したらカーソルが消えてしまいました。バグですね。

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

f:id:kazuhironagai77:20210214223138p:plain

今度は「出る」を押してもカーソルが表示されました。

Innでは泊まるを選択した場合、Sleepingというwidgetが作成されます。

f:id:kazuhironagai77:20210214223159p:plain

こんなヤツです。

f:id:kazuhironagai77:20210214223227p:plain

Saveでも似たようなWidgetを作成します。

f:id:kazuhironagai77:20210214223252p:plain

このwidgetを呼ぶ事にします。

f:id:kazuhironagai77:20210214223307p:plain

Saveをテストします。

セーブボタンを押します。

Prayウィジェットのアニメーションが流れます。

f:id:kazuhironagai77:20210214223323p:plain

f:id:kazuhironagai77:20210214223331p:plain

f:id:kazuhironagai77:20210214223338p:plain

アニメーションが終わったら元の画面に戻りました。

コメントも正しく表示されています。

f:id:kazuhironagai77:20210214223355p:plain

これで神官NPCは完成とします。

4.2 Loadした後に倒したモンスターは復活するのか?

ストーリーに関係するボスキャラが復活したらおかしいですが、雑魚のモンスターは朝になると復活したりしてもおかしくはないです。

今、配置しているモンスターらは雑魚モンスターと仮定すれば、復活しても良くなりMonster Spawn Dataをセーブする必要は無くなります。

4.3 Saveデータの数

前は、少なくとも3つ位のセーブデータが保持出来るような機能は必要と思っていました。

しかしゲームの面白さはバランスだと言う事に気がついた後では、沢山セーブ出来る事もバランスが変化する事が分かりました。のでsave出来るデータは一個だけとします。

5.データ以外のSaveに必要な要素の確認 Part 2(ゲーム外)

ここは調査の記録になります。良く分かっていない事ばかりなので間違えているかもしれませんが最初の一歩と言う事です。

今、データのセーブにおいて私が知りたいのはUE4では、セーブしたデータをどんな形でどこに保存しているのかです。

その理由は、保存しているデータを直接書き変える事で、キャラのパラメーターや所持するアイテムや武器、使用出来る魔法などを好き勝手に書き変えられてしまうかもしれないと私が恐れているからです。

今回確認したいのはその部分です。

後、それらのデータをサーバーに保存する方法を教えているTutorialがあるかも調べます。

5.1 セーブデータの保存場所

このサイトによると

f:id:kazuhironagai77:20210214223440p:plain

セーブデータとセーブされる場所に関しては以下の様に説明されています。

f:id:kazuhironagai77:20210214223457p:plain

Ch4_3で調べて見ると、

f:id:kazuhironagai77:20210214223514p:plain

ありました。

NodePad++で開いて見ると、ほとんどは意味が分からない滅茶苦茶なalphabetの羅列ですが、幾つかは

f:id:kazuhironagai77:20210214223530p:plain

f:id:kazuhironagai77:20210214223539p:plain

f:id:kazuhironagai77:20210214223557p:plain

f:id:kazuhironagai77:20210214223613p:plain

f:id:kazuhironagai77:20210214223626p:plain

意味が分かる単語があります。

.sav fileを読み込めるeditorを使用するか、もしくは技術のある人が作成した解析ツールを使用すれば、ここに書かれているデータを変更する事は簡単に出来そうです。

一般に販売されているゲームはPackageした後のSave dataの管理はどうやっているんでしょうか?携帯のゲームのガチャなどでレアアイテムが出た事に変えられたら結構大問題だと思います。

SlotにSaved dataの保存を任せるのではなくServer側に保存させてしまえば良いのかもしれませんね。

以下に示した図は単なる想像なのですが、

以下の方法でガチャを行うと、

f:id:kazuhironagai77:20210214223645p:plain

途中で結果を変える事が出来る可能性がありますが、

f:id:kazuhironagai77:20210214223700p:plain

以下のやり方ならば、途中でデータを改ざんしても結果を変更する事は出来ません。

f:id:kazuhironagai77:20210214223717p:plain

これだったらServerに直接攻撃されない限りは結果を改ざんされる事はなさそうです。

どうしても結果の改ざんを防ぎたいならblock chainのやり方を真似るのも手かもしれません。block chainがどうやっているのか具体的には知りませんが。

5.2 セーブデータの暗号化

セーブデータをSlotとしてClientのPCもしくはスマートホン内に保存する場合は、暗号化は絶対必要と思っていたのですが、前節の調査と考察を踏まえて、もう一度考えてみるとそうでもない気がしてきました。

  • まず買い取り型の一人で遊ぶゲームの場合ですが、Cheatしても誰にも迷惑はかけないので、ご自由にどうぞ。とも考えられます。
  • みんなで遊ぶゲームでCheatされるのが問題だったんです。そのタイプのゲームは前節のやり方でやれば、Clientが改ざん出来るデータは全くないのでCheatは防げます。

つまり、Slotにあるセーブデータを暗号化する価値はあんまり無いと思われます。

5.3 Serverに保存する場合の方法について

やり方を教えているTutorialがあると助かる訳でそれがないかを調べます。特にServerをタダで使用出来る方法を教えてくれるヤツがありがたいです。

Multi-play用のServer-Clientの作成方法はやっぱり沢山検索に出て来ますね。

How to Host Your UE4 Dedicated Server on AWS for FREEAWSのサーバーを無料で使用していますね。AWSはある程度までは無料なんでしょうか?GoogleのFire Baseをちょっとだけ勉強した時、来月からお金がかかりますみたいなメールが来たので止めちゃいましたが。無料なサービスならAWS使用したいですね。

しかし今回、私が知りたいのはServerにPlayerのデータを保存する方法だけです。

Multi-play Gameのような複雑な仕組みは、もっとUE4に詳しくなってから勉強します。

そのまま検索しても何も引っかからないので「Serverを使用してUE4でLoginを作成する方法」で検索してみました。Loginする方法とデータをサーバーに保存する方法は多分ほとんど同じでしょう。

Unreal Engine 4 Tutorial: Online Login System

がまずありました。Node.jsとAWSを使用しているみたいです。この辺のやり方はWebの開発者だったら超基本的な事かもしれません。UE4からデータをServerに発信する方法と受け取る方法さえ分かれば、残りはWEB開発の人に全部任せるのも手かもしれません。私はWEBの勉強よりUE4の勉強がしたいですし。

5.4 今回の調査からのSave方法に対しての結論

Slotにセーブでいいです。特別にServerを作成してゲームのdataの管理をする必要はないと思います。Multi-play用のゲームを作成した時にServer関係は勉強します。

6.Save機能のTutorialを復習する

正直、もう復習する必要ない位、UE4のSave方法について理解している自信がありますが、一応勉強します。

「2.2 2019-10-20のブログの復習」で紹介されていた

この二つのサイトを復習します。

全部、見ました。

UE4のSave方法について理解してから見るとHTF do I? Use the SaveGame Object in Unreal Engine 4は、簡便に説明していますが、基本はしっかり説明して、このTutorialを見るだけで学習者はSaveGameノードを使用してGame Dataをセーブする事が出来る作りになっています。

Saving and Loading Your Game

の方は、BPとC++の両方のやり方が説明されていますが、BPに関して言えば説明が十分と言えず、これを見ただけでは、学習者はSaveGameノードを使用してGame Dataをセーブする事は出来ないと思いました。C++に関しては、Asynchronousでセーブする方法や、Save Game To Slot関数が、Game DataをBinaryに変換してセーブしている事などが解説されていて勉強になりました。

7.まとめと感想

流石に集中力が切れて来たので今週はここまでにします。残りは来週以降やっていきます。