FatherScorpion's diary

年1更新!...できたらいいなぁ。

Unityで破壊可能オブジェクトを作ったお話

はじめに

この記事は東京高専プロコンゼミAdventCalendar② 14日目の記事です。

adventar.org

①もあります。

adventar.org

ちなみに筆者はブログを書くのは初めてです。変なところがあったら笑って見て下さい。

 

破壊可能オブジェクト?

本題に入りますが、皆さんは破壊可能オブジェクト(と私が勝手に言っている物)をご存じでしょうか?読んで字の如く、破壊できるオブジェクトの事ですね。ゲームや何かでよく見かける事かと思います。

破壊可能オブジェクトが出てくるゲームは数多くあると思いますが、私が真っ先に思い浮かべるのはレインボーシックスシージ(R6S)です。一応説明するとR6Sは銃で撃ち合うFPSゲームです。大きな特徴として殆どのオブジェクトを破壊する事ができるのですが、まあほんとに何でも壊すことができます。

f:id:FatherScorpion:20191214150152j:plain

花瓶などの小物やプレイヤーが設置するバリケードなんかは勿論、床や壁なんかもバンバン粉砕できます。

自分はプロコンが終わった直後にR6Sを買い、試験そっちのけで一日中プレイする自堕落な日々を過ごしていました。

また、そのころ弊ゼミ自由部門ではUnityを使っていたこともあり、Unityをある程度使えるようになっていたので、気分転換がてら自主製作の簡単なゲームを作ろうとしていました。

で、面白いゲームを作るには何が必要かな~などと考えていたのですが、まあここまで前振りをしていたら答えはもう自明ですね。破壊です。

破壊できるキューブを作ろう

最初はオブジェクトを破壊する爆弾を作ろうかと思っていましたが、その前にまず壊される側のオブジェクトが必要だなと思ったのでそれを作ってみました。

最初は単純に、あらかじめ細切れにしておいたキューブを用意して、それに別の物体が衝突すれば上手いこと弾け飛ぶのではないかと考えていました。

 f:id:FatherScorpion:20191213160604j:plain

今回は1×1×1の立方体を10×10×10個敷き詰めます。Unity世界では大きさ1=1mだったと思うので大きめの部屋くらいのサイズはありますね。スクリプトを組んで生成したので隙間なくギッチリ埋まっていると思います。

準備が出来たので早速配置をしてみますが、ここで問題発生です。

  f:id:FatherScorpion:20191213162351j:plain

はい、無事破壊できましたね()。

地面から少し浮かせた地点に先ほどの集合体を配置したところ、地面に衝突した数秒後にこうなりました。しかしまあこれは想定内ではあります。では接地した状態で配置するとどうなるかといえば、

f:id:FatherScorpion:20191213162922p:plain

こうなります。いくら接地した状態とはいえ、重力なりなんなり何かしらの力が働いている以上物体は完全に静止するまで少し時間がかかります。そして1000個ある内の1個でもキューブが動いてしまえば、連鎖的に崩壊が始まってしまいます。

また、最下段にある比較的静止した状態のキューブに注目すると、うっすらと、いえ、がっつりとキューブ間の隙間が見えてしまっています。

確かにこの方法でも調整すれば壊れるオブジェクトは作成できるでしょうが、いかんせん自壊した感が強く、自分で破壊したという達成感、エクスタシーを得ることが出来ません。別の方法を模索する必要がありそうです。

衝突検知のスクリプトを組む

恐らく、世にあるゲームの破壊可能オブジェクトは最初からバラバラに分かれている訳ではなく、何かしらの判定を検知してバラバラになっています(と勝手に思っています)。なので、そんな挙動を再現するプログラムを書いてみようと思います。

内容を大まかに説明すると、

  1. 10×10×10のキューブを用意する。これにスクリプトをアタッチする。

  2. 物体の衝突を検知する。

  3. キューブを削除し、さっきまで使っていた細切れキューブを同じ場所に出現させる。

これで見かけ上はキューブが破壊されたように見える筈です。 早速ですが以下がコードとなります。C#です。

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

public class Flagments : MonoBehaviour
{
    public void SummonFlagments()
    {
        GameObject flagment = (GameObject)Resources.Load("Flagments");
        Instantiate(flagment, this.transform.position + new Vector3(-4.5f, -4.5f, -4.5f), this.transform.rotation);
    }
    private void OnCollisionEnter(Collision collision)
    {
        if (!collision.gameObject.CompareTag("Ground"))
        {
            SummonFlagments();
            Destroy(this.gameObject);
        }
    }
}

関数名とかは超適当ですが許して下さい。内容は殆ど前述の通りですね。細切れキューブをプレハブにしておいて、衝突を検知したらそいつを呼び出すようにしてあります。後は衝突判定をGroundタグが付いたオブジェクト以外にしてあります。このGroundタグはあらかじめ地面に付けてあります。地面に触れた瞬間弾け飛んでは意味が無いですからね。

最後にこいつを10×10×10キューブにアタッチするだけです。では適当な物体をぶつけてみたいと思います。

f:id:FatherScorpion:20191214184937j:plain

ちょうどいい場所にサイコロがあったのでこいつを発射します。過去に自分が、

blender-cg.net

こちらのサイトを参考に作りかけのまま放置していた物体です。マテリアルが適応されていない(作っていない)ので真っ白ですね。適当にスペースキーを押したらキューブにサイコロが放たれるスクリプトでも書くとします。コードは割愛させて頂きます。

いざ発射

以下の動画が全てを語ってくれていると思います。

非常に気持ち良く吹き飛んでくれたかなと思います。

という訳で記事の内容は終わりです。いずれ細切れキューブのプレハブ生成も自動化して、あらゆる大きさのオブジェクトにも対応させたりしたいですね。

おしまい