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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Unity

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

Q&A

解決済

2回答

978閲覧

unityで砕け散る弾丸が作れません。

urusu2019

総合スコア19

Unity

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

0グッド

0クリップ

投稿2018/12/05 16:23

前提・実現したいこと

ここに質問の内容を詳しく書いてください。
発射した弾丸が障害物に当たると砕け散るようにしたいと考えています。
■■な機能を実装中に以下のエラーメッセージが発生しました。

発生している問題・エラーメッセージ

弾はmayaでオブジェクトを粉砕。 unityにて発射元にprefabに指定。 ですが障害物に当たっても飛散せず固体のままです。

該当のソースコード

ソースコード

試したこと

試した事は、
発射口とprefabを複数にして、各発射口にthis.GetComponent<Rigidbody>().Sleep();を付与し初動でのリジットボディ無効を行いましたが弾同士の衝突は避けられるが飛んで行きません。

衝突時にprefabの親を消すスクリプトを付与すると全て消えてしまうようです。
せめて発射直後に弾同士のコライダー反応で飛散してくれるならIgnoreCollisionで行けそうなんですが・・・
mayaでの粉砕状態も色々試しましたが、
後処理はシェイプ、オリジナルサーフェスは削除で良いような気がします。
unity上で解決できる問題と思いますが他にやれる事はありますか?

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

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

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

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

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

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

izmktr

2018/12/05 18:02

問題点と話題がバラバラです。「障害物にあたっても何も起きない」と書いていながら「弾同士の衝突で困っている」と全然別のことを書いています。文章を推敲して書き直してください。
guest

回答2

0

ベストアンサー

Mayaを持っておらず(魅力的なソフトですが高価ですね)、Mayaから出力されたモデルをインポートした結果どのような状態にあるのか分かりませんが、もしかして個々の破片にはCollider(おそらくMesh Colliderでしょうか)があるのみでRigidbodyがアタッチされておらず、親オブジェクトに一つだけRigidbodyが付いている状態だったりしないでしょうか?
もしそうでしたら、それをそのままUnity上で動かしてもオブジェクト全体が一塊の剛体としてふるまい、破片が飛び散るような動きはさせられないんじゃないかと思います。
かといって個々の破片に単純にRigidbodyを付けた状態で発射すれば、いきなり弾丸が砕けてしまうでしょうから対策が必要なはずです。

一案としては、破片同士をFixedJointで連結してやる手がありそうです。breakForceを適切に設定すれば、接続部に一定以上の力がかかった際にジョイントが破断し破片が飛び散ってくれそうです。
ただし、発射時は負荷上限を大きくしておいて、発射後に上限を目標値に合わせるような工夫が必要かもしれません。発射方式にもよるでしょうが、負荷上限が小さいまま発射すると、発射時の加速に耐えられず砕けてしまうなんてこともありそうです。

別案として、発射から着弾までは単純なコライダーとRigidbodyを一つずつ持つ未粉砕弾丸を使い、着弾を検出した際に複数の破片からなる粉砕弾丸に差し替えてしまう手もあるでしょう。
第一の案の、複雑なコライダーを持ち複数のRigidbodyを継ぎ合わせたオブジェクトを常時使用するのは、単純なオブジェクトを使うよりも計算コストの面で不利なはずです。着弾後の演出としてだけ粉砕表現があれば十分なのでしたら、こちらの方が有効そうな気がします。

ご参考として、後者の案を試してみました。粉砕弾丸への差し替えにもいくつか方法が考えられますが、今回は下図のように、未粉砕弾丸の子オブジェクトとして粉砕弾丸を内蔵しておき、衝突時に粉砕弾丸を未粉砕弾丸の外に取り出して、未粉砕弾丸は削除するという方式にしました。
親オブジェクトである未粉砕弾丸はSphereColliderとRigidbodyを一つずつ持つシンプルなオブジェクトです。子オブジェクトである粉砕弾丸は破片の集合体で、個々の破片にそれぞれMeshColliderとRigidbodyが付けられています。

オブジェクトの構成

未粉砕弾丸の方に下記のスクリプトをアタッチし、衝突時のオブジェクト差し替えを行いました。

C#

