OnTriggerEnter2DとOnCollisionEnter2Dの違い

OnTriggerEnter2DとOnCollisionEnter2Dの違いが分からん!

更に言うと、OnCollisionExit2DとOnTriggerExit2Dの違い、OnCollisionStay2DとOnTriggerStay2Dの違いも分からん!

っとなったので調べました。2Dについて書いていますが、基本3Dも一緒だと思います。

1.引数が違う

OnCollisionEnter2DとOnTriggerEnter2Dですが、よくよく見ると引数が違います。

void OnTriggerEnter2D( Collider2D other) { ... }
void OnCollisionEnter2D ( Collision2D other ) { ... }

1つ目の違いはここです。

綴りが似ているので最初気が付きませんでした。しかし、CollisionとColliderってどう違うんでしょう?

CollisionとColliderの違い

Collisionは衝突が発生した時の情報を受け渡しするためのクラスのようです。衝突時の衝突点や相対速度、衝撃の強さなどの情報が保持されています。

Unity - Scripting API: Collision


Colliderは、衝突判定をさせるためのコンポーネントでGameObjectにアタッチして使うクラスです。

Unity - Scripting API: Collider

2.使用する場面が違う。

実は使い分けは簡単で、ColliderのisTriggerがオンの時は、OnTrigger〜系のコールバック、オフの時はOnCollision〜系のコールバックを使えばOKです。

ちなみに同時に両方呼ばれることは無いみたいです。どちらかしか呼ばれません。

しかし、ColliderのisTriggerフラグって何なんでしょう?

トリガーってなに?

ColliderはisTriggerというフラグを持っています。これを有効にすると Rigidbody の衝突判定を行わなくなります。

分かりやすい例を挙げます。

f:id:wkpn:20161003120454p:plain

図で、青い四角も赤い四角も、どちらもBoxCollider2Dがついています。さらに青い方には、Rigidbody2Dもついているので、ゲームをスタートすると重力によって落下し始めます。

f:id:wkpn:20161003120504p:plain

isTriggerがオフのときには、下図のように赤い四角の上に青い四角が乗っかります。

f:id:wkpn:20161003120511p:plain

isTriggerをオンにすると、下図のように赤い四角に乗らずにスルーしまて落下し続けます。(OnTrigger系のCallbackは発生します。)

ちなみに、赤い四角、青い四角、どちらか一方だけでもisTriggerを有効にすれば、すり抜けるようになりTrigger系のコールバックが呼ばれるようになります。

まとめ

  1. 衝突して反射するような物理挙動をするオブジェクトに仕込むときはOnCollision〜系のコールバックを使う
  2. isTriggerフラグを立てて、触れたかどうかだけ判断したいようなオブジェクトに仕込むときはOnTrigger〜系のコールバックを使う
  3. それぞれ引数に入ってくる型が違うので注意

【Unity道場】Unity 5.4 & 5.5 新機能キャッチアップ講座に行ってきた。

Unity 5.4 & 5.5 新機能キャッチアップ講座」に行ってきました。

このような回を無料かつ軽食付きで開催してくれるUnity Japan様には頭が上がりません。ありがとうございます。

個人的に気になったポイントと所感をメモ。

Unity5.4

GPU Instancing

  • ライトプルーブの影響をうけられないとか、色々と制約が多いようなので、結局使い所が無いような気も... 何に使うのが良いんだろう?
    • 追記:弾幕シューティングになんか良いんじゃない?というご意見を頂きました。確かにそれは良さそう。

Transform周りがSIMDを利用するようになった

  • 使ってなかったのかぁ。
  • Transform周りだけなのかなぁ。Vector系の演算は全部使うようにできんのだろか?

