【第５回UE4ぷちコン】UE4初心者がUnrealEngineでノベルゲームを作ってみた

UE4初心者が第５回UE4ぷちコンにノベルゲーム等の作品を作って応募しました。

※イベントBeginPlayの挙動をぷちコンで初めて理解するほどのド素人です。

# ぷちコン応募作品

そんな初心者が、なんの熱意が生まれたのか第５回UE4ぷちコンを３作品作りました。

【第5回UE4ぷちコン応募作品】 ユニティちゃんのハート集め
https://www.youtube.com/embed/d0232-UcHvo

【第5回UE4ぷちコン応募作品】こはくちゃんズでわかるUE4ぷちコン　愛のチュートリアル 
https://www.youtube.com/embed/HlORm-ei5Do
<iframe width="312" height="176" src="http://ext.nicovideo.jp/thumb/sm28656951" scrolling="no" style="border:solid 1px #CCC;" frameborder="0"><a href="http://www.nicovideo.jp/watch/sm28656951">【ニコニコ動画】【第5回ぷちコン】こはくちゃんズでわかるUE4ぷちコン愛のチュートリアル</a></iframe>


【第5回UE4ぷちコン応募作品】愛、見つめていますか？ 
https://www.youtube.com/embed/4Al2XfehVOo

ぷちコン作品を開発して、少しだけUE4に詳しくなりました。

# UE4でノベルゲーム作り

この中でも自分が特に熱意をもって挑戦したのは、UE4でノベルゲームは作れるのだろうかというところです。

その結果が「こはくちゃんズでわかるUE4ぷちコン　愛のチュートリアル」です。