1using System.Collections; 2using UnityEngine; 3 4public class SphereController : MonoBehaviour 5{ 6 public float Delay = 5.0f; 7 public float InitialSpeed = 20.0f; 8 public float ShatteringImpulse = 100.0f; 9 public Transform Target; 10 private Vector3 previousAngularVelocity; 11 private Vector3 previousCenterOfMass; 12 private Vector3 previousPosition; 13 private Vector3 previousVelocity; 14 private new Rigidbody rigidbody; 15 private Transform shatteredSphere; 16 17 private IEnumerator Start() 18 { 19 this.rigidbody = this.GetComponent<Rigidbody>(); 20 21 // 子オブジェクトとして内蔵している破砕済みオブジェクトを取得し、 22 // Start時点では非アクティブ状態にしておく 23 this.shatteredSphere = this.transform.Find("ShatteredSphere"); 24 this.shatteredSphere.gameObject.SetActive(false); 25 26 // 一定時間経過後、目標に向かって発射する 27 yield return new WaitForSeconds(this.Delay); 28 29 if (this.Target != null) 30 { 31 this.rigidbody.AddForce( 32 (this.Target.position - this.transform.position).normalized * this.InitialSpeed, 33 ForceMode.VelocityChange); 34 } 35 } 36 37 private void FixedUpdate() 38 { 39 // FixedUpdate時点の位置・速度・角速度を覚えておく 40 // 微妙な違いですが、OnCollisionEnter時点のこれらは衝突後のもののようですので 41 // それを使った場合だと破片の動きがちょっと気に入らなかったため、衝突直前の 42 // 位置・速度・角速度を使うことにしました 43 this.previousPosition = this.rigidbody.worldCenterOfMass; 44 this.previousVelocity = this.rigidbody.velocity; 45 this.previousAngularVelocity = this.rigidbody.angularVelocity; 46 this.previousCenterOfMass = this.rigidbody.worldCenterOfMass; 47 } 48 49 private void OnCollisionEnter(Collision collision) 50 { 51 // 衝突しても、衝撃が一定量を下回っていれば何もしない 52 if (collision.impulse.magnitude < this.ShatteringImpulse) 53 { 54 return; 55 } 56 57 // ひとまず自分自身は非アクティブにし... 58 this.gameObject.SetActive(false); 59 60 // 破砕済みオブジェクトをルート直下に取り出し... 61 this.shatteredSphere.SetParent(null); 62 63 // 破砕済みオブジェクトを衝突直前の位置に移動し... 64 this.shatteredSphere.position = this.previousPosition; 65 66 // 破砕済みオブジェクトをアクティブにし... 67 this.shatteredSphere.gameObject.SetActive(true); 68 69 // 衝突直前の速度を各破片に与え... 70 foreach (var r in this.shatteredSphere.GetComponentsInChildren<Rigidbody>()) 71 { 72 // 破壊前の重心から遠い位置にある破片は、回転によってハンマー投げのように飛びそうだと思い 73 // 回転の影響を追加してみましたが、省略して計算量を削減しても見栄えはさほど変わらないと思います 74 r.velocity = this.previousVelocity + Vector3.Cross(this.previousAngularVelocity, r.worldCenterOfMass - this.previousCenterOfMass); 75 } 76 77 // 自分自身はもう不要なので削除する 78 Destroy(this.gameObject); 79 } 80}

動かしてみますと下図のようになりました。一定以上の衝撃が加わるとオブジェクトが差し替わり、個々の破片が独立して運動します。

プレイモード

投稿2018/12/10 09:47

Bongo

総合スコア10807

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

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

urusu2019

2018/12/10 15:56

貴重な回答ありがとうございます。 衝撃力での粉砕有無は驚きました。 そんな事も可能なのですね。 衝撃時、粉砕の開始地点はやはり中心から開始されるのでしょうか? 現実のように、中心ではなく外側から砕けていくように、それと破片全てが一気に飛散するのではなく、一部分だけ飛散せず残るようにしようとすると、破片に体力を付与し、何らかの衝撃が加わったら体力が削られ、0になったら破片同士が解除になり分離していく・・・みたいな事も可能なのでしょうか?
Bongo

2018/12/10 21:54

なるほど、確かにそういった複雑な破壊を表現するのは、最初に挙げたジョイント接続方式の方がいいかもしれませんね。オブジェクト差し替え方式でも、差し替え時に各破片に与える速度を調節することで多少は粉砕の開始点をいじれるかもしれませんが、この方法では結局すべての破片が一斉に独立して動き出すのは変わりませんので、大して見た目の違いは現れないような気がします。 破片に体力を持たせるというのはいい案だと思います。その方針で行くとすると、ジョイントのBreak ForceはInfinityのままにして、接続解除は完全にスクリプトで制御した方がいいでしょうね。 破片の耐久力が0になったときに接続解除するべきジョイントはあらかじめ全ジョイントを走査して調べておく必要がありそうです。衝突時にColliderあるいはRigidbodyから接続されているジョイントを取得できれば楽だと思うのですが、残念ながらそういった機能は用意されていないようです...
urusu2019

2018/12/11 15:09

ご連絡ありがとうございます。 もしかすると例えば、ShatteredSphereを4つ用意して、その内1つだけ粉砕させると残り3つ残っているので、弾の「欠け」が表現出来るかもしれませんね。 ジョイントの件興味があります。 色々と試してみます。 教えて頂いたコードですが、下記発射コードから prefabを指定したのですが粉砕はするようですが弾が飛んで行きません。 何か干渉するのでしょうか? public class Shooting : MonoBehaviour { // bullet prefab public GameObject bullet; // 弾丸発射点 public Transform muzzle; // 弾丸の速度 public float speed = 1000; // Use this for initialization void Start() { } // Update is called once per frame void Update() { // z キーが押された時 if (Input.GetKeyDown(KeyCode.Z)) { // 弾丸の複製 GameObject bullets = GameObject.Instantiate(bullet) as GameObject; Vector3 force; force = this.gameObject.transform.forward * speed; // Rigidbodyに力を加えて発射 bullets.GetComponent<Rigidbody>().AddForce(force); // 弾丸の位置を調整 bullets.transform.position = muzzle.position; } } }
Bongo

2018/12/11 20:31

「Rigidbodyに力を加えて発射」と「弾丸の位置を調整」の順序を逆にしてみるとどうなるでしょうか? 思うに、AddForceによって弾丸に速度が与えられるものの、その後のtransform操作によって速度が再計算されて、0になってしまうのではないでしょうか。
urusu2019

2018/12/12 14:59

回答ありがとうございます。 特に変化はありません。 弾は表示されますが飛んで行きません。
Bongo

2018/12/12 19:11

弾丸が前方に飛ばず、発射口からそのまま下へ落下する感じでしょうか? 試しに、「bullets.GetComponent<Rigidbody>().AddForce(force);」を「bullets.GetComponent<Rigidbody>().AddForce(force / 60.0f, ForceMode.VelocityChange);」に変えると変化はあるでしょうか。 また、参考として確認したいのですが、飛ばそうとしている弾丸のRigidbodyのmassと、メニューの「Edit」→「Project Settings」→「Time」のFixed Timestepはいくつでしょうか?
urusu2019

2018/12/13 11:15

返答ありがとうございます。 上手く砕けました! ちなみにmassは10、Timestepは0.02です。 なぜ成功したのか分かりませんが、色んなモードがあるんですね。 調べてみます。
Bongo

2018/12/13 11:34

うまくいったようでなによりです。詳しくはリファレンスをご参照いただきたいのですが、AddForceのモードを省略するとForceModeはForceとなります。この状態だと、一見弾丸に大きさ1000の大きな力を加えているように思われますが、実際にはmassとTimestepの影響が考慮されるため、コメントいただいた条件の場合の発射速度は秒速2m程度にしかならないと思われます。一方、VelocityChangeはAddForceの引数として与えたベクトルがそのまま速度として与えられるため、massやTimestepの設定に関わらず同じ速度で発射されるはずです。
guest

0

親と破片たちの親子関係を解除してから親を消すのはどうですか?
親からDetachChildren()で、子要素との親子関係を一括解除できます。
これなら親と一緒に破片まで消えることはないはずです。

投稿2018/12/09 07:16

Linkins

総合スコア82

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

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

urusu2019

2018/12/10 15:55

貴重な回答ありがとうございます。 ご連絡が遅くなりました。 試してみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問