ぱふの自由帳

週3更新(火・木・日)を目指すUnityブログ。良ければフォローお願いします(`・ω・´)

【unity2017】コライダーを1つだけ持ったオブジェクト同士の反射をスクリプトで実装する《2D》

はじめに

今回はタイトルに書いてあることを実装していきます。様々な方の意見を頂きつつ実装に至りました。私一人では解決しなかったと思うので未来の誰かの参考になれば幸いです。

実装

実装内容確認

今回使用する2つのオブジェクトを以下に示します。

  • Player
    • Sprite Renderer
    • Rigidbody 2D
    • Circle Collider 2D
  • Obstacle
    • Sprite Renderer
    • Box Collider 2D

Playerオブジェクトを操作し、Obstacleオブジェクトに衝突すると反射するという実装を行っていきます。その前に少しだけ話を....

なぜunityの物理演算を使用しないのか?

ここで、そもそもなぜ自前で実装する必要があるのかという事について少し説明しておきます。

unityの物理演算を用いて反射を実装する(Physics Material 2Dを用いる)と、反発係数を1にしていても以下の様な現象が起こります。(弱い方の力ベクトルが消えてしまう)

f:id:PafuOfDuck:20170902214033p:plain

私は今回、mi●iのモン●トの様な動きをするPlayerを実装したかったので自前で実装する必要がありました。

まず最初にどこで詰まったか?

以下の様な場合、どの辺に衝突したのかということをどの様に見分けるのかで詰まっていました。

f:id:PafuOfDuck:20170902215014p:plain

場合によっては、Box Collider 2Dを持つ子オブジェクトをObstacleに4つほど(4方向分)持たせる方法も出来なくはありませんが、今回ObstacleはMap Editorの様なアセットを使用してタイルとして配置する予定でした。そのアセットでは子オブジェクトをタイルに持たせることが出来ない(出来るかもしれないが私がその方法を理解出来ない)ので断念しました。

最終的に取った方法

様々なアドバイスを頂いた中で、今回はCollision2Dから法線を取得してvelocityと共にVector2.Refrectに渡すという 青木とと(ˊᗜˋ*)さん (@lycoris102)の案で実装していきたいと思います。

他の方々からPhysics2D.Raycastを実装してPlayerからRayを飛ばして当たった向きを調べるという方法も提案して頂きましたが、「Obstacleの角に当たった時どうするの?」と考えていく内にRayの数や太さを考慮するのが大変なのではないかと考え断念しました。(((せっかく頂いたのに...実装すら諦めてしまい申し訳ありません...orz

実装開始

さて、早速Bounce.csを実装していきます。

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

public class Bounce : MonoBehaviour
{
    private Vector2 lastVelocity;
    private Rigidbody2D rb;

    void Start()
    {
        this.rb = this.GetComponent<Rigidbody2D>();
    }

    void FixedUpdate()
    {
        this.lastVelocity = this.rb.velocity;
    }

    void OnCollisionEnter2D(Collision2D coll)
    {
        Vector2 refrectVec = Vector2.Reflect(this.lastVelocity, coll.contacts[0].normal);
        this.rb.velocity = refrectVec;
    }
}

Vector2.Reflectについてですが、私は今回初めて知る機能でした。以下に公式のリファレンスに書いてあった機能をそのまま貼っておきます。

法線を基準にしてベクトルの反射したベクトルを取得します。

このスクリプトをPlayerにアタッチすることでPhysics Material 2Dを用いることなく反射を実装できます。(変な所で反射している様に見えますが、FPSが低いだけです...笑)

f:id:PafuOfDuck:20170902231811g:plain

実は初め失敗した話

興味のない方は飛ばしてしてください。

先ほどのBounce.csでは

void FixedUpdate()
    {
        this.lastVelocity = this.rb.velocity;
    }

というようにFixedUpdate関数内で速度ベクトルをコピーしてOnCollisionEnter2D関数で使用していました。しかし、初めの方はOnCollisionEnter2D関数で速度ベクトルを取得して失敗しました。

こちら(Unityイベント関数の実行順)のサイトで確認できますがイベント関数の実行順は以下の様になってます。

f:id:PafuOfDuck:20170902230257p:plain

InternalPhysicsUpdateが速度の更新箇所になっているため、OnCollisionEnter2Dが実行される時に速度ベクトルをコピーしても既に速度ベクトルは0になってしまっており、全く跳ね返らないという状況になってしまっていました。

ここでも 青木とと(ˊᗜˋ*)さん がご指摘くださって解決しました。ありがとうございました!

おわりに

すごく苦労して沢山の人の意見を参考にさせていただきました。皆様、本当にありがとうございました!また何かありましたらその時はよろしくお願いいたします...(´・ω・`。)

初心者の方や熟練者の方を問わずTwitterのフォローお待ちしています...(`・ω・´) ヨロシクデス!