Developer

【Unity実践】#17 アセット編 ~ 箱の作成 part2 ~【Boxゲーム】
2021.04.02
Lv2

【Unity実践】#17 アセット編 ~ 箱の作成 part2 ~【Boxゲーム】

今回の内容

今回も箱のアセット適用、調整を行います。
まずは前回の最後に整理した以下の課題を対応して、箱の完成まで進めます。

課題
・箱をタップしたときに、消えるのではなく壊れるようにする。
・壊れた後の箱にコリジョンがある。(プレイヤーが乗らないようにしたい)

※初めての方はこちらから
【第1回記事】この連載について

タップ時に箱が壊れるようにする

まずは課題の1つ目ですが、現状の動きと理想の動きが以下になります。

↓ 現状の動き

↓ 理想の動き

対応の方針としては以下です。

方針
・BoxUnit に、自分自身を壊すメソッドを用意する(public で、外から呼べるように)
・BoxScript で、Destroy していた場所を変更。各子要素の上記メソッドを呼ぶ。

まずは方針の1つ目、BoxUnit のスクリプトを変更しましょう。
BoxUnit には標準で CrashCrateスクリプトが適用されています。

これを開いて、以下の通り Crashメソッドを追加しましょう。
Crashメソッドの処理内容は、OnTriggerEnterメソッドの4行をコピペしています。
ついでに、OnTriggerEnterメソッドはコメントアウトして無効にしておきます。

namespace ArionDigital
{
    using UnityEngine;

    public class CrashCrate : MonoBehaviour
    {
        [Header("Whole Create")]
        public MeshRenderer wholeCrate;
        public BoxCollider boxCollider;
        [Header("Fractured Create")]
        public GameObject fracturedCrate;
        [Header("Audio")]
        public AudioSource crashAudioClip;

        // コメントアウト
        //private void OnTriggerEnter(Collider other)
        //{
        //    wholeCrate.enabled = false;
        //    boxCollider.enabled = false;
        //    fracturedCrate.SetActive(true);
        //    crashAudioClip.Play();
        //}

        // このメソッドを追加
        public void Crash()
        {
            wholeCrate.enabled = false;
            boxCollider.enabled = false;
            fracturedCrate.SetActive(true);
            crashAudioClip.Play();
        }

        [ContextMenu("Test")]
        public void Test()
        {
            wholeCrate.enabled = false;
            boxCollider.enabled = false;
            fracturedCrate.SetActive(true);
        }
    }
}

次に BoxScript を以下の通り変更します。

using ArionDigital;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BoxScript : MonoBehaviour
{
    public enum PlayerMove { Default, MoveLeft, MoveRight }

    public PlayerMove playerMove;

    PlayerScript player;

    void Start()
    {
        player = GameObject.Find("Player").GetComponent<PlayerScript>();
    }

    public void onBoxClick()
    {
        // プレイヤーの移動を開始する
        switch (playerMove)
        {
            case PlayerMove.MoveLeft:
                PlayerMoveLeft();
                break;
            case PlayerMove.MoveRight:
                PlayerMoveRight();
                break;
        }

        // 子オブジェクト(箱)を全て破壊
        foreach (Transform unit in transform)
        {
            unit.gameObject.GetComponent<CrashCrate>().Crash();
        }

        // 破片が残るので消滅させる
        Destroy(gameObject, 1.0f);
    }

    // 初期配置で右に移動させる場合に使う
    void PlayerMoveRight()
    {
        if (!player.isMove)
        {
            player.MoveRight();
        }
    }

    // 初期配置で左に移動させる場合に使う
    void PlayerMoveLeft()
    {
        if (!player.isMove)
        {
            player.MoveLeft();
        }
    }
}

スクリプトで押さえておきたい点としては、33行目の foreach です。
Transformオブジェクト に対して foreach をかけると、子要素の Transformオブジェクトを取得できます。
今回の様に、子要素全てを取得して操作したい時には便利だと思います。

これでシーンを再生すると、以下の通り目的の動作となります。

壊れた後の箱のコリジョンを無くす

前回最初に箱を置いたときにも確認しましたが、以下のような配置で箱を壊すと、
壊れた後の箱とプレイヤーが衝突してしまいます。

これを解決するために、以下の対応を行います。

壊れた後の破片のレイヤーは、プレイヤーに衝突しないようにする。

まずはプレイヤーにレイヤーを設定します。
プロジェクトビューで Prefabs > Player を選択して下さい。

新規レイヤー「Player」を追加して設定します。