処理のマルチスレッド化

  • 5.3 -> 5.4 -> 5.5 と徐々に進んでいるらしい
  • メインスレッドとレンダースレッドで分かれているだけ?
  • どこまでスレッドセーフなプログラムを意識すべきなのかが気になる。
  • メインスレッドとレンダースレッドだけなら、そんなに意識しなくても大丈夫そうだけど...
  • プロファイラーでマルチスレッドっぷりを確認できるらしい。

VRのSingle-Pass Stereo Rendering

  • パス変更回数が減るので負荷減
  • 色んなバッファを両眼で使いまわすのかなぁ
  • やったことないけど両眼用の画を作るときってシャドウマップを作るパスは一回しか走らせないのかな?
  • Image Effectは両眼の画に対してまとめてやるので交じる可能性があるらしい

AndroidのIL2CPP対応

  • ファイルサイズはmonoより大きくなるらしい
  • もちろんビルド時間は長い

Light Probe Proxy Volume

  • 初めて知った。
  • ライトプルーブってオブジェクト単位で使用するプルーブを決めるけど、それだと大きいオブジェクトだと破綻しちゃう
  • それを解決するために空間を区切って、区切られた空間単位でプルーブを決めるっぽい。たぶん。

Unity5.5

RigidBody2Dにsimulatedというパラメータが追加される

  • オブジェクトのアクティブ/非アクティブを繰り返すと、内部ではコライダーの生成と破棄をしていて、コストが有った。
  • simulated: offは破棄しないで物理演算から除外出来る
  • オブジェクト生成代わりに、ObjectをPoolしてアクティブ/非アクティブの切り替えることは多々あるので、これは良いことを知った。
  • 早く5.5来て欲しい。

Full Binary Shader Serialisation

  • 消費メモリ減少
  • コンパイル時間減少
  • Shader Binaryってどんだけ互換性あるんだろう?
  • プラットフォーム切り替えが遅くなる?

iOSで新しい画像フォーマットに対応

  • ASTC 6x6 〜 16x16
  • 圧縮率:クオリティ比率はかなり良い
  • ただし、A8以上のチップ。つまりiPhone6以上。
  • それ以下の場合TrueColorになる!
  • って、しばらくは使えないってことじゃない?

ライン&トレイルレンダラーがまともに

  • 今まではクオリティが低くて使えなかったらしい
  • これは使いたい!

その先のUnity

Monoのバージョンアップ

  • 拍手が起きてた
  • Mono4.6、C#6.0 対応
  • まだ何も出てきてないし、正直、遠い未来なのではないかという不安が...

2D

  • Tilemapが追加
  • Z軸でのソート
  • 早く来て欲しい!

Unityで transform.localPosition.x = 1.0f; ができないわけ

C#初心者の私がUnityで最初戸惑ったのはtransform.localPosition.x = 1.0f;というようなことができないことです。

これをやろうとすると、

error CS1612: Cannot modify a value type return value of `UnityEngine.Transform.localPosition'. Consider storing the value in a temporary variable

というエラーが出ます。UnityEngine.Transform.localPositionは変更できないよって言われてます。

なんで、こんなことになるかというと、localPositionが値型で、なおかつプロパティだからです。

Transformの定義を見ると、

public Vector3 localPosition { get; set; }

というようになっています。

これが、プロパティでなく、

public Vector3 localPosition;

という形であれば、localPosition.x = 1.0fは可能です。

また、Vector3が参照型(class)であった場合も、transform.localPosition.x = 1.0f;という風に書くことができます。

localPositionは値型を返すプロパティなので、メモリ上に一時的にできた一時変数が返されてしまうのです。
そこに値を入れたところで何も起きません。なのでエラーになるのです。

C++で考えると、プロパティは以下のような関数があるようなものです。

const Vector3 GetLocalPosition() { return localPosition; }
void SetLocalPosition( const Vector3 pos ) { localPosition = pos; }

そう考えると、Vector3で渡さないといけないのが理解しやすいんじゃないかと思います。

P.S 2018.11.25
自分の理解が浅はかだったので修正しました。

UnityでUnit Testを行う

先日、勉強会でUnity Tests Runnerというのものがあることを知りました。

もともと、Unity Test Toolsというものの一部だったのが、Unity5.3から組み込みになったらしいです。

docs.unity3d.com

というわけで使ってみました。

使い方は簡単で、図のように

f:id:wkpn:20160925160828p:plain

「create」 -> 「Editor Test C# Script」

を選び、スクリプトファイルを作ります。スクリプトはEditorフォルダ以下に置いて置きます。

Editorフォルダ以下に置いておかないと、

The type or namespace name `NUnit' could not be found. Are you missing a using directive or an assembly reference?

