気づけばツール漬け

3D,2Dのツールを色々使ってみた記録を書いていきたいです。

「HEXALINE(ヘキサライン)」開発メモ

はじめに

第23回UE5ぷちコン応募作「HEXALINE(ヘキサライン)」開発メモです。使用したUEのバージョンは5.5.4です。

今回のテーマの「せん」は、解釈の幅が広くて面白いテーマだと感じました。思いつくネタが多かったので、要素を書き出して組み合わせるみたいな一人ブレストをやってみたりしました。

最初は「潜水」と「一閃」で何かできないかな~~と考えを進めていたのですが、ふと、「線をつなぐやりかた」を思いついてしまったので、そっちにシフトしました。「線をつなぐ」というのは、わりと最初に思いついていたのですが、「どうやって」が思いつかなかったので保留にしてました。

シンプルにできそうだし少し楽に作れそうだな、ということで線をつなぐゲームにしました。(後にそこそこ苦労することになるとは、このときの僕は考えもしなかったんだ……)

応募動画

応募動画はこちらです。

www.youtube.com

 

ダウンロード

パッケージ化しました。よかったら遊んでみてください。疑いのあるアプリとして警告が出ると思いますが、「詳細」から許可すれば大丈夫です。
初回の起動時は、画像が出てくるまで少し時間がかかるみたいです。

drive.google.com

 

ステージ構築

六角形のSpawner

六角形用のグリッド状に、六角形パネルアクターを並べるスポーナーを作ってマップを構築しました。六角形の尖っている部分を上として直径1mに設定しました。横側のまっすぐの辺は直径1mの円周の内側にあるので50cmずらして並べることはできず、短くなっている分だけオフセットして並べる必要があります。ごちゃごちゃしてますが、以下の図のような関数を作って六角形をならべました。横に並べていくと想定して、奇数行の場合は半分ずらすという処理を入れました。


六角形グリッドは以下のページを参考にしました。

www.redblobgames.com

 

EditorUtilityWidgetを作成

各ステージはサブレベルで作成しました。サブレベルに先程の六角形Spawnerを置いて六角形を並べたあと、それらに線と発火点を追加して1ステージを作成する流れにしました。
六角形と線のレイアウトをつくるために、ユーティリティウィジェットを作成しました。これのおかげでステージ制作がだいぶ楽になりました。並んでいる六角形のどれかを選択して発火点と入れ替えたり、六角形の上の線の位置を設定できます。線は角度が決まっているので全部で6箇所置けるようにして、それぞれ有無のBoolで設定しました。

 

スタート時にランダム回転

ステージ作成時は、とにかく一本の線でできるだけ長くなるなるように配置しました。このゲームの仕様では一本のチェーンが長いほど高得点になるはずなので、長くつながる線になるように構成しました。このページのトップにある画像は、ステージ3の最大チェーン数になる想定の線の状態です。
線が一本になるように設定してステージ開始時にランダムにそれぞれの六角形を回転させました。角度が決まっているのでSelectノードでそれぞれの角度を選択するようにしました。

線の配置とか六角形の位置は個別に変えていません。それぞれが回転しているだけです。なので、答えがわかっていればほぼ最高得点は出せるはずです。(ですが、自分でも正解の配置は覚えてません)

 

線がつながる判定

線モデルの構成

線はStaticMeshです。両端に小さめのキューブを置いて、そこから7cm程度の球形に近い小さなスフィアトレースを出すようにしました。

チェーンの判定

つながっている(隣接している)線の端っこならスフィアトレースにヒットするはずなので、ヒットした複数の線の中で発火していない線に対して、次の発火のイベントを飛ばすようにしました。ついでに現在のチェーン数も渡すようにしました。この流れを線が途切れる(発火してない線がなにもない。またはつながっていない)状態まで続けます。

クリア判定

線をつなぐのはよかったのですが、線は全部消えた? 一回で消えた? という判定にちょっと苦労しました。発火点も線も、自分につながっている線へ発火を受け継いでいくだけなので、ステージの上がどうなってるのかは誰も知りません。

まず、「つながる線がなにもないです」という状態を判定しないとならないので、Retriggerable Delay を使って1秒待つことにしました。各線は、つながる線が有るかどうかを判定しているので、BranchがFalseで通ってきた流れは何もつながっていないことになります。ただ、すべての線がその判定を送ってくるので送られてくるたびにRetriggerable Delay でリセットします。このDelayで無事に1秒経過したら、「つながる線が何も無い」といえるのでクリア判定に移ります。

発火点が全部発火状態になっていればクリアと決めていたのでその処理を続けます。発火点をクリックした時と、誘爆で発火した時に”Done”というタグを追加して、線が全部発火したあとにDone状態の発火点の数が、ゲーム開始時の発火点の数とイコール(図では>=にしてますが)ならクリアしていると判定しました。

ステージをクリアしたと判定したあとで、発火済みの線をチェックするようにしました。

線は発火したときに"Ignited"というタグを追加しておきます。Ignitedのタグがついた線の数がステージ開始時に数えた線の数と同じだったら全部発火したと判定しました。

さらに続きます。

発火点をクリックした時に"SelfStart"というタグをつけます。誘爆したときはつかないので、タグがついた数が1個だったときに「一回だけの発火だった」という判定にしました。

 

敵と線の処理

このゲームには制限時間をつけていませんが、擬似的な制限時間をつけるために敵を入れることにしました。敵がいるおかげで緊張感が出てよかったと思っています。前述したとおり、答えがわかってしまえばだいたいハイスコアになるので、それはそれでつまらないですし。
最初は線に敵がとりついたら六角形の回転ができなくなるという仕様にしていましたが、回転できなくなった場合は本当に何もできなくなくなり、ただただ鬱陶しいだけなので没にしました。

敵に食べられている線は、1秒毎にスケールをちょっとずつ小さくしていきました。食べ尽くされるまでの時間を可変できるようにしていましたが、最終的には20秒で設定しました。少し長いかなと思いましたが、意外と焦る状況になりやすくてちょうどよかったと思っています。

残り時間が4秒を下回ったら、六角形の色を点滅させてプレイヤーに知らせるようにしました。

敵は線に取り付いた瞬間に、線のチャイルドアクターに変えています。六角形を回転させても線にくっつきっぱなしで動かすためです。線を食べ尽くしたあとは、再びさまよう状態に戻すので敵アクターをスポーンし直す流れにしました。

敵への攻撃

線が発火すると同時に、線の末端から逆の末端まで太めのスフィアトレースを出して攻撃としました。線に敵がくっついているかどうかは関係なく、そのトレースにヒットしたら倒すようにしました。

敵モデル

今回も、敵のモデルのボーン配置とスキニングとアニメーションはUE5上で作りました。UE5のツールはまだいまいち使いづらいのですが、シンプルなモデルならそれほどストレスでもないです(個人の感想です)

目は片目につき2枚の板ポリゴンを配置して移動や回転で動かしました。2枚にしてるのは、バッテン型を作るためです。
六角形など背景も含めて全部3Dモデルですが、castShadowをつけないことでフラットな感じにしています。図のように敵の目もかなり浮いているのですが、影がない状態でほぼ真上から見ると違和感がなく、思ったより可愛くなったので気に入ってます。

おわりに

今回パズルぽいゲームを作ってみたのですが、なんだか自分でも気に入ってしまったので、もうちょっとステージを増やしたりしてみたいと思っています。いつになるかはわかりませんが……。