![image](https://qiita-image-store.s3.amazonaws.com/0/104377/8ff4118f-044b-ef9c-c8c7-cb0293d696e2.png)

![image](https://qiita-image-store.s3.amazonaws.com/0/104377/f17b3004-1651-9542-445f-44fb8ff8349d.png)

割とノベルゲームっぽくできたと思います。

UE4の初心者がどうやってノベルゲーム作ったのかを書いていきたいと思います。

# 参考にしたサイト等

以下のサイトを参考にさせていただきました。

[ちょこっとアンリアル(Enjoy2Unreal)ノベルゲームをつくってみる　その１～その5](http://enjoy2unreal.blogspot.jp/2015/06/blog-post_10.html?utm_medium=referral&utm_source=zeef.com&utm_campaign=ZEEF)

シナリオデータについて紙パレット コミティアT37a様のTwitterで公開したシナリオのデータテーブルが非常にわかりやすかったので、構造のみそのまま引用させていただきました。
><blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">会話ダイヤログですがBlueprint Dialogueを参考に自分に使いやすいように作り直しました・・！。データーテーブルで会話をセットアップ出来るようにしてあります。もっと最適化できそうだけどとりあえず動きました・・。<a href="https://twitter.com/hashtag/UE4?src=hash">#UE4</a> <a href="https://t.co/Vfas9uWA2s">pic.twitter.com/Vfas9uWA2s</a></p>&mdash; 紙パレット コミティアT37a (@kamipallet) <a href="https://twitter.com/kamipallet/status/718768997639041025">2016年4月9日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

素材は

キャラクター：ユニティちゃんのADV素材
背景画像：背景写真補完の会
UI素材：びたちー素材館

を利用させていただきました。

# ノベルゲーム作り

##　構造体＆データテーブル

シナリオはCSVに書き込んだため、構造体を用いてデータテーブルを作成しました。

・構造体
<img width="1251" alt="構造体.png" src="https://qiita-image-store.s3.amazonaws.com/0/104377/94145e3f-64f1-bc9e-3a57-bbf0108be1e4.png">

・データテーブル
<img width="1251" alt="データテーブル.png" src="https://qiita-image-store.s3.amazonaws.com/0/104377/0c441f17-4676-6d4d-dc89-67a2787baccc.png">


##　背景画像、立ち絵表示

![背景.png](https://qiita-image-store.s3.amazonaws.com/0/104377/e93e3041-3725-93d1-ed75-dd5a079e6490.png)

背景画像、立ち絵はPaperSpriteActorで表示しています。
立ち絵は画像を入れ替えたいため、NovelCharacterBluprintというBPを作成して動的に切り替えるようにしました。

## GameMode & PlayerController

マウスの左ボタンをクリックして文章を進めるため、PlayerContorollerを継承したBPを作成します。

<img width="334" alt="controller.png" src="https://qiita-image-store.s3.amazonaws.com/0/104377/d32ffedf-0e91-1f44-a730-32d75b38f44b.png">

名前はNovelPlayerControllerと名づけています。
このNovelPlayerControllerをGameModeに反映させるため、NovelGameModeというBPを作成して設定しています。

<img width="1256" alt="中身.png" src="https://qiita-image-store.s3.amazonaws.com/0/104377/a854b080-e255-3274-2ade-c24e17afec54.png">

NovelGameModeを設定→プロジェクト設定のマップ＆モードで設定しています

<img width="1271" alt="プロジェクト設定.png" src="https://qiita-image-store.s3.amazonaws.com/0/104377/80a8ce7c-0567-ba09-5785-2909b17876a7.png">

## NovelPlayerController

シナリオデータ読み込み、マウスのクリック処理はNovelPlayerControllerに記載しました。

BP全容
![プログラム1.png](https://qiita-image-store.s3.amazonaws.com/0/104377/718717bc-fa37-dc81-533d-500895584957.png)

変数
![変数.png](https://qiita-image-store.s3.amazonaws.com/0/104377/4265aea3-de42-4781-2a45-43f8234e1f51.png)

BPの詳細内容です。

### イベントグラフ

初期化とシナリオデータ読み込み
![プログラム2.png](https://qiita-image-store.s3.amazonaws.com/0/104377/519b2230-8fa8-de6e-1ad4-b42ddd4b7582.png)

マウス入力を受け付けるため、BeginPlayからEnableInputを呼び出し、GetDataTableRowNamesでシナリオデータを読み込み、ForEachLoopでまわしてデータをセットしています。

セットしているのは以下のとおりです。
![プログラム３.png](https://qiita-image-store.s3.amazonaws.com/0/104377/87e48f22-826a-9536-e987-634fd7c17854.png)

かなり汚いです。もっと効率のいいやりかたがありそうな気がします。

![プログラム4.png](https://qiita-image-store.s3.amazonaws.com/0/104377/e8e18c4f-9b66-ccab-d32f-560a70ea4838.png)

マウスを左クリックしたときに、次に表示するメッセージを取得します。

![プログラム5.png](https://qiita-image-store.s3.amazonaws.com/0/104377/91323363-2c2e-8d87-a4f4-ee206d1cc33b.png)

最後にシナリオデータに書かれたコマンドを判定します。
今回はEndというコマンド名が来たら、タイトルをOpenLevelで開く処理のみ実装しています。

### 立ち絵表示(showCharacter)

![プログラム6.png](https://qiita-image-store.s3.amazonaws.com/0/104377/a4a992aa-2941-2059-9ccd-2aab3d2c0628.png)

この関数が呼ばれたら、立ち絵画像をすべて薄い黒色に表示した後、会話をするキャラクターの立ち絵をシナリオデータを参照して表示します。

時間がなかったとはいえ、キャラクターの名前で表示する画像のリストを判定を入れているのはバッドノウハウです。
もっといい方法が間違いなくあると思うので、上記は参考程度にしてください。

## NovelCharacterBlueprint

立ち絵のクラスです。Actorクラスを継承しています。

### ビューポート

<img width="1251" alt="NovelCharacterBlueprint.png" src="https://qiita-image-store.s3.amazonaws.com/0/104377/ed2df0b9-7f39-0d53-876d-1250516626ff.png">

Actorクラスの子にPaperSpriteが入っています。
PaperSpriteを継承しなかったのはとある理由があります。

### 立ち絵変更(SetCharacterImage)

<img width="727" alt="SetCharacterImage.png" src="https://qiita-image-store.s3.amazonaws.com/0/104377/a5e1f1bb-59c0-0fe3-54ff-bbc383fdd550.png">

立ち絵（Sprite）を切り替える関数です。

PaperSpriteを継承したときはSetSpriteを呼んでもSpriteが切り替えられませんでした。
Actorの子にした場合はSetSpriteで切り替えられたため、そういう仕様っぽいです。

### ブラックアウト（CharacterBrackOut）

<img width="492" alt="CharacterBrackOut.png" src="https://qiita-image-store.s3.amazonaws.com/0/104377/fc24b33c-05f5-054a-99a5-17f5fdcab855.png">

下記の画像の通り、会話をしていないキャラクターを少し暗くするための関数です。

![tatie.png](https://qiita-image-store.s3.amazonaws.com/0/104377/aecef461-6887-d552-313d-d0b235891d6d.png)

外部のクラスから呼ばれることを想定しています。

## UI(NovelUI)

テキストを表示するためのUIを作成します。

### デザイナー

<img width="975" alt="デザイン.png" src="https://qiita-image-store.s3.amazonaws.com/0/104377/2d1f80b9-2496-e361-0914-02e6c0c2fd71.png">

デザインはこの通りです。
選択肢のボタン3つと、キャラクター名表示、本文表示を設置しています。

### グラフ

選択肢ボタンのクリックイベントグラフ、テキストと選択肢ボタンを表示するために沢山のバインドクラスを作成しました。

####イベントグラフ

<img width="849" alt="イベントグラフ.png" src="https://qiita-image-store.s3.amazonaws.com/0/104377/d8ecc2b0-3519-73fe-2ff9-d05a60ee5ca4.png">

選択肢ボタンのクリックイベントを拾っています。
押した場合、該当のシナリオにジャンプする処理です。

####バインドクラス

テキストを表示、選択肢ボタンの表示およびラベル表示をバインドクラスに設定しています。

・本文表示

<img width="1642" alt="メッセージ表示.png" src="https://qiita-image-store.s3.amazonaws.com/0/104377/9483d855-15ee-ab5b-8ea8-043f7becd9c7.png">

データテーブルでは改行コードは文字列（\nなど）に変換されるため、Replaceで改行コードに変換をかけます。
ToにShift＋Enterで改行が入力できます。

![image](https://qiita-image-store.s3.amazonaws.com/0/104377/07cdbb49-1f0e-c5ad-eaa1-500b6cff625e.png)

Replaceをはさむことで上記の画像のようにちゃんと改行表示ができます。

GetSubstringの部分で流れるような文字列表示をしています。
ただ、UIではDelayが使用できないため、文字表示のスピードを変更したい場合はHUDで作成するか、別のBPに処理を書いたほうがよさそうです。

・キャラクター名表示

<img width="1012" alt=キャラクター名表示.png" src="https://qiita-image-store.s3.amazonaws.com/0/104377/5ab4adaf-72b5-b53f-7612-0717686c6dff.png">

キャラクター名を表示するバインドクラスです。

・選択肢表示

<img width="1438" alt="選択肢ボタン表示.png" src="https://qiita-image-store.s3.amazonaws.com/0/104377/525e1a73-aa1b-6812-3ca6-2411e264eb73.png">

<img width="1296" alt="選択肢ボタンラベル表示.png" src="https://qiita-image-store.s3.amazonaws.com/0/104377/ec40b0b8-7f11-42dd-6013-16bcdccc3ea7.png">

データテーブルで選択肢のラベルがあった場合、以下の画像のようにボタンとボタンラベルを表示するバインドクラスです。

![image](https://qiita-image-store.s3.amazonaws.com/0/104377/f17b3004-1651-9542-445f-44fb8ff8349d.png)

・シナリオジャンプ処理

<img width="1295" alt="シナリオジャンプ.png" src="https://qiita-image-store.s3.amazonaws.com/0/104377/19d8b482-1a2a-c0ea-47f8-3c2223d3c7c8.png">

シナリオにジャンプする関数です。
選択肢を選んだ場合、該当のシナリオに本文のインデックスを設定するためのクラスです。

以下のローカル変数をセットしています。

<img width="377" alt="シナリオジャンプ.png" src="https://qiita-image-store.s3.amazonaws.com/0/104377/610cc092-e7d2-fd34-b634-2b4766508625.png">

## レベルブループリント

レベルブループリントはUIの表示とアウトライナに格納したNovelCharacterBlueprintをNovelPlayerController.CharacterArrayに入れる処理を行っています。

<img width="1611" alt="level.png" src="https://qiita-image-store.s3.amazonaws.com/0/104377/a9aa76f7-f18a-575b-4f9e-675a8af3f2f5.png">

# まとめ

UE4でも割と簡単にノベルゲームは作れます。
初心者ですらADVのエンジンっぽいものが作れる当たり、UE4は素晴らしいゲームエンジンです。
せっかく簡易的とはいえノベルゲームのエンジンを作ったので、UE4の勉強のためにもバックログなどノベルゲームでは当たり前に実装されている機能を追加していきたいと思っています。

© Unity Technologies Japan/UCL 
©びたちー素材館
©背景写真補完の会