Unity

【Unityパズルゲーム制作入門】〇×風ゲームの作り方【1次元配列および2次元配列の両パターンを解説】

本記事内には、アフィリエイトリンクを含む場合があります

ゲーム開発で色々なミニゲームを作り、
Unityに慣れてきたら、
パズルゲーム制作にチャレンジしてみましょう!

パズルゲームは、ストーリーもないので、
ルールさえわかれば、サクッと遊べる有用なジャンルです。
(旅先でオセロやトランプをする感覚)

トランプなどのカードゲームを使って、
自分オリジナルのルールで遊んた経験のある方にとっては
Unityでパズルゲームを自作できるようになると、
遊びの創造が無限に広がるでしょう~^^

今回は、その入門として最適なパズルゲームを例に作り方を紹介します。
C#スクリプトだけ、プログラミング初心者にはとっつきにくいかもですが、頑張りましょう!

↓このような三目並べゲーム(◯×風ゲーム)を作ります

基本は知ってる!オンライン対戦ボードゲームを作りたいならコレ↓

【Unityボードゲーム開発】オンライン対戦できるターン制ゲームの作り方【マッチングに必要なロビーシステムも】私はゲーム開発の中でも、オンライン対応にこだわってゲームを開発してきました。 代表作の"Our Casual Battlefield"...

1.オブジェクトの準備

大前提として、
Unityの2Dゲームプロジェクトを選択して、起動します。

シーンにて
2DオブジェクトのSpriteのSqureを作成します。
Positionはx=-1,y=1、Scaleはxもyも0.8にします。

そのSqureに「Box Collider 2D」コンポーネントをアタッチします。

これがボードゲームの1マスに相当します。

これをCtrl+Dで9個に複製し、並べます。
図のように、-1、0、1というPositionの間隔で並べます。
x,yの座標でいえば、
(x,y)=(-1,1), (-1,0), (-1,-1), (0,1), (0,0), (0,-1), (1,1), (1,0), (1,-1)
の9個です。

scaleを0.8にしているので、ちょうど間にラインが入って、
わざわざラインを作る必要もありません^^

 

次は、ゲームとして最低限必要なUIを配置していきます。

ゲーム制作でよく使われるTextMeshProを作成します。
名前はお好みでつけ、ポジションはだいたい中央上くらいにします。

ちなみにTextMeshProを作成すると、こういうウィンドウが出ます。
上の「Import TMP Essenntials」をクリックし、最低限必要なインポートを終えたら、閉じましょう。

次に、Imageを作成します。
RectTransformの左の方の四角いアイコンをクリックして、
青矢印が広がったアイコン(全画面にストレッチ)を選択します。
その上で、Left、Top、Right、Bottomを0にすると、画面全体にImageが広がります。

Imageのcolorは、透明度のある黒に設定します。

次に、Buttonを作成します。
ドラッグ&ドロップで、先ほどのImageの配下に移動させましょう。
Buttonの配下のTextの内容は、「Retry」としておきます。
(つまり、これはゲームをリトライするためのButtonです^^)

UIのオブジェクト作成は以上です。

 

次にボードゲームで扱うコマを作成します。

SpriteのCircleとDiamondの2種類を作成します。
CircleのScaleはxとyを0.8に、
DiamondのScaleはxを0.8、yを1.6にすると、イイ感じに1マスに収まるようになります。
またOrder In Layerを1にして、
必ずマスよりも前面に来るようにしておきます。

その2つをAssetsフォルダに(整理したい人はPrefabsフォルダを作って)、
ドラッグ&ドロップしてプレファブ化します。

※プレファブ化すると、いくつもシーンに生成することができるオブジェクトになります。

一度プレファブ化したら、シーン上にはその2つは要らないので、削除します。

ゲームに必要なオブジェクトはこれで全てです(少ない!)

これらの少ない素材をスクリプトでゲームにしていきます。

2.スクリプトでゲームを動かす

Assetsフォルダで、C#Scriptを作成し、GameManagerと名付けます。

それをダブルクリックして、VS codeなどで、以下のように編集していきます。

スクリプトの内容は基本的なことですが、
C#というプログラミング言語の初心者には、複雑に感じるかもしれません。

それぞれの意味は、「//~」でコメント書きしているので、参考ください。

まずボードゲームを作る際は、必ずと言ってよいほど、1次元や2次元の配列を宣言して使います。
上のスクリプトでは、3行3列でたったの9マスなので、1次元配列を使った管理方法です。

今回は、シーンにすでにつくられたボードに駒を置いていくだけなので、
Start関数で、特別に何か処理することはありません。

Update関数のみでゲームは進行します。
ここに焦点を当てて、少し解説します。

画面クリックでオブジェクトを扱うなら"Rayを飛ばす"

下の文は、テンプレといってよいほど、ボードゲーム作成では頻出です。
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
カメラからみてクリックした方向に光線を飛ばす処理です。

光線が当たった判定hitを情報に、
そこにあるゲームオブジェクトのポジションをVector3 posで取得します。

ボードゲームでなければ、そこにそのままプレファブ生成することもありますが、
ボードゲームでは1マスに収まるようにプレファブを置かねばなりません。
(同じマスに置けないなどがあるため、ボードゲームでは配列を扱う宿命にあるといえます)

 

オブジェクトポジションから配列の要素番号を算出する

はじめにオブジェクトを-1,0,1を使ったキリの良いx,y座標で並べています。

なので、光線衝突で取得したゲームオブジェクトのポジションは、
-1,0,1の数値で構成される座標となります。

