Developer

【初心者Unity】懐かしのゲームを作ってみよう!⑪
2022.01.31
Lv1

【初心者Unity】懐かしのゲームを作ってみよう!⑪


はじめに

今回は前回に引き続き、↓のゲームを作っていきます。

前回、Prefabを使って効率よくBlockを複製することができました。

これにてBlockは完成!と言いたいところですが、おやおや、動かしてみると何やら変な動きをします。なぜこのような変な動きをしてしまうのか?これを解消するにはどうすれば良いのか?今回はこれらを解決しつついよいよBlockを完成してしまいます!


どのあたりの動きが変なのか

それではどの様な動きをしているのか実際に再生してみましょう。

。。。いかがでしょうか?どの辺が「変な動き」かお気づきでしょうか?
これは、第9回で決めた仕様に対して変な動きをしています。
第9回で決めた仕様をおさらいすると↓の様になります。

BallがBlockに衝突すると
・Ballのy軸方向の動きが反対になる
・Ballのx軸方向の動きが反対になる

上の仕様から考えると、Blockに衝突した後のBallの動きが変です。Blockに衝突後、本来Ballは進行方向と反対方向に進まなければならないところを、何と突き進んでしまっています。さらによく見るとある1つのBlock(上の真ん中のBlock)対しては跳ね返っています。
なぜこんなことが起こっているのか突き止めてみましょう。

変な動きの原因追及

変な動きをご確認いただいたところでいよいよ、なぜそのような動きをしているのか原因を探っていきましょう。
しかし原因を究明するにも、要素がいくつもあってどこを当たっていけばよいのか少々悩ましいですよね。
そんな時は、「どの動きがおかしいのか」を見つけ、「本来こうあってほしいという動き」のプログラムが書かれている場所を確認すると良いです。
今回は上のセクションで「どの動きがおかしいのか」は突き止めることができました。
そうです、「BallがBlockを突き抜けて進む」ことがおかしかったのですね。
それでは、「本来あってほしい動き」はどのような動きでしょうか?
そうです、「BallがBlockに衝突すると跳ね返る動き」をしてほしいのでしたね。
実はこの動き、前々回の記事で実装しました。
さあ、どのオブジェクトのどの辺りに実装したでしょうか。
思い出していただけましたか?こちら、Ballのこのあたりです。

public class BallScript : MonoBehaviour
{
    Vector2 pos;
    int directionX;
    int directionY;
    // Start is called before the first frame update
    void Start()
    {
        pos = transform.position;
        directionX = 1;
        directionY = 1;
    }

    // Update is called once per frame
    void Update()
    {
        //斜め45度に進ませる
        Move();

        //右端に着いた時の処理
        if (IsRightEdge())
        {
            directionX = -1;
        }

        //左端に着いた時の処理
        if (IsLeftEdge())
        {
            directionX = 1;
        }

        //上端に着いた時の処理
        if (IsUpperEdge())
        {
            directionY = -1;
        }

        //下端に着いた時の処理
        if (IsLowerEdge())
        {
            directionY = 1;
        }
    }

    void Move()
    {
        pos.x += 0.03f * directionX;
        pos.y += 0.03f * directionY;
        transform.position = pos;
    }

    bool IsRightEdge()
    {
        return pos.x > 2.5;
    }
    
    bool IsLeftEdge()
    {
        return pos.x < -2.5;
    }
    
    bool IsUpperEdge()
    {
        return pos.y > 5;
    }
    
    bool IsLowerEdge()
    {
        return pos.y < -5;
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.name == "Paddle")
        {
            directionY = 1;
        }
        if (collision.gameObject.name == "Block")
        {
            directionX = -directionX;
            directionY = -directionY;
        }
    }
}

この部分の意味をよく思い出していただきましょう。
「もしBallがBlockに衝突したら、x方向とy方向を逆にする」
これではまだ何が問題なのか見えてきませんね。もう少し厳密に言語化してみましょう。
「もしBallが「Block」という名前のオブジェクトに衝突したら、x方向とy方向を逆にする」
お分かりいただけましたでしょうか?
上のif文の条件が「collision.gameObject.name == “Block”」となっているので、「衝突したオブジェクトの名前」を確認しています。
ここで再度、複製したBlockたちを確認してみましょう。

名前をよく見ると、「Block(番号)」というように余計な番号がついてしまっていますね。
これによってこれらのBlockの名前は「Block」とは別物と考えられてしまい、「もしBallが「Block」という名前のオブジェクトに衝突したら」という条件から漏れてしまいます。
ここまで分かっていただいたところで、先程の変な動きの動画を見ていただきますと、Ballがある1つのBlockに対しては跳ね返っていた理由もお判りいただけますよね?
そうです、Blockたちの中に1つだけ、正真正銘「Block」という名前の子がいたからです。その子に対してだけはこの条件がtrueになっていたのですね。