プレイヤーと同様に、箱の破片にもレイヤーを設定していきます。
BoxUnit > Crash_Crate_Fractured_LOD_1 を選択して下さい。

新規レイヤー「Crash_Crate_Piece」を追加して設定します。

同様に、BoxUnit(1)、BoxUnit(2)、BoxUnit(3)の「Crash_Crate_Fractured_LOD_1」のレイヤも、
「Crash_Crate_Piece」としてください。

「Crash_Crate_Piece」レイヤーの衝突判定を、「Crash_Crate_Piece」に限定する設定を行います。
Edit > Project Settings を開いてください。

Physics の項目で、衝突判定を以下画像の通り設定してください。

これでプレイヤーと壊れた箱との衝突が無効となります。

Box_Wall に触れると反転するようにする

Box_Wall はプレイヤーが衝突すると反転する必要があります。
プロトタイプの時と同様に Box_Wall に Wallタグを設定しましょう。

さらに、右側の Box の BoxScript で、PlayerMove を 「MoveRight」として確認してみます。

黄色い箱を壊すとプレイヤーが移動を開始しますが、Box_Wall をすり抜けてしまっています・・

初歩的なことで、コライダーの設定を行っていないのが原因です。
今回は、子要素である BoxUnit に沿ったコライダーを取得したいので、以下の設定を行います。

1.BoxUnit の BoxCollider(既存)のIsTriggerを外す。
2.Box_Wall に Rigidbody を追加する。
3.Rigidbody は Use Gravity を無効、Is Kinematic を有効に。


この対応の補足ですが、衝突するだけであれば1.の子要素のコライダー設定だけで済みます。
ただ、Wallタグを親要素の設定しているので、この判定を有効にするためには親要素側で衝突判定を持つ必要があります。
親要素にコライダーを設定する方法もありますが、今回のように子要素の形状に合わせたコライダー設定としたい場合、
親に Rigidbody を設定することで、子オブジェクトの形状に沿ったコライダーを親も持つような状態となります。
これによって、親に付いた Wallタグが有効となります。

Rigidbody の「Use Gravity」を無効、「Is Kinematic」を有効にしているのは、
後に作成する Box が落下してしまう問題が起きるので、その対策となります。
(参考)【Unity連載】はじめに知っておくべきRigidbodyコンポーネントの概要

再生すると以下の通り、衝突して反転もするようになります。

プレハブ化する

これでおよそ対応は完了となるのでプレハブ化を行いましょう。
プロトタイプの Box_Wall は上書きしてしまって構いません。(不安ならリネームして退避させましょう。)

最後に、水平に置くためのBoxプレハブを作成します。
紛らわしいのでシーンにある既存のBoxは削除します。

↓ 削除後の状態

作成手順ですが、Box_Wall を複製して、名前を Box とします。
タグの Wall は削除(Untagged に設定)して、位置を「0, 0, 0」としておきます。

子要素のBoxUnit(1)、BoxUnit(2)、BoxUnit(3)の位置を、それぞれ以下に設定してください。

親の位置を調整して、PlayerMove は 「Default」としておきましょう。

以下の様に、問題無く再生できていれば成功です。

これで完成となるので、Boxもプレハブ化します。(プロトタイプのBoxを上書きします。)

これで箱のプレハブが2つ完成したので、今後はこちらを必要に合わせて配置していきます。

Scene2, Scene3 の配置を調整

今回は最後に、GameScene2 や GameScene3 への影響が無いかの確認と調整を行います。
GameScene2 については以下の様になっており、
特に問題は無さそうです。

GameScene3 については以下の様になっており、
こちらは右側の Box の調整が必要そうです。

右側の Box の位置を以下の通り設定すると、元と同様の配置となります。

最初の黄色い箱の時は、z軸周りに回転させて水平床を作っていたのですが、
変更後の box では子要素(箱1つ)を横に並べる形に調整をしたので、ズレが生まれていました。

今回はまだステージ数が少ないのでこれくらいの変更で済みましたが、
これが100ステージとか作成したあとだと調整が非常に大変になるので、変更の際には注意が必要です。

おわりに

箱の作成はこれで完了となります。
実際のソースではもう少し細かい点も調整していたりするので、一部紹介だけしておきます。

・箱を色分けできるように
・壊れるときの音を、1つのオブジェクトだけ再生するように(今はBoxUnitごとに再生している)

例えばこの辺を調整していたりするので、余裕のある方は練習がてら対応してみて下さい。

次回はトラップにアセットを適用する予定です!

 
 

連載目次リンク

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

関連する連載リンク

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

© Unity Technologies Japan/UCL