というエラーが出ますのでご注意を。

できたスクリプトは、以下のようになっています。EditorTest()の中身がテストコードになっています。

using UnityEngine;
using UnityEditor;
using NUnit.Framework;

public class NewEditorTest {

	[Test]
	public void EditorTest()
	{
		//Arrange
		var gameObject = new GameObject();

		//Act
		//Try to rename the GameObject
		var newGameObjectName = "My game object";
		gameObject.name = newGameObjectName;

		//Assert
		//The object has a new name
		Assert.AreEqual(newGameObjectName, gameObject.name);
	}
}

EditorTestを書き換えて、自分のテストしたい内容に書き換えても良いですが、テスト項目が増えるたびに新たに関数を作ったほうが良いでしょう。頭に[Test]属性をつけておくと、それがテストコードになります。

例えば、自分は以下のようなテストを書いています。ちなみにclass名は何でも良いみたいです。

public class WKLibraryTest {
    [Test]
    public void SwapTest()
    {
        int i1 = 0;
        int i2 = 1;
        Utils.Swap< int >( ref i1, ref i2 );
        Debug.Assert( i1 == 1 && i2 == 0 );
    }

    [Test]
    public void Tuple2Test()
    {
        MyTuple2 tuple_test2 = new MyTuple2( "tuple2", 0 );

        Debug.Assert( tuple_test2.Item1 == "tuple2" );
        Debug.Assert( tuple_test2.Item2 == 0 );
    }
}

できたテストはエディター上から走らせることができます。

「Window」->「Editor Tests Runner」を選びます。

f:id:wkpn:20160925161157p:plain

すると以下のような窓が出てきます。

f:id:wkpn:20160925161214p:plain

「Run All」を押すと、全部、「Run Selected」だと選択しているもの、「Rerun Failed」だと前のテストで失敗したものをテストします。

成功すると以下のように緑のチェックが付き、

f:id:wkpn:20160925161236p:plain

失敗すると赤いチェックが付きます。

f:id:wkpn:20160925161252p:plain

テスト書くの面倒くさいので、あんまりやりたくはないのですが、ちょっとしたオレオレUtilityをチェックするのに使おうと思います。

AudioSourceの再生が終わったかどうかを取得する方法(が見つからない...)

表題の件ですが、結局いまのところ解決していません。

AudioSourceクラスにはisPlayingというプロパティがあります。一番最初に思いつくのは、これがfalseになったら再生が終わったと判断するという手です。

しかし、これには落とし穴があって、Pause()を呼ぶとisPlayingがfalseになるのでPauseしているのと、再生が終わっているのとを区別できません。


次にtimeというプロパティを見るという手があります。こちらが0になっていたら再生が終わったと判断するという手です。

しかし、AudioSourceをPlay()した直後はtimeが0なので、これも誤作動してしまいます。

最後に、上記を併用するという手があります。

public static bool IsFinished( this AudioSource audio )
{
    return audio.time == 0.0f && !audio.isPlaying;
}

このような判定です。

しかし、これもうまくいかないことがあります。Play()した直後にPause()すると、timeは0だしisPlayingはfalseなので再生終了したと判定されてしまうのです。

結局、今のところ良い方法は思いついておりません。再生終了したタイミングでcallbackを呼ぶような仕組みを作りたかったのですが...

