はじめに
どうも。Unity2Dアクションゲーム制作第6回です。
今回は収集可能なコインと、下からジャンプして叩くことで壊れるブロックを作ります。
前回:
www.umagame.info
コインの追加
オブジェクトの作成・スプライト
まず、コインの追加を行います。

今回はこんなスプライトを用意しました。
ウマ娘 プリティーダービーより
見た目はコインですが、元ネタ(ウマ娘)ではマニーという名前なのでUnity内ではこっちに合わせます。

画像のインポート・設定(やり方は第2回または第3回参照)をしたら、画像をそのままシーン内にドラッグアンドドロップして新たにオブジェクトを作成します。(ヒエラルキー内で右クリック→作成でも可)

名前もちゃんと変えておきます。

新しくスクリプトを作ります。

コイン以外にも回収可能なアイテムを作る可能性があるので、名前はCollectable(収集可能)にします。
public class Collectable : MonoBehaviour
{
public string ID;
void Start()
{
}
void Update()
{
this.transform.Rotate(0, 0.3f, 0);
}
}
コードはこんな感じです。publicのstring型変数IDを持ち、毎フレーム0.3°だけY軸が回転します。

スクリプトをMoneyオブジェクトに適用して、IDを「money」に設定します。このIDは後ほど使います。

これでテストプレイすると、コインが回転します。現時点では、コインに触れても何も起きません。
トリガーの設定

先程作成したMoneyのオブジェクトにコライダーを追加します。円の形なので、「Circle Collider 2D」を使います。

