一週間ゲームジャムでUnityでシミュレーションゲームを作ってみました。
はじめに
久しぶりにunityroomの一週間ゲームジャムに参加して、せっかくなので前からやろうと思っていたはてなブログも始めてみることにしました。 作ったのはこんなゲームです。 unityroom.com
一週間ゲームジャムでは毎回参加する際に何か一つ以上新しいことに挑戦することにしているのですが、今回は
に挑戦することにしてみました。
それと自己紹介の記事も書いてみたので、お前誰だよって感じの人はこちらもどうぞ。
目次
制作編
今回はUnityのバージョンは2017.4.21f1を使っています。その他にバージョン管理でGithubを、ドット絵の作成にAsepriteを使用しました。
ドット絵の作成
ドット絵はAsepriteを使って作成しています。AsepriteはSteamで1480円で購入可能です。
このソフト自体はだいぶ前に買ってあったのですが、ちょっと触ってほったらかしにしていたのでこちらの動画を見ながら作成しました。
【ドット絵】Aseprite(エースプライト)~初級者向け講座[質問形式]~
これはゲーム中でも使用しているツナの画像です。
Unityで実装
作成したドットを使用してUnityで実装します。全て書くとかなりの量になってしまうので、この記事ではタイルマップと敵の簡単なAIについて取り上げます。
タイルマップ
タイルマップはVisualizerとOcean、Groundの3つのレイヤーで構成しました。
OceanとGroundは名前の通り、海と陸地のタイルマップです。Visualizerは下の画像の様にユニット選択時に到達可能範囲を青く、攻撃可能範囲を赤く可視化させる際に使用しています。
このVisualizerはあらかじめタイルを設置せず、ランタイム中にスクリプトから配置したり、取り除いたりしています。 まずプレイヤーのクリックを検知してユニットの周りに可視化タイルを配置するためのクラスを作成します。
~ using UnityEngine.Tilemaps; using UnityEngine.EventSystems; public class Visualizer : MonoBehaviour, IPointerClickHandler { [SerializeField] Tilemap visualizerTilemap; [SerializeField] TileBase passibleTile; [SerializeField] TileBase attackableTile; // 到達可能な範囲を青く可視化させる void ShowPassibleTile(Vector3Int unitPos,int maxStep) { // 一マスごとに確認して青くする CheckPassible(unitPos,maxStep + 1); } // 到達できるか確認する void CheckPassible(Vector3Int pos,int remainStep) { if(posの位置のタイルがOceanかつ敵対するユニットが配置されていない。) { visualizerTilemap.SetTile(pos,passibleTile); --remainStep; if(remainStep == 0)return; // これ以上移動できない // まだ移動できるなら隣接するマスに到達可能か再帰的に確認していく CheckPassible(pos + Vector3Int.up, remainStep); CheckPassible(pos + Vector3Int.left, remainStep); CheckPassible(pos + Vector3Int.right, remainStep); CheckPassible(pos + Vector3Int.down, remainStep); } } // プレイヤーがクリックしたときに実行する public void OnPointerClick(PointerEventData data) { if(既に選択しているユニットがいる) { TileBase clickedTile = visualizerTilemap.GetTile(クリックされた地点); if(clickedTile == passibleTile) { // 移動処理 } else if(clickedTile == attackableTile) { // 攻撃処理 } // VisualizerTilemapにあるタイルを全て取り除き選択しているユニットがいない状態にする。 } else if(クリックした地点にまだ行動できるユニットがいる) { その地点にいるユニットを選択状態にする処理 ShowPassibleTile(ユニットの地点、ユニットの移動可能な距離); } } ~ }
色々省いているけどこんな感じの実装をしました。タイルマップも普段あまり触らないため、スクリプトからどう操作すればいいのかわかりませんでしたが、Unity Technologies社のGitHubリポジトリにある
GitHub - Unity-Technologies/2d-techdemos: Tech Demos for Unity 2D Features
が大変参考になりました。先ほどのコードにもありますが、tilemap.SetTile(position,tile)でpassibleTile(青のタイル)やattackableTile(赤のタイル)を第2引数に与えればそのタイルが配置され、nullを与えれば消すことが出来ます。後はtilemap.GetTile(position)でクリックした地点にpassibleTileがあれば移動、attackableTileがあってそこに敵がいれば攻撃とすればいいと思います。
敵のAI
敵のAIはストラテジーパターンで作成しています。ただ時間がなく間に合っていないので今回は敵のAIは1パターンしか用意していません。 まずは基底クラスを作成します。これはコンポーネントにする必要もないのでMonoBehaviorは継承させません。
public class BrainBase { public void Update() { if(次に行動させるユニットが決まっている) { // ユニットの行動を決定する DecideAction(); } else { // 次に行動させるユニットを決める DecideUnit(); } } // 選択したユニットの行動を決定する(攻撃、移動、待機など) virtual protected void DecideAction() { } // 次に行動させるユニットを決める virtual protected void DecideUnit() { } // 継承先で汎用的に使えそうなプログラム色々 ~ }
後はこのBrainBaseを継承して様々なパターンのAIを作成し、EnemyManager的なクラスを作成してBrainBaseの派生クラスのインスタンスを生成、保持します。敵のターンになった時に一気に全ての敵の行動が処理されるとプレイヤーが何が起こったのかわからなくなるので、コルーチンなどで一定時間ごとにBrainBase.Update()を実行すればいいです。僕は0.5秒に一回更新するようにしました。
公開編
今回はWebGLのビルドでつまづくこともなく、2日遅刻して提出しました 制作時からTwitterでマグロもののゲームを作っている人が多いのは気づいていたのですが、まさかツナの足軽で被る人がいるとは思ってませんでした。 以前参加した際にはランキングはついてなかったのですが、いつの間にかランキングが出るようになっていました。
残念ながら僕のゲームはランキングには入れませんでした。 やっぱりランキングに入っているゲームは全体的に完成度の高いものばかりですね。
おわりに
初めてのブログ記事ということもあり文章が拙く読みにくかったかもしれませんが、ここまで読んでくださりありがとうございました。 書いていくうちに文章力も上がるんじゃないかなと思うので、今後も書き続けながら技術力も上げていって読んでくださった方に有益な記事を書けるようになっていったらなと思っています。 記事の内容に関して「ここが間違っている」とか「こういう方法もあるよ」とか「ここをもっと詳しく書いてほしい」とかあれば出来るだけ対応していくつもりなので、意見や質問があれば気軽にコメントをいただけると嬉しいです。
おまけ
この「おわりに」に何を書くか迷っていたのですが、自分の好きなものの宣伝をして少しでも貢献出来たらいいなと思ったので書かせてください。
もし著作権的にまずい等あればここの項目は削除します。
時々作業用BGMとしても使っているのですが、今回作ったのが足軽のシミュレーションゲームなのでこれを選びました。 とは言っても今回作ったゲームとはシステムが似ているわけではないのですが…
太閤立志伝シリーズはかなり好きなシリーズで何年も新作を待ち続けているのですが、もうチームも解体されているとかであまり期待できなそうなので、実力をつけていつか自主制作で精神的続編にチャレンジしたい思っているゲームです。