何か良い案があればご教授お願い致します。

追記
Pauseせず再生状態のままピッチを0.0fにすればいけるとコメントで頂きました。ありがとうございます。

C#のlock構文

FruitsNinjaのCloneのソースコードを見ていてc#にlock構文があることを知りました。

https://www.assetstore.unity3d.com/jp/#!/content/65879


マルチスレッドのプログラムでは、ちゃんと排他制御をしないと問題がおきます。

その代表格がsingletonクラスで、自分もC++でゲーム作ってたときは、それ周りのバグで悩まされました。

上記のAssetでは、singletonの生成時にロックを掛けるようになっています。

簡略化して書くと、

private static object _lock = new object();

public static T Instance
{
    get
    {
        lock (_lock)
        {
            if (_instance == null)
            {
                _instance = (T)FindObjectOfType(typeof(T));

                if (_instance == null)
                {
                    GameObject singleton = new GameObject();
                    _instance = singleton.AddComponent<T>();
                    singleton.name = "(singleton) " + typeof(T).ToString();

                    DontDestroyOnLoad(singleton);
                }
            }

            return _instance;
        }
    }
}

というような感じになっています。

これをちゃんとやっておかないと、タイミングによってはシングルトンなのにオブジェクトが複数できちゃいます。

けど、そもそもUnityのユーザースクリプトってシングルスレッドで動いていると聞いたので、そんなに神経質にlockしなくても良いんじゃ...って思ったり。そのあたりどうなんでしょう???

全てのシーンに存在し、一つしか存在してはいけないマネージャー的存在の実装

kan-kikuchi.hatenablog.com

この記事を読んで、最近読んだFruitsNinjaをCloneしたアセットのことを思い出したので記事にします。

https://www.assetstore.unity3d.com/jp/#!/content/65879

ところで、このアセット、凄いです!画像変えるだけで、vegetable ninjaでもmeat ninjaでも何でも作れます!
こんなん無料で出すなんて太っ腹すぎる!しかもコードに書かれたコメントがめちゃくちゃ丁寧!
オススメです!

本題に戻ります。
上記の記事で問題として挙げられていることを列挙すると

  1. 全てのシーンに存在して欲しいが、いちいち全てのシーンに配置するのは面倒だし、シーンが変わる度にマネージャーが新しくなるので、シーンを跨いでの処理ができなくなる。
  2. かといってDontDestroyOnLoadを使ってマネージャーが消えないようにすると、シーンを移動した時にマネージャーが重複してしまう。
  3. かといって最初のシーンだけに設置すると、他のシーン単体でテストが出来ない。

ということです。なので、

  1. 全てのシーンに存在し、
  2. 重複せず
  3. シーン単体でのテストができる

ことが重要になります。

FruitsNinjaCloneのスクリプトにあるSingletonは、かなり上記の条件を満たしています。

コードを一部簡略化して載せると、

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T _instance;

    public static T Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = (T)FindObjectOfType(typeof(T));

                if (_instance == null)
                {
                    GameObject singleton = new GameObject();
                    _instance = singleton.AddComponent<T>();
                    singleton.name = "(singleton) " + typeof(T).ToString();

                    DontDestroyOnLoad(singleton);
                }
            }

            return _instance;
        }
    }
}

というような形になっています。

キモは、instanceが無かったらGameObjectを生成して、AddComponentしているところです。

これで、Instanceにアクセスが発生した瞬間にオブジェクトが生成されます。

問題点としては、Instanceへのアクセスが無ければオブジェクトが生成されずEditor上にもオブジェクトが現れないことですが、たいてい、どっかでアクセスがあるし、明示的にInstanceへのアクセスをしてやれば無理やり作ることもできるし、まあまあ良い感じなんじゃないかと思います。



1分間たまご - 激ムズ!

1分間たまご - 激ムズ!

開発元:Ken Watanabe
無料

posted with アプリーチ