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

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

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

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

Unity

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

Q&A

解決済

1回答

2450閲覧

オブジェクトのドラッグ&ドロップで階段やスロープを上りたい

tride

総合スコア68

C#

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

Unity

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

0グッド

0クリップ

投稿2021/07/31 10:57

編集2021/07/31 20:51

表題の通りではありますが、オブジェクト(以降A)をドラッグ&ドロップした時に、階段またはスロープ(斜面)を上るようにしたいです。

現在、以前の質問でRigidbody.velocityで壁の通り抜けをしないようにしたのですが、この状態で階段またはスロープのオブジェクト(以降B)にAがさしかかると、ゆっくり高さ(y位置)がBの高さまで上がりはするものの、Bの高さを越えきれずに中に埋もれてしまいます。
その後、ドラッグを止めると弾かれる様にニュイッと上に移動します。
また、そこから段差を降りると非常にゆっくりと降下(毎秒0.015ぐらいの速度)して元の床面に着地します。

高さ自体はMathf.Clanpで制限しているので、その範囲内に高さが収まるのは分かるのですが、上昇下降がゆっくりになってオブジェクト面に沿って移動ができない理由が分からず、どうしていいか行き詰っています。

目的
オブジェクトをドラッグ&ドロップした時、床面に沿うように階段状のオブジェクトまたはスロープ状(傾斜)のオブジェクトを登攀したい。

困ってる事
・登攀時、上昇速度がゆっくりになってオブジェクトに埋まる。
・登攀後落下時、下降速度が非常にゆっくりになって着地までに時間がかかる

実施したこと
参考サイトを見てPhysics.gravity(またはrigidbody.AddForce)で重力を強く変更したがスロープの抵抗感が強くなるだけで実現せず。
他の質問を見てvelocityのyの値だけ元の値に戻してみたが実現せず。
・CharacterControllerを追加すると、クリックするだけで明後日の方向にランダムに向かう。

環境
・Unity 2021.1.12f1
・Visual Studio 2019

C#

1public class PlayerMove : MonoBehaviour 2{ 3 /// <summary> 4 /// 現在オブジェクトを掴んでいるかを判定するフラグ 5 /// </summary> 6 private bool isGrabbing; 7 8 /// <summary> 9 /// 3D空間上で位置判定を行う為の不可視の3Dステージ 10 /// </summary> 11 Plane Plane; 12 13 /// <summary> 14 /// プレイヤーオブジェクト(位置変更用) 15 /// </summary> 16 /// <remarks>transformでは位置書き換えはできないので用意</remarks> 17 private Transform PlayerObject; 18 19 /// <summary> 20 /// 移動範囲となる床 21 /// </summary> 22 private GameObject WorldPlane; 23 24 // Start is called before the first frame update 25 void Start() 26 { 27 //原点よりY軸1メートル上にPlaneを作成(配置済みのプレーンより上の位置に配置) 28 Plane = new Plane(Vector3.up, Vector3.up); 29 WorldPlane = GameObject.Find("WorldPlane"); 30 } 31 32 // Update is called once per frame 33 void Update() 34 { 35 //左クリックされたか 36 if (Input.GetMouseButtonDown(0)) 37 { 38 // カーソルからカメラ奥方向へのレイ(不可視の点光線)を定義 39 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 40 41 // レイを発射して照射先にオブジェクトが存在すればRaycastHitで出力 42 if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity)) 43 { 44 // タグ名で自機を判定 45 if (hit.collider.CompareTag("Player")) 46 { 47 //マウスドラッグ状態とする 48 isGrabbing = true; 49 // 掴んでいるRigidbodyオブジェクトを保存 50 PlayerObject = hit.rigidbody; 51 52 } 53 } 54 } 55 56 if (isGrabbing) 57 { 58 // カーソルからカメラ奥方向へのレイ(不可視の点光線)を定義 59 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 60 61 // Planeにレイを貫通させる 62 Plane.Raycast(ray, out float rayDistance); 63 64 // レイが当たった交点位置をプレイヤーオブジェクトの位置にする 65 PlayerObject.position = ray.GetPoint(rayDistance); 66 67 //ドラッグを解除したか(左クリックボタンが離れたか) 68 if (Input.GetMouseButtonUp(0)) 69 { 70 isGrabbing = false; 71 } 72 } 73 74 //床の範囲を取得 75 float areaX = WorldPlane.GetComponent<Renderer>().bounds.size.x; 76 float areaZ = WorldPlane.GetComponent<Renderer>().bounds.size.z; 77 78 //プレイヤーの移動範囲を床面だけに制限 79 transform.position = new Vector3( 80 Mathf.Clamp(transform.position.x, -(areaX / 2), areaX / 2 ), 81 Mathf.Clamp(transform.position.y, 0.5f, 10f), 82 Mathf.Clamp(transform.position.z, -(areaZ / 2), areaZ / 2) 83 ); 84 } 85 86 /// <summary> 87 /// 移動をFixedUpdateのタイミングで行う 88 /// </summary> 89 void FixedUpdate() 90 { 91 if (IsMouseGrabbing) 92 { 93 // カーソルからカメラ奥方向へのレイ(不可視の点光線)を定義 94 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 95 96 // プレイヤーの高さにPlaneを更新して、カメラの情報を元に地面判定して距離を取得 97 Plane.SetNormalAndPosition(Vector3.up, transform.localPosition); 98 99 // Planeにレイを貫通させる 100 if (Plane.Raycast(ray, out float rayDistance)) 101 { 102 // レイが当たった交点位置へ1フレームで移動するようにvelocityを設定する(他オブジェクトへの貫通移動制限) 103 PlayerObject.velocity = (ray.GetPoint(rayDistance) - PlayerObject.position) / Time.deltaTime; 104 105 // レイが当たった交点位置をプレイヤーオブジェクトの位置にする 106 //PlayerUnit.position = ray.GetPoint(rayDistance); 107 108 } 109 } 110 } 111}

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2021/08/02 06:06

試したとの事ですが、重力による下降が弱くなる原因はvelocity.yの値の問題だと思います。 public class Test : MonoBehaviour { Rigidbody rb; private void Start() { rb = GetComponent<Rigidbody>(); } private void FixedUpdate() { // float y = rb.velocity.y; rb.velocity = Vector3.zero; // rb.velocity = new Vector3(rb.velocity.x, y, rb.velocity.z); } } このコードの下降状態と同じなのでは? 上昇に関してはそこだけの問題ではないと思うのでこちらに。
guest

回答1

0

ベストアンサー

以前のご質問の折には言及しなかったのですが、Transformの操作とRigidbodyによる物理的運動の併用は要注意でして、普通はどちらか一方に絞って変な干渉を回避するのがセオリーかと思います。
Rigidbody - Unity マニュアル」には...

リジッドボディは、ゲームオブジェクトが物理エンジンの制御で動作することを可能にします。これにより、リアリスティックな衝突や様々な種類のジョイントのような挙動が可能になります。リジッドボディに力を加えることによってゲームオブジェクトを操作すると、Transform コンポーネントを直接調整するのとまったく違ったものになります。通常の場合は、同じゲームオブジェクトのリジッドボディと Transform を両方同時に操作することはしないでください。

との記述があります。おそらく「プレイヤーの移動範囲を床面だけに制限」の部分のTransform操作によってRigidbodyの速度が失われてしまったんじゃないでしょうか。

TransformRigidbodyのどちらで行くかですが、Transformでやるとなると周囲の地形をRaycastだとかで観測し、自前で適切に地形に沿った動きになるよう調整してやる必要がありそうです。現状のコードをベースに修正するならば、Rigidbodyに寄せていく方針の方がいいんじゃないかと思いました。スクリプトを下記のように変更してみましたが、いかがでしょうか?

lang

1using UnityEngine; 2 3public class PlayerMove : MonoBehaviour 4{ 5 /// <summary> 6 /// 現在オブジェクトを掴んでいるかを判定するフラグ 7 /// </summary> 8 private bool isGrabbing; 9 10 /// <summary> 11 /// プレイヤーオブジェクトのRigidbody(位置変更用) 12 /// </summary> 13 /// <remarks>transformでは位置書き換えはできないので用意</remarks> 14 private Rigidbody PlayerObject; 15 16 /// <summary> 17 /// 移動範囲となる床のRenderer 18 /// </summary> 19 private Renderer WorldPlaneRenderer; 20 21 /// <summary> 22 /// PlayerObjectの移動目標地点 23 /// </summary> 24 private Vector3 Destination; 25 26 // Start is called before the first frame update 27 void Start() 28 { 29 // シーン中からWorldPlaneを探すのに加え、Rendererの取得までStart内で済ませてしまう 30 // (ゲーム実行中にRendererの付け替えなんて普通はしないだろう...と思いまして、 31 // それならいちいちUpdate内でRendererを取得をせずともかまわないんじゃないかと 32 // 考えて、このようにしてしまいました) 33 WorldPlaneRenderer = GameObject.Find("WorldPlane").GetComponent<Renderer>(); 34 } 35 36 // Update is called once per frame 37 void Update() 38 { 39 //左クリックされたか 40 if (Input.GetMouseButtonDown(0)) 41 { 42 // カーソルからカメラ奥方向へのレイ(不可視の点光線)を定義 43 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 44 45 // レイを発射して照射先にオブジェクトが存在すればRaycastHitで出力 46 if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity)) 47 { 48 // タグ名で自機を判定 49 if (hit.collider.CompareTag("Player")) 50 { 51 //マウスドラッグ状態とする 52 isGrabbing = true; 53 54 // 掴んでいるRigidbodyオブジェクトを保存 55 PlayerObject = hit.rigidbody; 56 } 57 } 58 } 59 60 if (isGrabbing) 61 { 62 //ドラッグを解除したか(左クリックボタンが離れたか) 63 if (Input.GetMouseButtonUp(0)) 64 { 65 isGrabbing = false; 66 67 // ドラッグ解除時にはドラッグ対象物の水平速度をゼロにすることにした 68 // これをコメントアウトすると、ドラッグ中にオブジェクトに与えた 69 // 水平速度が残留するため、オブジェクトを素早くドラッグしながら 70 // 手を離すと、そのままオブジェクトが飛んでいくはず 71 PlayerObject.velocity = new Vector3(0.0f, PlayerObject.velocity.y, 0.0f); 72 } 73 else 74 { 75 // カーソルからカメラ奥方向へのレイ(不可視の点光線)を定義 76 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 77 78 // PlayerObjectの位置を通る水平面にレイを貫通させる 79 if (new Plane(Vector3.up, PlayerObject.position).Raycast(ray, out float rayDistance)) 80 { 81 // レイが当たった交点を目標地点とする 82 Destination = ray.GetPoint(rayDistance); 83 84 // 床の範囲を取得 85 Vector3 worldPlaneSize = WorldPlaneRenderer.bounds.size; 86 float areaX = worldPlaneSize.x; 87 float areaZ = worldPlaneSize.z; 88 89 // 目標地点を床面だけに制限 90 Destination.x = Mathf.Clamp(Destination.x, -(areaX / 2), areaX / 2); 91 Destination.y = Mathf.Clamp(Destination.y, 0.5f, 10f); 92 Destination.z = Mathf.Clamp(Destination.z, -(areaZ / 2), areaZ / 2); 93 } 94 } 95 } 96 } 97 98 /// <summary> 99 /// 移動をFixedUpdateのタイミングで行う 100 /// </summary> 101 void FixedUpdate() 102 { 103 if (isGrabbing) 104 { 105 // 目標位置へ1フレームで移動するようにvelocityを設定する(他オブジェクトへの貫通移動制限) 106 // ただし速度のY成分は上向きなら0とし、下向きなら適度な等速運動(さしあたり10m/s)とした 107 // 上向き運動の速度をゼロにすることで、斜面を登る際にめり込み解消の力積によってオブジェクトが 108 // 空に舞い上がるのを防止し、下向き運動にある程度の一定した速度を持たせることで、斜面を下る 109 // 際にはオブジェクトが地面に押しつけられ、斜面に沿って下らせることを狙った 110 Vector3 velocity = (Destination - PlayerObject.position) / Time.deltaTime; 111 velocity.y = PlayerObject.velocity.y >= 0.0f ? 0.0f : -10.0f; 112 PlayerObject.velocity = velocity; 113 } 114 } 115}

図1

なお、ドラッグしているオブジェクトは見た目はただの白い豆腐ですが、実は物理的形状は下図のように上3/4だけが直方体で、下1/4は球体になっています。

図2

こういった起伏のある地形を滑らせる場合、全体が単一のBoxColliderだと地形コライダーのつなぎ目だとかに簡単にひっかかってうっとうしかったため、足元をなめらかな曲面のSphereColliderにすることで段差を乗り越えやすくしてみました。ご参考になりますでしょうか?

投稿2021/08/03 11:03

Bongo

総合スコア10811

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

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

tride

2021/08/04 01:16

>Transformの操作とRigidbodyによる物理的運動の併用は要注意でして、 >普通はどちらか一方に絞って変な干渉を回避するのがセオリーかと思います。 知りませんでした。やはりそうなんですね。 混在してて挙動を制御する時にややこしかったので 恐らくどっちか片方に絞るんだろうなぁとは思ってたのですが、 後述のMathf.Clampでどうしたものかとこねくり回していました。 >void Start() >{ > // シーン中からWorldPlaneを探すのに加え、Rendererの取得までStart内で済ませてしまう > // (ゲーム実行中にRendererの付け替えなんて普通はしないだろう...と思いまして、 > // それならいちいちUpdate内でRendererを取得をせずともかまわないんじゃないかと > // 考えて、このようにしてしまいました) > WorldPlaneRenderer = GameObject.Find("WorldPlane").GetComponent<Renderer>(); >} 言われてみれば確かにその通りです。 RendererでUpdateの毎にGetComponentが動くのは無駄な処理だと思います。 まるで気が付いていませんでした。 >// ドラッグ解除時にはドラッグ対象物の水平速度をゼロにすることにした >// これをコメントアウトすると、ドラッグ中にオブジェクトに与えた >// 水平速度が残留するため、オブジェクトを素早くドラッグしながら >// 手を離すと、そのままオブジェクトが飛んでいくはず >PlayerObject.velocity = new Vector3(0.0f, PlayerObject.velocity.y, 0.0f); 確かにこれなら操作面で移動が楽になりそうです。 velocityの使い方としても、とても参考になりました。 >// レイが当たった交点を目標地点とする >Destination = ray.GetPoint(rayDistance); > >// 床の範囲を取得 >Vector3 worldPlaneSize = WorldPlaneRenderer.bounds.size; >float areaX = worldPlaneSize.x; >float areaZ = worldPlaneSize.z; > >// 目標地点を床面だけに制限 >Destination.x = Mathf.Clamp(Destination.x, -(areaX / 2), areaX / 2); >Destination.y = Mathf.Clamp(Destination.y, 0.5f, 10f); >Destination.z = Mathf.Clamp(Destination.z, -(areaZ / 2), areaZ / 2); レイの交点位置で範囲を制限することで、tranceformを使わずに済ませてると。 先でも書いていましたがこの部分でもかなり詰まっていたので、助かりました。 >// 目標位置へ1フレームで移動するようにvelocityを設定する(他オブジェクトへの貫通移動制限) >// ただし速度のY成分は上向きなら0とし、下向きなら適度な等速運動(さしあたり10m/s)とした >// 上向き運動の速度をゼロにすることで、斜面を登る際にめり込み解消の力積によってオブジェクトが >// 空に舞い上がるのを防止し、下向き運動にある程度の一定した速度を持たせることで、斜面を下る >// 際にはオブジェクトが地面に押しつけられ、斜面に沿って下らせることを狙った >Vector3 velocity = (Destination - PlayerObject.position) / Time.deltaTime; >velocity.y = PlayerObject.velocity.y >= 0.0f ? 0.0f : -10.0f; >PlayerObject.velocity = velocity; なるほど・・・。 速度のY成分が0以上なら上向き判定として0でめり込み解消と空に舞い上がるのを防止し、 0未満で下向き運動と判定して落下加速させて地面に押し付けると。 ははー・・・。思わず拍手しました。 とても分かりやすい説明で理解できました。 後は階段上の時にどうなるかですが、まずは教えて頂いた方法をソースに反映させて考えてみたいと思います。
tride

2021/08/06 13:39 編集

現在、ソースの内容を反映して動作を確認中なので今しばらくお時間頂けたらと思います。 現在以下の点で質量や抗力を調整したりして問題点を確認中です。 ・ドラッグすると速度に関わらずガクガクとプレイヤーオブジェクトがブレるが、時々何かの拍子にスムーズになる。 ・スロープと平面の境界線で引っかかるが、引っかからない時もある。 ちなみに先の返答で以下の事を記載していましたが、こちらを消すとマウスのレイ交点座標で位置制御を行っている都合、Plane外へすっ飛んでいくんですね。動かしていてちょっと動きを勘違いしておりました。 PlayerObject.velocity = new Vector3(0.0f, PlayerObject.velocity.y, 0.0f);
tride

2021/08/14 02:42

上記引っ掛かりの件は、単にyのゼロ点位置が僅かにズレていた事が原因でした。 よって、解決したとして問う質問はベストアンサーで終了します。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問