「”れい”をさがせ」制作メモ
第12回UE4ぷちコン応募作「”れい”をさがせ」の制作メモです。UE4のバージョンは、4.22.3です。
なんだかんだで6回目の応募でした。
応募動画はこちらです。
www.youtube.com
ゲームはこちらにアップしました。よかったら遊んでみてください。
企画について
今回テーマは「れい」ということで、いくつかアイディアは浮かんだのですが、作業する時間があまりとれなかったのでシンプルなものにしました。とはいえ、シンプルすぎると思ったので、ゲーム的な味付けとしてスピード感を入れてみました。
最初は、1ステージにつき5秒程度の制限時間を設定して、時間切れになっても容赦なくどんどん進むという展開を考えていました。しかし、作業しているうちに以下の懸念点が浮かび上がってきたのでボツにしました。
- 5秒は、見つからないときはあっというまに過ぎ去る。
- ゲームをしている感覚になりづらい。(罰ゲームなみのしんどさ)
- ギリギリで発見したが、クリックできなかった。納得いかない。
じゃあ10秒ならどうかと考えましたが、10秒は長すぎです。7秒、8秒は、中途半端で気持ち悪いです。
ということで、ステージごとに時間をリセットせず、全体の制限時間で30秒と設定しました。
全体の制限にすることで、見つけるのが滞った場合があっても、他のステージでリカバリ可能になります。
(全6ステージなので、1ステージあたり5秒は変わらないのですが、プレイヤーの気持ち的には全然違うはず)
レベルの構成
パーシスタントレベルと、その中のサブレベルで分けてシームレスに展開させました。
パーシスタントレベルを開きっぱなしにして「タイトルレベル」「ゲームレベル」「リザルトレベル」と順番にアンロードしてからロードするという流れです。
ロードするタイミングは、プレイヤーコントローラーからイベントディスパッチャで通知しました。
図は、パーシスタントレベルBPの全体です。
タイトルレベルを再び読み込むときは、Remove All Widgetsを置いて、ウィジェットがたまり続けないようにしました。
ゲーム用のサブレベルは1つだけです。グリッド状にブロックを並べる処理をするアクターを1個置いてあるだけです。
ゲームの流れの管理は、ゲームのデータを入れておく構造体を使いました。
ゲームデータの構造体をステージ数分の配列にして、ステージクリアごとにインデックスを進めていく形にしました。
BlockNumとBlockIntervalは、ブロックを並べるときに使いました。TimeValueとCameraLocationはステージごとに変えるつもりで設定しましたが、結局使いませんでした。
正解をクリックしたとき、ステージ上のブロックをTagで検索して、全消ししてから改めて並べ直しました。
グリッド状に並べる
ブロックはアクターで作りました。そのチャイルドアクターとして「当たり」「ハズレ」「ダミー」を作りました。
グリッド状に並べる方法は、みつまめ杏仁さんの記事を参考にしました。ありがとうございます。
BuildGridという関数を作って、GameInstanceにあるゲームデータの構造体配列から、BlockNumとBlockIntervalを受け取り、処理しました。
かならず正方形にするつもりだったので、Hnumで横に並ぶ数の分で縦にも並べます。
線がごちゃっとしてたので、わざわざローカル変数に入れてます。
→
→
まずは、ハズレブロックだけを並べます。
ハズレブロックだけをグリッド状にならべつつ、それらを全部配列に入れました。
当たりとダミーで置き換える
ハズレブロックが入っている配列のランダムなインデックスを指定して、正解ブロックをハズレブロックと入れ替えました。ReplaceHitActorという関数を作って処理しました。
ダミーのブロックも同様に入れ替えました。このとき、すでに入れ替え済みの正解ブロックを上書きしないように、選択済みのインデックスと同じ場合を避けました。ただ、この流れのため、ダミーブロックが出てこないことがまれにあります。
正解が出てこないのは致命的なバグですが、ダミーのブロックがあってもなくても、不正解なブロックなのは変わりないので、特に問題なしとして対処しませんでした。
ダミーのブロックは、特に必要ないものですが、ゲームとしてはぜひほしいと思って入れました。シンプルゆえに、なかなかミスはしないので、ダミーの存在によって一瞬迷いが生じて「やっちまった」、または「やられた」という状況になるのは大事です。
3段階の難易度設定と文字を選ぶ仕組み
最初は完全にランダムにしようと思っていましたが、簡単な文字と難しい文字の差がはげしいので、3段階くらいに分ける必要があると考えました。
INT型の配列を難易度分の3個作って、全文字種分の数値を順番に入れていきました。
この配列をシャッフルして、結果から上の2個分を抜き出し、最終的なステージ順(文字種)を決める配列に入れました。これを3回繰り返します。全部で6要素の配列が1個できあがります。
マクロの中身は、こんなかんじ。
選ばれた6個の値は、各ブロックのアクターがもっている文字テクスチャの配列から抜き出すためのインデックスにしました。図のように、正解ブロックには正解の文字の配列、ハズレにはハズレの文字の配列を設定しました。
上記の配列の設定は、ゴリおしな手作業でやったので結構面倒でした。次に同様のことをやる機会がある場合はなんとかしたいです。
各ブロックからの処理
正解か不正解かは、ブロックごとに固定なので、クリックのイベントで各処理に飛ばしました。
親のアクターのクリックイベントから、イベントディスパッチャをCallして、それぞれの子で受けます。
不正解ブロックなら、1秒マイナスする処理。
GameModeで時間のカウントダウンを処理したので、そこの変数から-1しました。
正解ブロックなら、次のステージへ進む処理。
正解後は一旦クリックできないようにして、ステージのインデックスを進めて、全クリアしているか判定しました。
まだなら、フェードを挟んで次のステージ構築用のイベントディスパッチャをコール。これはパーシスタントレベルのBPと、ブロックを並べるアクターがそれぞれ受けて処理します。
→
→
ステージ全クリア(または時間切れ)になったら、リザルトへ移ります。
→
→
●と×は、テクスチャにしてポリゴンに貼り付け(JudgeMesh)、クリックしたときに表示させました。
ステージ開始直後の、文字の部分が回転するのは、TimeLineノードとSetRelativeRotationでうごかしました。
制限時間とスコアの計算
ゲーム終了時点の時間の値を、まず100倍してからTurncateノードで小数点以下を消し、さらに100倍してスコアとしました。単純です。ただ、終了した時点で画面上に表示されていた時間と、リザルトで表示されるスコアは、0.01秒分少なくなっていたので、わざと+0.01してからスコアにしています。
あと、時間切れで終わっても微妙に0じゃない値が変数に残っていたので、時間切れなら0にしました。
おわりに
思いついたけど、やれなかったネタをいくつか。
- lay(横たわる)ネタ。
横に寝かせたグレイマンを腰を軸にして、寝た姿勢のまま上半身下半身を別々に稼働させて進んでいくゲーム。QWOPとか、ツボゲーみたいなノリになったかも(うまくできればですが・・・)。
- 鈴の音を連鎖させる。
これは、フラッシュアイディア的なもので、ゲームにするところまでは思いつかなかったです。
- 冷気を集める、育てる。
これもまたフラッシュアイディア……、で、どうする? の部分が思いつきませんでした。
以上です。