質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

4回答

1063閲覧

Unityで生成されたオブジェクトの物理演算を一定時間後に停止したい!

A.Komachi

総合スコア6

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2022/10/16 11:32

編集2022/10/16 11:34

Unity初学者から,物理判定について質問です.

イメージ説明

添付写真のように,楕円を上から下に落として積み上げるプログラムをUnityで作成しています.
積み上げが進むと楕円の数が多いためか処理が重い・物理演算に時間が掛かってしまいます.
そのため積み上げの途中で下に溜まっている楕円は随時物理演算を停止し,処理を軽くしたいのですが,どのように行うのが適切でしょうか?

以下が現状、各楕円にアタッチしているスクリプトです


C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class Ellipse : MonoBehaviour 6{ 7private bool make_next = true; 8private float x; 9private float maked_time = 0.0f; 10Rigidbody rb; 11private bool stoped = true; 12 13void Start() 14{ 15x = transform.position.x; 16} 17// Update is called once per frame 18void Update() 19{ 20if (Time.time - maked_time > 0.3f && make_next) 21{ 22make_next = false; 23// 次の楕円を生成する処理 24FindObjectOfType<DropDownEllipseMiddle>().NewEllipse(x); 25} 26else if(Time.time - maked_time > 10.0f && stoped) 27{ 28Stop(); 29} 30} 31 32void Stop() 33{ 34stoped = false; 35rb = this.gameObject.GetComponent<Rigidbody>(); 36rb.isKinematic = true; 37} 38}

このスクリプトだと
There is no 'Rigidbody' attached to the "" game object, but a script is trying to access it. You probably need to add a Rigidbody to the game object "". Or your script need to check if the component is attached before using it.
と警告が表示されます(プログラムが止まるわけではありません)

物理演算は停止しても,当たり判定は働いたままにしたいです.
またプログラムそのものの処理が軽くなれば良いため,下にある楕円の物理演算を停止させるのはあくまで手段の一つです.処理を軽くする良いアイデアはありますでしょうか?
もしあればご教授いただけると幸いです.

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Bongo

2022/10/16 22:22

「There is no 'Rigidbody' attached to~」が出るとのことでちょっと気になりまして確認したいのですが、各楕円には他にどのようなコンポーネントをアタッチしていますでしょうか? スクリーンショットを拝見しますに2Dの世界のように思うのですが、もしRigidbody 2Dを使っていらっしゃるのでしたら、スクリプトの方もRigidbodyではなくRigidbody2D(https://docs.unity3d.com/ja/current/ScriptReference/Rigidbody2D.html )を操作するように書き換える必要があるかもしれません。
A.Komachi

2022/10/17 01:13

ご指摘ありがとうございます. 各楕円には ・Polygon Collider 2D ・Rigidbody2D ・Sprite Renderer (Sprite = Circle) がアタッチされています. スクリプト内でRigidbodyと記述されている箇所を全てRigidbody2Dに変更すると警告がなくなりました.ありがとうございます,初歩的なミスでした. ただし rb.isKinematic = true; は当たり判定もなくなってしまうのか,楕円が壁の外に出るようになってしまいます. 下に溜まっている楕円を固定する方法,または処理を軽くするもっと良い方法はございますでしょうか
guest

回答4

0

イメージ説明

改善されたプログラムの動作様子


カクつきは録画映像をgifに変換した影響のためで,実際は全くカクつかないです。
回答していただいたお二方ありがとうございました,とても勉強になりました.

(お二方から共に大変参考になる意見をいただいたためベストアンサーは保留にします)


イメージ説明

上手く画像も生成できています.
これをさらにGANに突っ込んだりと...これから楽しいことをいっぱいします.

投稿2022/10/19 14:07

A.Komachi

総合スコア6

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

イメージ説明
isKinematic = true;
にすることで楕円が壁の外に出る様子

投稿2022/10/18 08:27

A.Komachi

総合スコア6

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Bongo

2022/10/18 22:29

ふわっと等速運動しているように見えますので、私の回答で申し上げた「velocityとangularVelocityをゼロに」というのが効くかもしれませんね。それについてはもうお試しでしょうか? それと、Rigidbody 2Dの「Collision Detection」がContinuousだと、こういった多数のオブジェクトを扱う場合はけっこう負荷が上がってしまうような感じでした。もしContinuousでしたら、衝突判定の精度は低下するでしょうがDiscreteにすると高速化につながるかもしれません。高速で運動するオブジェクトのすり抜け防止にはContinuousが有効だろうと思いますが、オブジェクトが低速ならなるべくDiscreteにして処理速度を稼いだ方がいいんじゃないかと思います。
A.Komachi

2022/10/19 14:15

ありがとうございます,確認したところ「collision Detection」はDiscreteになっていました.この部分は一度も触れていないためDiscreteがデフォルトかもしれません. Continuousで検証したところ,すり抜けがかなり防止されましたが,楕円が積みあがるにつれてかなり処理が重くなってしまったため,残念ながらDiscreteを採用します. ご指摘いただきありがとうございます.
guest

0

負荷軽減についてはUnchFullburstさんのアドバイスがご参考になりそうですね。私の方では、質問へのコメントでご報告いただいた「楕円が壁の外に出る」という件について考えてみました。

十分に実験したわけではないのでちょっと自信はないですが、楕円に速度や角速度が残っているとまずいのかもしれません。isKinematictrueにするだけですと、私の試したところでは下図のようにキネマティック化した楕円がふわっと浮かび出したり、壁を貫通して漏れていってしまう奇妙な挙動が観察されました。ご質問者さんの場合はどのような挙動でしょうか?

図1

そこでキネマティック化に併せてvelocityangularVelocityをゼロにしたところ、ちょっと観察した限りでは変な動きは起こさなくなったようでした。

C#

1rb.isKinematic = true; 2rb.velocity = Vector2.zero; 3rb.angularVelocity = 0.0f;

図2

他にもisKinematictrueにする代わりにbodyTypeStaticにしたり...

C#

1rb.bodyType = RigidbodyType2D.Static;

Rigidbody2Dを破壊してしまうのでも、異常な挙動を防止できるようでした。

C#

1Destroy(rb);

simulatedfalseにするのはどうだろうかとも考えたのですが、リファレンスにある...

シミュレーションしない場合、アタッチされているすべての Collider2D や Joint2D は物理シミュレーションに組み込まれません。

という点がまずいかもしれませんね。やってみたところコライダーが機能しなくなって、上に積もったボールが箱の底まで落ちてきてしまいました。

図3

投稿2022/10/17 14:08

Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

A.Komachi

2022/10/19 13:37 編集

回答者さん側でシミュレーションまでしていただき大変恐縮です,ありがとうございます. rb.isKinematic = true; だけではご指摘いただいた通り、楕円が宙に浮く挙動(Bongo様が載せてくださった1つ目のgifと全く同じ挙動)を示します. 3つのアドバイス ①rb.velocity = Vector2.zero; と rb.angularVelocity = 0.0f; ②rb.bodyType = RigidbodyType2D.Static; ③Destroy(rb); それぞれ試してみたところ,どれも問題なく動作しました. FPSを見ると若干③つ目が最も高かったため,この案でプログラムを作成したいと思います,ありがとうございます.とても勉強になりました. またせっかくなのでプログラム作成結果についてもgifを掲載します
guest

0

方針としては、1つの管理クラスにScene内の動かすべきElipse全てを管理させます。

100個のupdateで100個のオブジェクトを動かすより、1個のupdateで1000個のオブジェクトを動かした方が軽いです。
しかも今だと各EllipseはisKinematicがオフになっても、Update自体は無駄に回し続けるので重いです。
タイマーの計算を全てのElipseが永遠に行い続けてるわけですね

ついでに0.3秒ごとの生成判断もElipseではなく、管理クラスにやらせましょう。
そっちのが責務がはっきりしてコード読みやすいです。
今だとDropDownEllipseMiddle?が管理クラスっぽい動きをしているんですかね?
とりあえず以下はDropDownEllipseMiddleを使わない場合として書きますが、適当に同じことをやらせてください


まずElipseにUpdateは実装せず、今Updateの中で行っている処理をCountTimerとかElipseUpdateとか、そんな名前の外から叩けるpublicなメソッドにします。
で、Scene内のElipseをListで持つElipseManagerを作り、ElipseManagerのUpdateの中でfor使ってぶん回します。
こうすることで1個のupdateで数百のElipseを動かせます。

で、なおかつElipseの方にもElipseManagerの参照を持たせておき、StopしたときにElipseManagerのListから自身を消すようにします。
これでScene内のElipseがどれだけ増えても、ElispseManagerは常にisKinematicがfalseなElipseだけを触るようになります。

これならScene内のElipseの数が増えても爆発的な負荷増は防げるのではないでしょうか。

C#

1public class Ellipse : MonoBehaviour 2{ 3 private float stopTimer = 0.0f; 4 private Rigidbody2D rb; 5 public EllipseManager manager; 6 7 void Start() 8 { 9 rb = GetComponent<Rigidbody2D>(); 10 } 11 12 public void CountingStopTimer() 13 { 14 stopTimer += Time.deltaTime; 15 if (stopTimer > 10.0f) 16 { 17 Stop(); 18 } 19 } 20 21 void Stop() 22 { 23 rb.isKinematic = true; 24 manager.Remove(this); 25 } 26}

C#

1public class EllipseManager : MonoBehaviour 2{ 3 public Ellipse ellipsePrefab; 4 public Vector3 spawnPosition; 5 public float spawnTime = 0.3f; 6 private float spawnTimer = 0.0f; 7 public List<Ellipse> ellipseList = new List<Ellipse>(); 8 9 void Update() 10 { 11 spawnTimer += Time.deltaTime; 12 if (spawnTimer > spawnTime) 13 { 14 SpawnEllipse(); 15 spawnTimer = 0.0f; 16 } 17 18 if (ellipseList.Count == 0) 19 { 20 return; 21 } 22 23 for (int i = ellipseList.Count - 1; i >= 0; i--) 24 { 25 ellipseList[i].CountingStopTimer(); 26 } 27 } 28 29 public void SpawnEllipse() 30 { 31 Ellipse ellipse = Instantiate(ellipsePrefab, spawnPosition, Quaternion.identity); 32 ellipse.manager = this; 33 ellipseList.Add(ellipse); 34 } 35 36 public void Remove(Ellipse ellipse) 37 { 38 ellipseList.Remove(ellipse); 39 } 40}

投稿2022/10/17 06:04

編集2022/10/17 06:08
UnchFullburst

総合スコア663

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

A.Komachi

2022/10/19 13:44

ご回答ありがとうございます.スクリプトまで作成していただけるなんて大変恐縮です. ご指摘の通り現状ではDropDownEllipseMiddleが管理クラスっぽい動きをしています.個々のオブジェクトでUpdateを行っていることが処理を重くする原因となっているのですね,とても勉強になります. 掲載していただいたコードに従来のコードを追加する形でプログラムの修正を行いました.極力管理クラス内で処理を行うよう修正致しました. 結果は従来と比べて処理速度が大幅に向上し,カクつきも全くなくなりました. FPSも300以上上がっています,ありがとうございます. オブジェクトの一括管理の方法について勉強し切れていない状態でしたので,この回答で理解が深められました,ありがとうございました.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問