変な動きの修正

変な動きの原因が判明したところで、いよいよこの問題を修正して行きましょう。
この様な場合、方法は2種類あります。
オブジェクトを修正するか、プログラムを修正するか、です。
正直、オブジェクトを修正する方が感覚的で分かりやすいです。
だってそうですよね?Blockたちの名前を全て「Block」にしてしまえば良いではないですか(笑)
しかし今回はあえてこの方法を使いません。理由としてはゲームオブジェクトの名前が重複していると少々面倒ごとが起こることがあるからです。
したがって、ゲームオブジェクトの名前の重複は避けるのが一般的です。
そこで今回は、プログラムの修正という手段をとります。
さあ、プログラムの修正ということですが、これまでは名前を元に他のオブジェクトとBlockを判別していましたがその方法を変えるということになります。
名前以外の他にBlockたちの共通点はあるのか。。。まあいっぱいありますが、今回は程よいものを付加しましょう。その名もtagです。
tagとはその名の通り、オブジェクトに付けるもので、それをつけることであるオブジェクトを他のオブジェクトと差別化することができます。
例えると、服屋でそれぞれの洋服についているタグ(値札)の様なものです。同じ商品には常に同じタグが付き、それらをバーコードリーダーに通すと同じ価格が表示されますよね?
今回は「Block」という名前のtagを作成し、それをBlockたちにペタペタ貼り付けていきます。
そして、if文の方では「もしBallが「Block」という名前のtagのついたオブジェクトに衝突したら」という条件に変更します。
そこでまずはtag付けからしていきましょう。
tag付けの対象ですが、今回はBlockが6個あるので6個分つければ良いのか。。。と思いきや、何とこれ一発でできます!
そう、これら6つのBlockの元となったPrefabに付けることで何と6つのBlockに反映されるのです!
それではPrefabへのtag付けの方法は、tagを付けたいPrefabを選択し、Inspecter上の「Tag→Add Tag」と進みます。

次にTagsの中の「+」をクリックし、名前を入力し「Save」をクリックします。
今回のtag名は分かりやすく「Block」にしておきましょう。

これで完成したかのように見えますが、実はまだtagを作成しただけで適用できていません。
後は、再度Prefabを選択し、「Block」tagを適用して完了です。
適用の仕方はPrefabのInspecter上にあるTagの中にある「Block」を選択します。

次に、プログラムの修正を行います。実はこの修正、とっても簡単。
これまでは名前(name)で判別していたものをtagで判別すれば良いだけなので、nameをtagに変更して終わりです。

public class BallScript : MonoBehaviour
{
    Vector2 pos;
    int directionX;
    int directionY;
    // Start is called before the first frame update
    void Start()
    {
        pos = transform.position;
        directionX = 1;
        directionY = 1;
    }

    // Update is called once per frame
    void Update()
    {
        //斜め45度に進ませる
        Move();

        //右端に着いた時の処理
        if (IsRightEdge())
        {
            directionX = -1;
        }

        //左端に着いた時の処理
        if (IsLeftEdge())
        {
            directionX = 1;
        }

        //上端に着いた時の処理
        if (IsUpperEdge())
        {
            directionY = -1;
        }

        //下端に着いた時の処理
        if (IsLowerEdge())
        {
            directionY = 1;
        }
    }

    void Move()
    {
        pos.x += 0.03f * directionX;
        pos.y += 0.03f * directionY;
        transform.position = pos;
    }

    bool IsRightEdge()
    {
        return pos.x > 2.5;
    }
    
    bool IsLeftEdge()
    {
        return pos.x < -2.5;
    }
    
    bool IsUpperEdge()
    {
        return pos.y > 5;
    }
    
    bool IsLowerEdge()
    {
        return pos.y < -5;
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.name == "Paddle")
        {
            directionY = 1;
        }
        if (collision.gameObject.tag == "Block")
        {
            directionX = -directionX;
            directionY = -directionY;
        }
    }
}

これで早速実行してみましょう!

いかがでしょうか?変な動きが直ったのではないでしょうか?

おわりに

これにて無事、6つのBlockが完全に完成しました!
いやはや、長かったですね。。。
これにて本シリーズを締めくくってしまっても良いのかもしれませんが、せっかくなので最後までゲームっぽく行きましょう。
やはりゲームといったらゲームをクリアした時に画面に現れる「Game Clear」の文字とゲームオーバーの時に画面に現れる「Game Over」の文字ですよね?(笑)
ということで次回はそれらを実装します!
乞うご期待!

 


連載目次リンク

「初心者のための」Unityゲーム制作 目次

Unity実践編 - 目次リンク

実践Unityゲームプログラミング 連載目次