OnTriggerEnter2Dで接触判定がたまに無視されてしまう
- 評価
- クリップ 0
- VIEW 2,665
スマホ用のパズルアプリを制作しております初心者です。(Unity5.6使用)
パズルピースをドラッグドロップで、
正解の位置のスロットにはめ込むというゲーム内容です。
(70個程のパズルピースがあります)
OnTriggerEnter2Dで接触判定しております。
パズルピースと正解スロットとの接触があった場合、以下の3つの命令を同時に実行しております。
- パズルピースオブジェクトの一部分をDestroy
- スロットオブジェクトの添付スクリプトをDestroy
- パズルピースオブジェクトの添付スクリプトをDestroy
問題点
たいていの場合、3つの命令は問題なく作動するのですが、
だいたい数十回に1回ほどですが、接触判定後の上記の3つの命令が全て無視されます。
その他の問題の詳細
- PC上(MAC)でもスマホ実機上でも起こります
- パズルピースが70個のシーンでこの問題が起こり、パズルピースが10個のみのシーンでは起こらないようです。
- パズルピースのRigidbodyの「Sleeping Mode」を「Start Awake」にしても「Never Sleep」にしても起こります
無視された場合は、パズルピースをもう一度ドラッグし直して、
再び同じ位置のスロットにはめ直しをすると、3つの命令は実行されます。
対策(やってみた事)
接触判定が無視されてしまう事に対しての対策として、
FixedUpdate内にも、接触判定時と同様の上記の3つのの命令を入れました。
(もし削除希望のオブジェクトが残っていた場合に、実行されるように設定しました。)
※常時FixedUpdate内で判定を連続するとメモリ消費が大きいと思われるので、
フラグがtrueの時のみに判定を実行されるようにしております。
(ドラッグ終了時点でフラグをtrue、0.3秒後にフラッグをfalseと設定))
その結果、FixedUpdate内の命令が効いているようで、
接触判定が無視されることはなくなったようです。
(それによりRigitbody2DのSleeping ModeをStart Asleepにしました
「Start Asleep」がもっともメモリ消費が少ないと思われるので)
質問
2重に(OnTriggerEnter2DとFixedUpdate)で同じDestroyの命令を実行することに
不安を感じております。この方法はやはりおかしいでしょうか?
(ログを確認すると先にFixedUpdate内の命令が実行されているようで、
実際には、FixedUpdateのみが効いていて、OnTriggerEnter2Dの命令は効いてないと思われます)
何か別の方法はありませんでしょうか?
UpdateおよびFixedUpdateは遅く、メモリ消費も心配なので、できればUpdate、FixedUpdateを使用せずに
OnTriggerEnter2Dで確実に命令が実行される方法はないでしょうか?
すいませんが、ご教授よろしくおねがいいたします。
追記(やってみた事2)
「OnTriggerEnter2D」を「OnTriggerStay2D」に変更し、FixedUpdateの命令をやめてみましたが、
それでもまだ、ごくたまに上記1〜3の命令が、無視はされなくなったようですが、
命令実行のタイミングが若干遅れる事があります。
※さらに「FixedUpdateで上記1~3の命令を実行する場合」と、
「OnTriggerStay2Dで実行する場合」を比較すると「OnTriggerStay2D」の方が
多くメモリ消費しています。(大幅な差はありませんが)
(常時命令が呼び出されないように両方の方法ともに、フラグで命令の実行時間を制限しております)
※Rigidbody2Dの「Collision Detection」は「Continous」、
「Sleeping mode」は「Never sleep」に設定しております。
再度考えましたが、
シンプルなドラッグドロップのパズルゲームで、
静止してるスロットにパズルピースをドラッグして重ねるだけなので、
理論的には「OnTriggerStay2D」ではなく
最初に「OnTriggerEnter2D」で重なった瞬間のみ判定するほうが、
正しいような気がしております。
どうにか「OnTriggerEnter2D」で確実に命令を実行させる方法なないものでしょうか?
ご教授よろしくおねがいいたします。
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
+1
OnTriggerEnter2Dが発動しない原因
OnTriggerEnter2D内にDebug.Logを置いても表示されないことが確認出来ている場合、
「そもそも衝突が発生していない」ことになります。
この原因としては下記ページにある
「UnityのCollision Detectionでとんでもなくつまづいた - こまけぇこたぁいんだよ」
Collision Detectionの設定によるもの。
もしくは
「Post Position 【Unity】 静止した物体同士の当たり判定の検出について」
における「Rigidbodyが付いている方のオブジェクトが移動もしくは回転している(静止しているとダメ)」というパターンが考えられます。
FixedUpdateは使用すべきではない
質問主さんの仰る通り、FixedUpdateは頻繁に呼ばれる為(条件分岐を挟んでいたとしても)使用は最小限にすべきです。
上記ColliderとRigidbodyの設定で直らない場合は、「OnTriggerEnter2D+FixedUpdate」を「OnTriggerStay2D(=接触している間呼ばれ続ける)」にするとシンプルになるかと思います。
こちらもフラグ判定は必要になりますので、以下のようにするとよいかと思います。
(Destroyは即時行われない場合があるので判定には使用していません)
private bool isCollision;
void OnTriggerStay2D(Collider2D other) {
if (!isCollision) {
isCollision = true;
//Destroy処理
}
}
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
check解決した方法
0
追記 / 自己解決しました
あくまで当方の予測ですが、70個のパズルピースがあるという事により、
Unityの処理が追いつかない部分があり、そのせいで命令が無視されるのではないか、と思われます。
(パズルピースが10個ほどのシーンではこの問題は起こっておりません。)
解決方法
OnTriggerEnter2D使用で、
コライダーの形を「BoxCollider2D」から「CircleCollider2D」に変えました。
理由は、いくつかのサイトで「CircleCollider2D」の方が、最もシンプルな処理で、
「BoxCollider2D」より処理が早いとの記述があった為です。
さらに、スロット側、パズルピース側の
両方のコライダーの判定範囲の半径を共に最小数値の「0.0001」に変えました。
(この数値が大きくなると上記の判定無視が起こってしまいます。)
※ Rigidbody2D「Collision Detection」は「Continous」、「Discrete」両方共に動作します。
※ Rigidbody2D「Sleeping Mode」は「Never Sleep」に設定しております。
※ 上記のFixedUpdate内での衝突判定の処理は削除しました。
何度も設定を変えて実験しましたが、
70個のパスルピースがあるという、この私の状況では、
上記の設定でなければ、うまく作動しない模様です。
閲覧いただき、ありがとうございました。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.37%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2017/10/30 13:03
1つめの
「Collision Detection」は現状ですでに「Continous」になっておりました。
2つめの
「Rigidbodyが付いている方のオブジェクト」=「パズルピース」になるのですが、
ドラッグで移動して、スロット(静止してます)の上に重ねるので、
移動している最中に接触しております。
移動はしていて、回転はしていない状態です。
ですので上記2つには該当しないと思われます。
元は「OnTriggerStay」を使用しておりましたが、少し前に「OnTriggerEnter」に変えておりました。
理由は「OnTriggerStay」の方が、呼び出し回数が多いので、
メモリ消費を心配して接触した瞬間だけ呼び出される、
「OnTriggerEnter」に変えておりました。
しかしながら判定が無視されるという問題が出てきてしまい、
さらに、やはりFixedUpdateは使うべきではないとなると、
もう一度「OnTriggerStay」に戻して
調整を考えたいと思います。
ありがとうございました。
2017/10/30 13:19
FixedUpdateは触れていない間も呼ばれることを考えると、OnTriggerStayの方がいいという判断でした。
が、改めて考えると、ピースが多くて目的のオブジェクト以外でも衝突していてOnTriggerStayも呼ばれっぱなしになっているような場合は、ManagerクラスのFixedUpdateで管理したりする方がスマートかもしれません。
(既に設定済みかもしれませんが、もしピース同士、スロット同士の衝突が必要無いなら、衝突レイヤーの設定でオフにすると多少改善するかもしれません。
https://docs.unity3d.com/jp/current/Manual/LayerBasedCollision.html)
2017/10/30 13:53
当方の制作中のゲームはシンプルなパズルゲームですので、
動くものは、ドラッグできる「パズルピース」だけで、
あとは何も動きませんし、衝突もありません。
やはりフラグで判定時間を限定しながら「OnTriggerStay」を使用するのが
いいかと思います。
レイヤーの設定はしておりませんでした。確認してみます。
ありがとうございました。
2017/10/30 21:29
まだ確実に命令が実行されるようにはできないようです。
(上記の「追記」に記しました。)
(命令が実行されるのですが、ごくたまに命令実行が若干遅れる事があります)
2017/11/01 10:26
衝突判定はピースとスロットの2つしか存在しないということですが、ドラッグしたタイミングでRigidbodyやColliderを有効化しているような形でしょうか?
もしそうならその処理と関連して物理判定有効化までの遅延が発生している可能性も考えられます。
(とは言えOnTriggerEnterを飛ばしてOnTriggerStayが呼ばれるというのは疑問ですが……)
話をひっくり返して申し訳無いですが、どうしても無理ならFixedUpdateでもいいと思います。
(メモリ消費量を気にするなら、少し操作感に差が出るかもしれませんがUpdate内でRayCastを使って「タッチしている場所がピースとスロット両方と重なったら」という処理とも比較してみるといいかもしれません。どちらが負荷が低いかは要検証ですが……)
2017/11/03 20:35
解決しました。上記に(自己解決)記しました。
いろいろご説明いただき大変ありがとうございました。
感謝しております。