【Unity/2D】Flappy Bird風ゲームを作ってみる話 #5 オブジェクトを破棄するエリアの作成

記事をご覧いただき、誠にありがとうございます。

投稿主の無能です。

前回は、土管を動かすスクリプトを作って、土管が左へ動くようにしました。

今回は、オブジェクトを破棄するエリアを作っていきたいと思います。

オブジェクトを破棄するエリアとは?

オブジェクトを破棄するエリアってどういうこと?と思われるかもしれませんが、結構重要な事です。

例えば調べ物をしているときに、ブラウザを使って検索したとします。

そして複数のタブを開いた状態で、また分からない語句が出たとして、またタブを開いて検索します。

…と繰り返すうちに、タブを何十枚も開いていて、(お使いのブラウザによって)ブラウザの処理速度が滅茶苦茶遅くなります。

ブラウザをUnity、タブをオブジェクトとすると、同様の事象がUnityにも起こります。

ですので、不要になったオブジェクトは、適宜破棄しないとゲームが重くなってしまうわけです。

オブジェクトは、生まれたら存在し続けますので、明確な処理や条件が無いと、ずっとゲーム内に存在し続けるので、ゲームでの役目を果たしたら、処理(破棄)しなければなりません。

それが作ろうとしている「オブジェクトを破棄するエリア」になります。

エリアを作る

DestroyAreaを作成

では破棄のエリアを作っていきます。

不要になったオブジェクトを破棄する方法は幾つかありますが、無能は破棄エリアを作って、オブジェクトが破棄エリアと接触したらそのオブジェクトを破棄する、という形を取りたいと思います。

ではまず破棄エリアを作ります。

Unityで空のオブジェクトを作成します。

空のオブジェクト(Create Empty)を作成する

そのオブジェクトの名前を「DestroyArea」とします。

「DestroyArea」にリネーム

子オブジェクトを追加する

そしたら、このオブジェクトに子オブジェクトを追加します。

Create Empty Childで追加できます。

追加できたら、名前を「Area_North」にします。

DestroyAreaに子オブジェクトを追加

追加した子オブジェクトの名前を「Area_North」にする

これが北側の破棄エリアになります。

Area_Northオブジェクトに「接触したら破棄する」という処理をアタッチしたいと思います。

接触したら破棄、という処理をこなしてくれるコンポーネントは何でしょうか?

そうですね、当たり判定が必要なのでColliderになります。

本記事では、棒状のColliderを四方に敷いて、そこを破棄エリアとします。

という事で、このオブジェクトにBox Collider2Dをアタッチします。

InspectorビューのAdd Componentボタンから、Box Collider2Dをアタッチしてください。

Area_NorthにBox Collider2Dをアタッチ

Box Collider2Dをアタッチ

アタッチできたら、Is Triggerにチェックを入れます。

Is Triggerにチェックを入れる

このIs Triggerにチェックを入れるとどんな変化が現れるのかと言うと、まずチェックしたオブジェクトをすり抜けるようになります。
でも「接触した」という判定は持っているようになります。

例え話をすれば、現実世界で目の前に壁があるとします。

壁は普通であれば、触ることもできるし、風やその他のものから防いでくれますね。

その壁がIs Triggerにチェックを入れると、当たり判定だけを持ち、壁の役割である触ることも、他のものも素通りさせるようになります。

簡単に言うと、壁という外観のイメージは維持しつつ、通過できるようになるわけです。

別のイメージを話すと、駆けっこで一位の特権である「ゴールテープを切る」が分かりやすいかと思います。

何故Is Triggerにチェックを入れるという作業が必要になるかというと、このチェックによって、処理が変わるからです。

二つのオブジェクトがあったとして、どちらにもIs Triggerにチェックを入れない状態だと、処理内容は「Collider同士が接触した」という接触判定になります。

この場合は「OnCollisionEnter」という処理になります。

Is Triggerにチェックを入れたオブジェクトが、接触するどちらか一方にある場合は、OnCollisionEnterではなくTriggerがあるので「OnTriggerEnter」という処理になります。

このように処理が分かれるわけです。

接触判定の判断の仕方
  • Colliderの接触・被接触オブジェクトのどちらか一方、または双方にIs Triggerにチェックがある場合→OnTriggerEnter(2D)
  • Colliderの接触・被接触オブジェクトの双方にIs Triggerにチェックが入っていない場合→OnCollisionEnter(2D)

これを四方に隙間なく配置して、不要なオブジェクトの破棄を行います。

Area_Northを北側に配置する

それでは北側に配置しましょう。

Box Collider2DのOffsetで位置を調整します。

視認できないので、Edit Colliderボタンを押しておき、Colliderが視認できるようにしておいて、Offsetで高さを調整します。

Area_Northに土管が接触しないように注意して、余裕のある高さにします。

Edit Colliderボタンを押してColliderを視認できるようにしておく
OffsetのYで高さを変更

Offset調整後のCollider

では次に、Sizeを変更して横に長く伸ばします。

SizeのXを変更し横に長く伸ばす

Size変更後のCollider

これで北側が出来ました。

この作業を他の三方で繰り返し、四方を囲んでいきます。

Collider同士は重なっても問題ありません。

ではArea_Northを複製して三方を作りましょう。

Area_Northを複製しリネーム

Area_North

Area_South

Area_East

Area_West

四方を囲むことができた(Colliderは重なってもOK)

後でCanvasを追加するので、その時にまた位置やサイズを調整します。

ではプレイヤーにもColliderをアタッチします。

プレイヤーにColliderをアタッチ

プレイヤーにもColliderをアタッチします。

接触・被接触オブジェクトの双方にColliderが無いと接触判定はできません。

この辺りは忘れがちになるので、接触判定が出来ないときは、慌てずに確認するようにしてください。

プレイヤーは今回めたんの画像を設定したので、複雑な形状になっています。

複雑な形状のものにColliderをアタッチしたい時は、Polygon Colliderが最適です。

という事で、Polygon Collider2Dをアタッチします。

InspectorビューのAdd ComponentボタンからPolygon Collider2Dを選択

Polygon Collider2Dをアタッチ

あれ?Colliderが反対を向いてる…

Colliderが反対を向いている状態

Sprite RendererのFlipは画像にのみ効果があるので、その他のコンポーネントには影響しないようです。

ですので、Sprite RendererのFlipのチェックを外します。

Sprite RendererのFlipのチェックを外す

チェックを外したら、TransformのScaleのXを-1にします。

TransformのScaleのXを-1にする

これでオブジェクトの向き・Collider共に右を向いてくれました。

オブジェクトの向き・Collider共に右向きになった

ではスクリプトを書いていきます。

スクリプトを書いてオブジェクトを破棄する機能を実装する

後はスクリプトを書いて、Colliderに接触したオブジェクトを破棄する機能を付けていきます。

一つ注意点があって、プレイヤーがColliderに接触した時の処理になります。

土管オブジェクトは、この後土管を生成するスクリプトを書く予定なので破棄して構いませんが、プレイヤーは破棄してしまうと、ゲームを再起動してやらないと生成できません。

ですので、プレイヤーは破棄せずに非アクティブにする、という方法でゲーム画面から姿を消してもらうようにします。

土管はオブジェクトの破棄で、プレイヤーはオブジェクトを非アクティブ化、という違いがあるので注意しましょう。

オブジェクトのタグ付け

ではプレイヤーを判別するためにオブジェクトをタグ付けします。

プレイヤーのオブジェクト名の直下にタグの項目があるので、リストの中から「Player」を選択します。

タグの項目の中から「Player」を選択

Playerというタグが付いた

これでプレイヤーとそれ以外のオブジェクトを区別できるようになったので、スクリプトを書いていきます。

スクリプトの作成

今回は新たに二つのスクリプトを作成します。

一つ目は「ObjectDestroyer」として、四方のColliderのオブジェクトにアタッチします。

「ObjectDestroyer」スクリプトを新規作成

四方のColliderのオブジェクトにアタッチ

二つ目は「Variables」とします。

「Variables」スクリプトを新規作成

このスクリプトは何処にもアタッチせず、所謂「変数や定数の倉庫」的な役割になります。

スクリプトで共通で使われる変数や定数を、このVariablesスクリプトに格納しておくことで、スクリプトのマジックナンバー化の防止や可視化の度合いを上げてくれます。

あくまで共通の変数や定数の倉庫なので、そのスクリプト内で使う変数や定数はそのスクリプト内に記述します。

Variablesスクリプトは、具体的にはタグの名前や0などの定数など、作成する全スクリプトで共通のもののみを格納する、という役割になります。

それでは準備ができたので、Variablesスクリプトから見ていきましょう。

Variables.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Variables : MonoBehaviour
{
    public const float zero = 0;                                // ゼロの定数

    public static string tag_Player = "Player";                 // タグ:プレイヤー
}
では見ていきましょう。

まず、見慣れないconstですが、定数というものになります。

定数

変数が「変わる数」なのに対し、定数は「定まった数」になります。

定数はコンパイル時に既知である必要があるので、宣言時に同時に初期化する必要があります。

const 定数名 = ○○;

という感じですね。

定数についての詳細はMicrosoft Learnを参照してください。

ゼロの定数は結構あちこちで使う機会があるので、Variablesスクリプトを作る時は一緒に作ってしまっておいた方が後々楽ができると思います。

その他の変数

後は先程タグ付けした、プレイヤーのタグの名前になります。

このようにVariablesスクリプトには共通の変数や定数を格納していきます。

では本題となるObjectDestroyerスクリプトを見ていきましょう。

ObjectDestroyer.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ObjectDestroyer : MonoBehaviour
{
    #region OnTriggerEnter2D
    // 接触判定
    void OnTriggerEnter2D(Collider2D collision)
    {
        // 接触オブジェクトのタグがPlayerの場合
        if (collision.CompareTag(Variables.tag_Player))
        {
            // 接触オブジェクトを非アクティブにする
            collision.gameObject.SetActive(false);
        }
        // それ以外の場合
        else
        {
            // 接触オブジェクトを破棄する
            Destroy(collision.gameObject);
        }
    }
    #endregion
}
それでは見ていきましょう。

OnTriggerEnter2D関数

Colliderのどちらか一方にでもIs Triggerにチェックが入っていたら、OnTriggerEnterになります。

それで接触判定を行うので、OnTriggerEnter2Dを呼んでいます。

引数はcollisionになります。

このcollisionを基に、接触したオブジェクトの情報を取得していきます。

最初に、条件分岐で接触したオブジェクトがPlayerというタグを持っている場合と、それ以外に分かれます。

Playerというタグを持っている場合、つまり接触オブジェクトがプレイヤーだと判明した場合は、接触したオブジェクトを非アクティブにします。

それ以外の場合、つまり接触したオブジェクトの持っているタグがPlayer以外の場合は、接触したオブジェクトを破棄します。

これで想定の処理になってくれるか動作確認します。

動作確認

想定の処理は、接触したオブジェクトが
  • プレイヤー→非アクティブになる
  • プレイヤー以外(土管)→破棄される(ゲームから存在が破棄される)
という処理になります。

ではゲームを実行して確認しましょう。

土管オブジェクトがDestroyAreaに接触して破棄されました。

Sceneビュー

Hierarchyビューを確認すると、土管オブジェクトの存在はありませんので、無事に破棄されました。

Hierarchyビュー

Playerの方も確認します。

PlayerはDestroyAreaに接触すると、非アクティブになりました。

Sceneビュー

Hierarchyビューを確認すると、想定通り非アクティブになっています。

Hierarchyビュー

これで想定通りの処理ができました。

まとめ

今回は
  • DestroyAreaを作成し、接触したオブジェクトが破棄されるエリアを作成した
  • PlayerにColliderをアタッチして、接触判定が可能になるようにした
  • スクリプトを書いてオブジェクトの破棄・非アクティブ化が可能になった
という事をやりました。

次回は、土管を一定間隔で生成する仕組みを作りたいと思います。

では、また次回!

コメント