例えば、
当たったオブジェクトのポジション(x,y)座標が(-1,-1)は
画面でいうと下段左のSqure、
その隣の下段中央のSqureの座標は(0,-1)、
その右のSqureの座標は(1,-1)です。

9つのSqureを、下段から左、中央、右にかけて数えていくと、
その番号と配列の要素番号を対応させられれば、
ボードゲームのマス目を、1次元配列によって管理できるという仕組みです。
(こんがらがってきたら下の図を見ましょう^^;)

取得した座標(x,y)、それぞれにいったん+1して
「x+y*3」という式に入れれば、
配列の要素番号に対応する数値(整数)となります。
スクリプトでは、そのように計算し、idxという変数に代入しています。

これで配列の要素番号からマス目の座標が紐づくので、
マス目の状況を配列で管理できるようになります。

配列のint型の値によってプレファブを置けるか判定

スクリプトでは、配列の全要素の「値」を、デフォルトで-1に宣言しています。
Playerがプレファブを置くことで、この値を変更させていきます。
値はそのPlayer番号の、0または1とします。

もし、そのマス目(その配列要素)の値が-1なら、まだプレファブが置かれていない、つまり、置けるということです。

配列とは、0から始まる配列の要素番号とそれに値(intやstringなど)を格納することができます。

今回はint型の配列、要素数は9、値は全て-1なので、正確に宣言すると
int[] board = new int[9]{-1, -1, -1, -1, -1, -1, -1, -1, -1}
となります。

 

勝敗チェック

このゲームは自分のコマが3つ並べば、勝利です。
3つ並ぶパターンは数えられるくらいなので(縦3横3斜2の8つ)、
それらパターンをListに格納しておき、
毎ターン、ボードの状況がそれと同じかどうかで判定します。

ここで、二重ループを使います。
(ここから複雑になってきます!)

3つ並ぶパターン(int型配列)を要素に持つList(List<int[]> lines)をforeachで回し、
さらに、そのパターン(int[] v)の1要素ずつををforでチェックします。

※foreachはListの要素全てを処理、forは指定した回数を処理する

特に、forでは1要素目と2要素目、3要素目を比較して、boardの配列において、それらが示すマス目が同じ値(コマをおいて変化する数(0または1))かどうかチェックします。

「同じでない、もしくは、まだコマが置かれていなくて値が-1」なら、
issameという差し当たりのboolはfalseとなり、
フラグwinがtrueになることはありません。

 

引き分けの判定

毎ターン、フラグwinがtrueにならなければ、ゲームは引き分けか続行かを判定していきます。

このゲームでは、すべてのマスにコマを置いても、勝敗が付かなければ、引き分けです。

なので、board配列(の値に)に空きマス(-1)があるかないかで決めます。
空きマスチェックはforでboard配列の全要素を(board.Lengthで)調べます。

その結果、空きマスの数cntが0なら、結果UIと、TextMeshProの内容を”引き分け(DRAW)”を表示します。

なお、これらの処理をするUpdate関数の最後に手番を回します。
”勝ち”または”引き分け”にならなければ、手番が回る仕組みです。

 

3.スクリプトとオブジェクトの紐づけ

GameManager.csができたら、空のゲームオブジェクトにアタッチして、プレファブやUIのオブジェクトを紐づけます。

また、RetryボタンのOnClickにGameManagerのOnClickretry関数を紐づけます。

これでゲームが完成です!

 

パズルゲームはスクリプトの設計作業がほとんどで、
製作者のC#の理解と使いこなすスキルが求められます。

特に、配列やループ処理についての理解が必要です。
自分でアレンジしてみて、
オリジナルゲームを作ってみると理解が深まるでしょう~。

 

そして、実はここからが本題です。

実際に色々なパズルゲームを作っていくなら、
2次元配列を使って作成する方法が王道です。
(マス目ももっと多いはずなので)

なので、同ゲームを
2次元配列を使って作成するスクリプトバージョン
次の項で紹介します。

配列の理解さえあれば、むしろこっちのほうが簡単です。

4.2次元配列 ver.

先ほど作ったオブジェクトはそのままで、GameManager.csの内容を変更します。

2パターンを比べたい場合、違う名前のスクリプト(GM.csなど)で作成しましょう。

このようなスクリプトになります。

2次元配列の場合、すべての値を-1に初期化したい場合は、
Start関数で、for文などのループで処理する方が楽です。

また、勝敗判定は、
勝ちパターンと比較するのでなく、連続するコマを検出する方法をとっています。
こちらのほうが勝ちパターンがたくさんあるようなゲームには適しているので、ご参考ください。

勝敗判定の仕方やマス目にコマを置けるかどうかの処理が、パズルゲームの種類によって様々です。
勉強したい方は、そこに注目して勉強するのが良いと思います。

また、おまけとして、デバッグ用の関数(OnClickDebug())も追加しておきました。
何かおかしい時は、ボタンなどで呼び出して確認してみると良いでしょう。

作ったボードゲームをオンライン対戦できるようにしたいなら↓

【Unityボードゲーム開発】オンライン対戦できるターン制ゲームの作り方【マッチングに必要なロビーシステムも】私はゲーム開発の中でも、オンライン対応にこだわってゲームを開発してきました。 代表作の"Our Casual Battlefield"...
ABOUT ME
いなも@システマライフハッカー
”仙豆”を開発することを夢見て、健康食品会社で働いていたものの、2016年に出会ったロシアの武術”システマ”こそ、その糸口があると感銘し、勝手にシステマ普及活動を始める。 一方で、クリエイティブなモノ作りが好きで、DX社会で楽しみを見出せる"Unity”を活かして、”スマートかつ快適な暮らし”のヒントを発信している。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA