🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C#

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

Unity

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

Q&A

解決済

1回答

951閲覧

AddRelativeTorqueをかけた後に『ConfigurableJoint』のロックのかけ方

DY2peace

総合スコア20

C#

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

Unity

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

0グッド

0クリップ

投稿2020/12/07 11:19

編集2020/12/10 02:22

前提・実現したいこと

 ConfigurableJointでつないだカプセルをAddRelativeで動かした後、ConfigurableJointのLocked(X軸)で固定したいです。
イメージ説明

※床に落としても崩れない、強固な固定を行いたいと考えてます。ConfigurableJointのLockが基本的に
ぶつけてもくずれないのでどうしてもこれを使いたいと考えてます。

発生している問題

 現在位置・角度を取得してから、ロックをかければできるかなと思ったのですが、
初期位置に戻ってしまいます。
イメージ説明

該当のソースコード

以下のコードで実施しましたが、初期位置に戻ってしまいました。

C#

1 //ジョイントロックの検討 2   if(Input.GetKey(KeyCode.V))//原点位置に戻ってしまう・・・ 3 { 4     Vector3 tmp = CapJoint.transform.localPosition; 5 Vector3 RotPosi = CapJoint.transform.localEulerAngles;     6  7 CapJoint.transform.localPosition = new Vector3(tmp.x,tmp.y,tmp.z); 8 CapJoint.transform.localEulerAngles = new Vector3(RotPosi.x,RotPosi.y,RotPosi.z); 9 10 this.configurableJoint = CapJoint.GetComponent<ConfigurableJoint>(); 11 this.configurableJoint.angularXMotion = ConfigurableJointMotion.Locked; 12 13 }

###追記情報(2020年12月10日)
イメージ説明

ご迷惑をおかけいたしますが、どなたかご教示いただけると幸いです。

よろしくお願いいたします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

ご質問者さんがお試しの通り、Locked状態にすると初期姿勢にロックされてしまうようですね。
下記のようにスペースバーを押すとangularXMotionをロックするスクリプトを用意し...

C#

1using UnityEngine; 2using UnityEngine.UI; 3 4[RequireComponent(typeof(ConfigurableJoint))] 5public class JointController : MonoBehaviour 6{ 7 [SerializeField] private Text stateText; 8 private ConfigurableJoint joint; 9 10 private void Start() 11 { 12 this.joint = this.GetComponent<ConfigurableJoint>(); 13 } 14 15 private void Update() 16 { 17 if (Input.GetKeyDown(KeyCode.Space)) 18 { 19 this.joint.angularXMotion = ConfigurableJointMotion.Locked; 20 } 21 22 this.stateText.text = this.joint.angularXMotion.ToString(); 23 } 24}

外力でジョイントを屈曲させ、その状態でロックすると姿勢が勢いよく復帰して吹っ飛びました。

図1

なにか初期姿勢をリセットするような機能はないものかと調べてみても結局見つからなかったのですが、下記のように...

C#

1using UnityEngine; 2using UnityEngine.UI; 3 4[RequireComponent(typeof(ConfigurableJoint))] 5public class JointController : MonoBehaviour 6{ 7 [SerializeField] private Text stateText; 8 private ConfigurableJoint joint; 9 10 private void Start() 11 { 12 this.joint = this.GetComponent<ConfigurableJoint>(); 13 } 14 15 private void Update() 16 { 17 if (Input.GetKeyDown(KeyCode.Space)) 18 { 19 this.joint.angularXMotion = ConfigurableJointMotion.Locked; 20 this.joint.swapBodies = !this.joint.swapBodies; 21 this.joint.swapBodies = !this.joint.swapBodies; 22 } 23 24 this.stateText.text = this.joint.angularXMotion.ToString(); 25 } 26}

swapBodiesを切り替えてすぐ元に戻す」という一見無意味な行為をやったところ、どうやら初期姿勢が屈曲した状態に変わったようでした。

図2

すみませんが、そもそもswapBodiesがどういう役割を持つのかよく分からないです。リファレンスの説明から想像すると「オブジェクトAにジョイントをアタッチしてオブジェクトBと接続した状態でswapBodiesをオンにすると、オブジェクトBにジョイントをアタッチしてオブジェクトAと接続した状態と同じ挙動をする」といった感じでしょうかね?

実際のところは何をやっているのかを追おうとしても、こういった物理シミュレーション関連はほとんどがネイティブコードになっていて(UnityCsReference/Dynamics.bindings.cs at master · Unity-Technologies/UnityCsReference · GitHub)手詰まりになり、結局謎のままでした。まあともかく、この両Rigidbodyの関係を入れ替える過程のどこかに初期姿勢を再設定する部分があるのかもしれません。

##ロック時にリミットを更新する案

実験用のスクリプトを下記のように変更しました。

C#

1using UnityEngine; 2using UnityEngine.UI; 3 4[RequireComponent(typeof(ConfigurableJoint), typeof(Rigidbody))] 5public class JointController : MonoBehaviour 6{ 7 [SerializeField] private Text stateText; 8 [SerializeField] private float torqueMagnitude = 20.0f; 9 private ConfigurableJoint joint; 10 private new Rigidbody rigidbody; 11 private bool isLocked; 12 private Vector3 polarAxis; 13 private Vector3 pole; 14 15 // axisとsecondaryAxisを接続相手の座標系に変換して保存するメソッドを作っておく 16 private void ResetPolarAxis() 17 { 18 this.pole = 19 this.joint.connectedBody.transform.InverseTransformDirection( 20 this.transform.TransformDirection(this.joint.axis)); 21 this.polarAxis = 22 Vector3.ProjectOnPlane( 23 this.joint.connectedBody.transform.InverseTransformDirection( 24 this.transform.TransformDirection(this.joint.secondaryAxis)), 25 this.pole); 26 } 27 28 // また、現在のsecondaryAxisの向きをconnectedBodyの座標系に変換して 29 // polarAxisの向きと比較することで角度の変化を算出するプロパティを用意しておく 30 private float Azimuth => Vector3.SignedAngle( 31 this.polarAxis, 32 Vector3.ProjectOnPlane( 33 this.joint.connectedBody.transform.InverseTransformDirection( 34 this.transform.TransformDirection(this.joint.secondaryAxis)), 35 this.pole), 36 this.pole); 37 38 // そしてロック状態を操作するためのプロパティを作る 39 private bool IsLocked 40 { 41 get => this.isLocked; 42 set 43 { 44 if (value == this.isLocked) 45 { 46 return; 47 } 48 49 this.isLocked = value; 50 if (value) 51 { 52 // まずロック時点のリミットを保存しておき... 53 var oldHighLimit = this.joint.highAngularXLimit; 54 var oldLowLimit = this.joint.lowAngularXLimit; 55 56 // ロック時点の基準方向からの回転角を取得しておく 57 var azimuth = this.Azimuth; 58 59 // そして回転をロック、基準方向を更新し... 60 this.joint.angularXMotion = ConfigurableJointMotion.Locked; 61 this.joint.swapBodies = !this.joint.swapBodies; 62 this.joint.swapBodies = !this.joint.swapBodies; 63 this.ResetPolarAxis(); 64 65 // ロック時点の回転角を使ってリミットを修正する 66 var newHighLimit = oldHighLimit; 67 newHighLimit.limit += azimuth; 68 var newLowLimit = oldLowLimit; 69 newLowLimit.limit += azimuth; 70 this.joint.highAngularXLimit = newHighLimit; 71 this.joint.lowAngularXLimit = newLowLimit; 72 73 // 参考情報としてリミットをコンソールに出力する 74 Debug.Log($"High Limit: {oldHighLimit.limit} -> {newHighLimit.limit}, Low Limit: {oldLowLimit.limit} -> {newLowLimit.limit}"); 75 } 76 else 77 { 78 this.joint.angularXMotion = ConfigurableJointMotion.Limited; 79 } 80 } 81 } 82 83 private void Start() 84 { 85 this.joint = this.GetComponent<ConfigurableJoint>(); 86 this.rigidbody = this.GetComponent<Rigidbody>(); 87 88 // Start時点でResetPolarAxisを実行して、初期基準方向を覚えておく 89 this.ResetPolarAxis(); 90 } 91 92 private void Update() 93 { 94 if (Input.GetKeyDown(KeyCode.Space)) 95 { 96 this.IsLocked = !this.IsLocked; 97 } 98 99 this.stateText.text = this.joint.angularXMotion.ToString(); 100 } 101 102 private void FixedUpdate() 103 { 104 this.rigidbody.AddRelativeTorque(this.joint.axis * (this.torqueMagnitude * Input.GetAxis("Vertical"))); 105 } 106}

動かした様子も撮影してみたものの、この図ではちょっと分かりにくかったかもしれません。すみません...

図3

投稿2020/12/08 21:36

編集2020/12/10 20:57
Bongo

総合スコア10811

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

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

DY2peace

2020/12/10 02:27

Bongo様 前回に引き続き、今回もアドバイスありがとうございます。 いただいた提案で試してみて、問題なくストップさせることができました。  また新たな問題として、12月10日の追記情報で記載しましたが、Locked+SwapBodiesで動きを止めたあと、またLimitに戻したいとき、角度制限(X angular Limit)が止めた箇所からの位置で範囲が決まってしまうみたいです。  できれば角度制限は初期位置基準のままキープしたいと思っているのですが、何かいい方法など思いつきましたら、ご教示いただけると幸いです。 ⇒おそらくConfigurableJointを2つ、(初期位置の角度制限キープ用とLocked+SwapBodies用)を用意しておきえば解決できるかもしれないですが、Jointをできる限り増やしたくないとも思ってます。
DY2peace

2020/12/10 10:53

また別のやり方で、強制的にFixedJointをAdd Componentで追加してやってみました。ご提示いただいた方法と比べて、完全停止に近いのですが、追加したときの反応が鈍い感じがしました。適当なキーを押してOnOffしたのですが、すぐにFixedJointが出てくるときと出てこないときがありました。
Bongo

2020/12/10 19:52

なるほど、屈曲した状態を新しい基準姿勢として再設定したのであれば、制限角度もその姿勢を基準に決められるというのは納得のいく挙動ではありますね。 では、ロック時に角度制限を回転量に応じて再設定してしまうというのはどうでしょうか。好都合なことにX軸だけは制限角度の上限と下限を個別に設定できますので、たとえば初期制限角度が±30°だったとすると、+10°回したところでロックした場合には上限を+20°、下限を-40°にしてしまえば可動範囲を一定に保つことができるように思います。 実験してみた結果を追記しました。ご参考になりますでしょうか?
DY2peace

2020/12/12 13:13

ご回答・ご対応いただきありがとうございました。 無事自分の意図していた通り、動作確認できました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問