有名なゲームである「ブロック崩し」を題材として、実際にUnityでのゲーム制作を行ってみましょう。最初から要素を詰め込んでも大変なので、非常にシンプルなものを作成します。
1度に全て書いてしまうと文量が多くなるので、数回に分けて説明します。この記事ではブロックとボール、プレイヤーの挙動を実装します。
- プレイヤー・ボール・ブロックの作成(今回)
- ゲームクリア・ゲームオーバー処理の実装
- リトライの実装
- ボールの改善
- ゲームのビルド
Unityプログラミング講座一覧はこちら
作るゲーム
青色の棒を左右に動かして、赤色のブロックを全て壊すゲームです。ボールを下に落としてしまったらゲームオーバーになります。
作成手順
新規プロジェクトを作成する
プロジェクトのテンプレートを2Dとして作成します。プロジェクトを作成するとSample Sceneというシーンが存在するのですが、この名前だと何のシーンなのか判断できないので、プレイ画面という意味を込めてPlayにシーン名を変更します。
途中でダイアログが出ますが、Reloadで問題ないです。
カメラの設定を行う
初期状態だとカメラの表示範囲が狭いので、調整します。TransformコンポーネントのPosition
の値を(0, 0, -20)
に設定してカメラを位置を変更します。そして、表示範囲を広くするために、CameraコンポーネントのSize
の値を12
にします。
背景の色はCameraコンポーネントのBackground
で変更できます。お好みでどうぞ。
壁を作成する
まず、壁を作成します。Hierarchyビューで右クリックをしてCubeを作成します。
Cubeを4つ作成して大きさと位置を変えて上下左右の壁を作りましょう。筆者は次の値を用いました。
- 上下の壁のScaleは(15, 1, 1)
- 左右の壁のScaleは(1, 20, 1)
- 位置は以下の表の通り
名前 | x | y | z |
Wall Left | -7 | 0 | 0 |
Wall Right | 7 | 0 | 0 |
Wall Top | 0 | 10.5 | 0 |
Wall Bottom | 0 | -10.5 | 0 |
現段階だと、壁は暗く表示されています。これはシーン上にライトが存在しないためです。ライトを作成してもいいのですが、今回はライトなしで単色に表示するマテリアルを作成します。
これからいろいろなファイルを作っていくため、整理しやすいようにMaterialsフォルダを作り、その中にWallマテリアルを作成しました。今後マテリアルを作成する場合はこのフォルダの中に作るといいでしょう。
マテリアルのShaderの箇所をUnlit
のColor
に変更してください。Unlitは「アンライト」という意味であり、つまり、ライトに関係なくゲームオブジェクトを描画する設定です。これで好きな色で表示できるようになります。
作成したマテリアルを4つの壁に適用すれば、壁は完成です。(マテリアルの適用はマテリアルをSceneビューかHierarchyビューにあるゲームオブジェクトにドラッグ&ドロップすれば可能です。)
2Dゲームなのに3D Object?
今回作成するのは2Dゲームですが、2D Objectを使用する場合は画像ファイルを用意する必要があります。(今回でいえば、円の画像を用意しなければなりません。)
楽をするために今回は3D ObjectのCubeとSphereを四角と円として使用することにします。カメラのProjectionの設定がOrthograhic(平行投影)になっていれば、遠いもの/近いものを同じ大きさで表示するので2Dに見せることができます。
ボールを作成する
位置、大きさ、マテリアルの設定
続いて、ボールを作成します。3D ObjectのSphereを作成し、名前をBallにします。位置はとりあえず(0, 0, 0)
で良いでしょう。Scaleは(1, 1, 1)だとすこし大きいので、(0.5, 0.5, 0.5)
にします。また、壁と同様にマテリアルを作成し、好きな色に設定しましょう。
Rigidbodyの設定
ボールは壁やブロック、プレイヤーとの衝突によって跳ね返る必要があります。このような挙動は物理演算によって実現するのが簡単です。そこで、BallにRigidbodyコンポーネントをアタッチします。(Inspectorビューの下部にあるAdd ComponentからPhysics->Rigidbodyを選択する。)
Rigidbodyでは質量(Mass)や重力を使用するかどうか(Use Gravity)、空気抵抗(Drag, Angular Drag)、制約条件(Constraints)などが設定できます。ここでの設定は以下の通りです。
- 空気抵抗は無しにするので、DragとAngular Dragは0にする。
- 重力は使用しないので、Use Gravityはoffにする。
- 位置はz方向に動いて欲しくないので、Constraintsのpositionのzをonにする。
- 回転して欲しくないので、Constraintsのrotationの全てをonにする。
Physics Materialの設定
物理演算の設定はもう一つあり、Physics Materialで行います。これは、他の物体と衝突したときにどのくらい摩擦が生じるか(摩擦係数)、どのくらいの強さで跳ね返るのか(反発係数)を指定するものです。
Projectビューで右クリックしてCreate->Physic Material
を選択して新規作成します。(画像では分類のため、PhysicsMaterialsフォルダを作成し、その中に作成しています。)
作成したPhysics Materialの名前はNoFrictionとしました。摩擦をなくすためにDynamic/Static Frictionを0にし、減速して跳ね返ることがないようにBouncinessを1にします。
Friction/Bounce Combineは2つの物体がぶつかったときに摩擦/反発係数をどう計算するかを設定する項目です。Minimum/Maximumは2つのゲームオブジェクトに設定された2つのPhysics Materialのうち小さい/大きい値を採用する設定です。したがって、Fricition CombineをMinimumにすれば、常にFrictionが0で計算されるようになります。同様の理由からBounce CombineはMaximumを選び、常にBouncinessを1として計算するようにします。
作成したNoFrictionはBallの衝突判定処理を担っているSphere ColliderのMaterialに設定してください。
スクリプトの作成
Ballスクリプトを作成し、ボールを動かしてみましょう。以下のように作成し、Ballゲームオブジェクトにアタッチしてください。(やり方についてはこちら。コンポーネント名とファイル名を同じにしてください。)
using UnityEngine;
class Ball : MonoBehaviour
{
// ボールの移動の速さを指定する変数
public float speed = 5f;
Rigidbody myRigidbody;
void Start()
{
// Rigidbodyにアクセスして変数に保持しておく
myRigidbody = GetComponent<Rigidbody>();
// 右斜め45度に進む
myRigidbody.velocity = new Vector3(speed, speed, 0f);
}
}
StartメソッドでRigidbodyコンポーネントにアクセスして速度velocityを設定することでボールを動かします。x成分、y成分を同じにしているため、斜め45度で進むことになります。
移動の速さはInspectorビュー上で調整できるようにpublicにしています。この値を変えてプレイしてみてちょうどいい値を探しましょう。
プレイヤーを作成する
既存コンポーネントの設定
Cubeを作成し、名前をPlayerにします。Transformコンポーネントの値を変えて位置と大きさを調整してください。筆者は位置を(0,-7,0)に、大きさを(2, 0.5, 1)にしました。Playerのマテリアルを作成し、色を変更しましょう。
プレイヤーもボールや壁にぶつかるべきなので、移動を物理演算を用いて実装します。つまり、Rigidbodyの設定を行います。Rigidbodyコンポーネントをアタッチして、Ballと同様に空気抵抗を0、重力なしにしてください。Constrantsは位置xのみ許容するようにして、回転しないようにしましょう。
このままだとボールと衝突した際に、ボールは跳ね返らないと思います。これは、プレイヤーの質量がボールと同じであり、ボールに加わる力が小さいためです。(軽いものにぶつかる時と重いものにぶつかる時を考えてください。同じ速度でぶつかっても重いものにぶつかったときのほうが衝撃が強くなることが想像できるはずです。)ボールをきちんと跳ね返らせるために、Playerの質量を大きくします。PlayerにアタッチしているRigidbodyのMassの値を大きくしてください。100ぐらいで大丈夫です。
スクリプトの作成
Playerスクリプトを作成し、左右に動かせるようにします。以下のように作成し、Playerゲームオブジェクトにアタッチしてください。
using UnityEngine;
class Player : MonoBehaviour
{
// プレイヤーの移動の速さ
public float speed = 10f;
Rigidbody myRigidbody;
void Start()
{
// Rigidbodyにアクセスして変数に保持
myRigidbody = GetComponent<Rigidbody>();
}
void Update()
{
// 左右のキー入力により速度を変更する
myRigidbody.velocity = new Vector3(Input.GetAxis("Horizontal") * speed, 0f, 0f);
}
}
移動はRigidbodyにアクセスして速度velocityの値を変更することで行います。Input.GetAxis(“Horizontal”)は右入力の時1に、左入力の時-1に、入力なしのとき0になるのでこれを利用します。速度のx成分にこの値を使えば、右入力/左入力の時に速度のx成分が正/負になるため、左右に動くようになります。
また、速さの調整ができるようにpublic変数speedを用意しています。
Rigidbodyをつける?つけない?
今回の例では、壁にはRigidbodyをつけていませんが、ボールはちゃんと壁に衝突しています。そのため、プレイヤーもRigidbodyをつけずにTransformコンポーネントの位置を変えて動かせばいいと思う人もいるかもしれません。
Rigidbodyをつけるかつけないかの基準は「動くか/動かないか」で決めるとよいです。つまり、壁は動かないのでRigidbodyをつけず、プレイヤーは動くのでRigidbodyをつけています。
これはUnityの物理演算の仕様なのですが、動く物体にRigidbodyをつけないと処理が重くなったり、正しく処理できなくなる不具合が生じます。また、物理演算する物体を動かす際に、transformで位置を変えることも推奨されていません。プレイヤーをRigidbodyのvelocityで動かしているのはそのためです。
よくわからないなら、「物理演算させるものは全てRigidbodyを使い、Rigidbodyで動かす」と覚えておきましょう。今回の場合なら、壁にRigidbodyをつけても構いません。
ブロックを作成する
既存のコンポーネントの設定
ブロックを作成します。Cubeを作成し、名前をBlockに変更します。大きさと位置を調整して色を変えましょう。大きさを(2, 1, 1)に、色を赤色にしてみました。
プレハブにする
ブロックはステージに複数配置します。同じようなゲームオブジェクトを複数使用する場合はプレハブ機能が便利です。プレハブにしたいゲームオブジェクトをHierarchyビューからProjectビューにドラッグ&ドロップすれば作成できます。Blockプレハブを作成しましょう。
プレハブからゲームオブジェクトを作成する(インスタンス化といいます)にはプレハブをSceneビューやHierarchyビューにドラッグ&ドロップします。試しに、BlockプレハブからBlockのゲームオブジェクトを作成してみてください。
Blockゲームオブジェクトを複数作成して並べてみましょう。実はコピーを作るだけならプレハブ機能を使わなくてもCtrl+d
のショートカットで実現できます。
プレハブ機能の真価が発揮されるのはBlockゲームオブジェクトに変更を加えたい場合です。次のスクリプト作成でその効果を利用してみましょう。
スクリプトの作成
Blockスクリプトを作成し、ボールと衝突したときに消えるようにします。スクリプトは以下のように作成し、Blockプレハブにアタッチしてください。プレハブにアタッチすることで、複製したBlockゲームオブジェクト全てに、その変更が反映されます。この機能のおかげでBlockの変更が容易になり、ゲームオブジェクト1つ1つにスクリプトをアタッチする必要がなくなります。
using UnityEngine;
class Block : MonoBehaviour
{
// 何かとぶつかった時に呼ばれるビルトインメソッド
void OnCollisionEnter(Collision collision)
{
// ゲームオブジェクトを削除するメソッド
Destroy(gameObject);
}
}
衝突時の処理はUnityが用意しているビルトインメソッドの1つであるOnCollisionEnter
メソッドを使用することで実現できます。引数には衝突した相手の情報が入るCollision
型の変数が必要です。
誰と衝突したかを判定する場合にはこの引数を使用しますが、今回は簡単のために、相手に関係なくBlockを消すことにするのでここでは使いません。
ゲームオブジェクトを削除するメソッドとしてDestroy
メソッドが用意されていますので、それを使用します。引数に削除するゲームオブジェクトを指定します。自分自身のゲームオブジェクトは小文字で始まるgameObject
によって取得できるので、それを指定すればOKです。
実行すると衝突時にBlockが消えていることがわかります。
衝突時に呼ばれるビルトインメソッド
OnCollisionEnterメソッドのほかに衝突が終わったとき(離れる瞬間)に呼ばれるOnCollisionExitメソッドや触れている間にずっと呼ばれるOnCollisionStayメソッドがあります。
これらのメソッドはBox ColliderやShpere ColliderなどのColliderコンポーネントがアタッチされていないゲームオブジェクトに対しては呼ばれない他、Colliderが2Dのものだったり、IsTriggerの設定がoffになっている場合にも呼ばれないので注意しましょう。
※Colliderが2Dの場合(Box Collider 2Dなど)には代わりにOnCollisionEnter2Dなど2Dがついたメソッドが呼ばれます。
まとめ
この記事ではボールとプレイヤー、ブロックの実装をしました。次回はゲームクリア/ゲームオーバー処理を実装します。
- プレイヤー・ボール・ブロックの作成(今回)
- ゲームクリア・ゲームオーバー処理の実装
- リトライの実装
- ボールの改善
- ゲームのビルド
Unityプログラミング講座一覧はこちら