ここで、「トリガーにする」にチェックを入れます。
コライダーをトリガーとして使うと、他のオブジェクトとの当たり判定がなくなりますが他のオブジェクトと重なりを検知することが出来ます。
そして、Player.csに以下のコードを追加します。
public class Player : MonoBehaviour
{
public int Money = 0;
private void OnCollisionEnter2D(Collision2D collision)
{
GameObject obj = collision.gameObject;
if (obj.CompareTag("InstaDeath"))
{
SceneManager.LoadScene("SampleScene");
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
GameObject obj = collision.gameObject;
if (obj.GetComponent<Collectable>() != null)
{
if(obj.GetComponent<Collectable>().id == "money")
{
Money++;
GameObject.Destroy(obj);
}
}
}
}
トリガーに設定したコライダーとの接触時に、OnTriggerEnter2Dというメソッドが呼び出されます。
そのトリガーがCollectable(回収可能なアイテム)かどうかを判定して、そのIDが"money"であれば以下の処理を実行します。
public int変数であるMoneyを1増やす(Moneyは獲得したコインの総数です)
コインのオブジェクトを削除
トリガーのオブジェクト(この場合はコインのオブジェクト)はOnCollsionEnter2Dと同様にcollision.gameObjectで取得できますが、長いのでobjという名前の変数に格納して使うようにしています。
前回(第5回)に追加したOnCollsionEnter2Dでもobjを使うように修正しておきました。
UIの追加
コインを取得した数をテキストで表示するようにします。

ヒエラルキーで右クリック→UI→「テキスト - TextMeshPro」をクリック。

名前はMoneyTextにします。

TextMeshProを初めて作成したときは、このようなウィンドウが開きます。
上の「Import TMP Essentials」をクリックして、TMP Essentialsをインポートしましょう。これはTextMeshProを使う際に必須となります。
下の「TMP Examples & Extras」はあってもなくてもどちらでも良いです。私はインポートしませんでした。
テキストを追加しましたが、シーン内にそれらしきものは見えないと思います。

それもそのはずで、このように初期設定のままだとテキストが作ったマップと比べてとてつもなく大きくなってしまっています。
ちなみに、右上のやつはCanvas(テキストなどのUIを配置するエリア)の中心です。

まず、Canvasをカメラ(Main Camera)の子オブジェクトにします。CanvasをMain Cameraにドラッグアンドドロップしてください。

これで、テキストを含むUIがカメラに追従するようになります。

次にCanvasのインスペクターから、「レンダーモード」を「ワールド空間」に設定して、

テキスト(MoneyText)の位置と幅、高さをこのように設定します。

するとこのような黄色の枠が表示されるので、位置を移動させ、黄色の枠が横長になるようにします。

テキストのインスペクターの「Font Size」を小さくします。今回は0.5にしました。
テキストの内容(初期設定では「New Text」になっていると思います)は変更しなくて良いです。
画像UI / テキスト制御スクリプト
画像のUIも追加します。

Canvasを右クリック→UI→「画像」をクリック。

名前はMoneyIconにします。

幅・高さを1に設定します。幅と高さの比はテクスチャに合わせてください。今回は正方形のテクスチャ(幅:高さ=1:1)なのでどちらも1にしています。

テキストの隣に置きます。

そして、画像ファイルを「ソース画像」にドラッグアンドドロップして画像を設定します。

画像のアニメーションとテキストの変更を行うスクリプトを作成します。名前はGameManagerにします。(正直名前は何でも良いです)
public class GameManager : MonoBehaviour
{
public TextMeshProUGUI MoneyText;
public UnityEngine.UI.Image MoneyIcon;
public Player PlayerObj;
void Start()
{
}
void Update()
{
MoneyIcon.GetComponent<RectTransform>().Rotate(0, 0.3f, 0);
MoneyText.text = ":" + PlayerObj.Money
}
}
中身はこんな感じです。
Update()にコインの画像を回転させる処理と、テキストにPlayer.Moneyを表示する処理を書いています。

作ったスクリプトを適用します。新たに空のオブジェクトを追加してもいいのですが、今回はTextMeshProを作成した際に追加に作成されたEventSystemを使おうと思います。

テキスト、画像、プレイヤーのオブジェクトを設定します。

これで、コインを取得した際にその数が表示されるようになりました。
これでコインの実装は完了ですが、複数のコインをマップ上に置くのでもう少しだけ作業が必要です。

まず、新しく空のオブジェクトを作成します。これはコインをたくさんコピーしたときに、ヒエラルキーがコインまみれにならないようにするためだけのオブジェクトです。

名前はMoneyObjectsにしておきます。

MoneyオブジェクトをMoneyObjectsにドラッグアンドドロップして、子オブジェクトにします。

次に、プレハブ用のフォルダを作ります。プレハブとは、同じオブジェクトをコピーしてたくさんシーンに置くために使う設計図のようなものです。

名前はPrefabsにします。

PrefabsフォルダにMoneyオブジェクトをドラッグアンドドロップして、Moneyのプレハブを作ります。

その後Moneyのコピー&ペーストを繰り返して、マップにコインを配置します。

配置する際は、グリッドスナップと呼ばれる機能を使うと便利です。
シーン上部のメニューを上画像のように設定して、グリッドスナップの「グリッドサイズ」をいじることでグリッドに合わせてオブジェクトを配置できるようになります。
(左から「ピボット」、「グローバル」となるようにして、磁石のアイコンをクリック)
プレハブの便利なところは、この多くのMoneyオブジェクトの設定を変えようとした際に、プレハブ側の設定を変えるだけでマップ上のすべてのMoneyオブジェクトも変更されるということです。
プレハブの設定を変える際は、Prefabsフォルダに作ったプレハブをダブルクリックすると編集画面に移ることが出来ます。
これでコインは完成です。
破壊可能レンガの追加
オブジェクトの追加 / スクリプトの編集
次に、下から叩くと壊れるブロックを追加します。

前回(第5回)と同様にタイルのテクスチャファイルを更新し、Sprite Editorからスライスし直します。今回は4種類のレンガのテクスチャを用意しました。

右クリック→「2Dオブジェクト」→「スプライト」→「正方形」から新しくオブジェクトを作成し、

スプライトを適用します。(名前はBreakableBricksとしました)

コライダー(Box Collider 2D)を追加します。

そして、破壊可能かどうかを判別できるようにタグを追加します。タグ→「タグを追加」をクリック。

名前はBreakableとしておきます。
これでブロック側の設定は完了...と言いたいところですが、

こちらもタイルマップ同様に移動キーを押しながら側面に接触すると引っかかりますので、これを修正しておきます。

修正方法は前回(第5回)と同じです。「Platform Effector 2D」を追加して、

「エフェクターで使用」をオンに、「1方向の衝突判定を使用」をオフにするだけです。
あとはPlayer.csを少しいじるだけでOKです。
private void OnCollisionEnter2D(Collision2D collision)
{
GameObject obj = collision.gameObject;
if (obj.CompareTag("InstaDeath"))
{
SceneManager.LoadScene("SampleScene");
}
BoxCollider2D c = GetComponent<BoxCollider2D>();
if (obj.CompareTag("Breakable") && rb.velocity.y == 0 &&
obj.transform.position.y - obj.GetComponent<Collider2D>().bounds.size.y / 2 >= this.transform.position.y)
{
GameObject.Destroy(collision.gameObject);
}
}
OnCollsionEnter2Dを上記のように編集します。
今回はブロックの下からプレイヤーが衝突してきたかどうかの判定を以下の条件を使って実装しました。
(実装方法はこれ以外にもあります。というか正直、この実装の仕方は個人的には納得してませんが、これより良い方法が思いつきませんでした)
衝突対象が「Breakable」のタグを持っている
プレイヤーのY軸方向の速度が0(衝突した瞬間はY軸方向の速度が0となるので)
プレイヤーのY座標が、衝突対象(ブロック)の当たり判定の下端のY座標より小さい
これら3つの条件すべてを満たした際に、衝突対象であるブロックのオブジェクトを削除するようにしています。
これで、破壊可能なブロックの実装は完了です。
プレハブの作成
コインと同様に、ブロックのプレハブも作ります。

その前に、作ったブロックをコピーして色違いも作成しておきます。

先程と同様に、空のオブジェクト(BricksObjects)を作成してブロックをその子オブジェクトとします。

Prefabsフォルダにドラッグアンドドロップでプレハブを作成します。

あとはコピペでマップに配置しましょう。こちらもグリッドスナップを使うと便利です。
今回は、収集可能アイテム(コイン)と下から叩くと破壊できるブロックを追加しました。
言うのを忘れていましたが、3ブロックの高さを超えられるようにジャンプ力を15から17に変えています。
こんな感じで、簡単なギミックなら数行のコードで再現できてしまいます。プログラミング下手な私でも。
ソースコードは前回と同様に、GitHubからコピペまたはダウンロードできます。今回は第6回なので、TutorialGame/6にスクリプトを置いています。
https://github.com/Umagame/TutorialGame
さいごに
今回は以上となります。
一応このシリーズの目的はUnityの使い方を解説することなので、今回のようなギミックを追加する回はこれで終わりです。多分。
次回は、効果音およびBGMの追加とタイトル画面・ゲームオーバー画面の実装を予定しています。
投稿日は未定ですが多分年内には出せます。
↑出せませんでした。
www.umagame.info