2Dアクションゲームを作っています
Rigidbody2dとcoliderを持った重力の影響を受けず動くオブジェクト(ブロック)が複数と
同じくRigidbody2dとcoliderを持った重力の影響を受け動かせるオブジェクト(プレイヤー)があるとして次の状況が作りたいです
・ブロック同士は物理的に干渉しあう。
・プレイヤーはブロックを一切動かせない。
・ブロックはプレイヤーの足場や壁になる。
何かいい方法は無いでしょうか
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

回答1件
0
ベストアンサー
プレイヤーとブロックの質量差を十分大きくするというのはどうでしょうか?
例えば下図の場合、プレイヤーの質量が約0.5kgなのに対し、オレンジ色のブロックは500~1000kgにしてみました。これぐらい差があると、プレイヤーがブロックに飛び乗ってもブロックの動きにはほとんど影響を及ぼさないかと思います。
ただし、ブロックが重いので、動いているブロックはかなり大きい運動量を持っていることになります。反発係数を調整したり、あるいはプレイヤーの速度に上限を設けるなど対策を施さないと、ブロックがゆっくりぶつかっただけなのにプレイヤーが遠くまで吹っ飛ばされる...なんてことになるかもしれません。
追記
ブロックへの干渉をなくす手はないか検討してみたのですが、ちょっと無理やり感のある方法になってしまいました...
概要としては、FixedUpdate内で...
- プレイヤーのsimulatedを切ってからPhysics2D.Simulateで物理状態を1ステップ進める。
...ブロックがプレイヤーの存在を無視した動きで移動・回転する。 - 全ブロックのbodyTypeをダイナミックからスタティックに切り替える。
...ブロックを空間内に固定してしまう。なお、その結果ブロックの速度・角速度が失われてしまうので、後で復元するためにこれらを覚えておく。 - プレイヤーのsimulatedをtrueに戻して、物理状態を1ステップ進める。
...このとき、プレイヤーにとってブロックは壁と同様の静的物体に見える。触れることができるが、押しても上に乗っても動かない。 - 全ブロックをスタティックからダイナミックに戻す。
...ここで先ほど覚えておいた速度・角速度をRigidbody2Dに書き戻し、次回のFixedUpdateに備える。
といったような案です。先に述べたプレイヤーとブロックの質量差を大きくする方法と比べると、1回のFixedUpdateの中で2回もシミュレーションを実行させたり、スタティック・ダイナミックをころころ切り替えたり...と、シミュレーションエンジンには少々優しくない気はしますが、オブジェクトが多すぎなければなんとかなりそうでした。
下記のようなスクリプトを空のゲームオブジェクトにアタッチしてシーンに置き、
C#
1using System.Linq; 2using UnityEngine; 3 4public class BlockVersusPlayerPhysicsController : MonoBehaviour 5{ 6 // Rigidbody2Dと、その速度・角速度をペアで覚えておくための型 7 private class Rigidbody2DData 8 { 9 public float angularVelocity; 10 public Rigidbody2D rigidbody2D; 11 public Vector3 velocity; 12 } 13 14 private Rigidbody2DData[] blocks; 15 private Rigidbody2D[] players; 16 17 private void Start() 18 { 19 // シーン上のアクティブなブロック、プレイヤーを取得しておく 20 // さしあたりブロックやプレイヤーは増減しないと仮定して、Startで1回だけ取得していますが 21 // これらが新たに出現・消滅する場合は、その際にblocks・playersにも追加・削除して 22 // 配列を実際のシーン上のオブジェクトを反映するよう更新してやる必要があるでしょう 23 // また、私の場合ではブロックを別スクリプトのStart内で生成しているので、 24 // このスクリプトの実行はそれよりも後になるよう実行順を調整しています 25 var activeRigidbodies = FindObjectsOfType<Rigidbody2D>(); 26 var blockLayer = LayerMask.NameToLayer("Block"); 27 this.blocks = activeRigidbodies 28 .Where(r => r.gameObject.layer == blockLayer) 29 .Select( 30 r => new Rigidbody2DData 31 { 32 rigidbody2D = r, 33 angularVelocity = r.angularVelocity, 34 velocity = r.velocity 35 }) 36 .ToArray(); 37 var playerLayer = LayerMask.NameToLayer("Player"); 38 this.players = activeRigidbodies 39 .Where(r => r.gameObject.layer == playerLayer) 40 .ToArray(); 41 42 // Physics2Dの自動シミュレーションは切っておき、挙動はFixedUpdate内でコントロールする 43 Physics2D.autoSimulation = false; 44 } 45 46 private void FixedUpdate() 47 { 48 // プレイヤー側のシミュレーションを停止 49 foreach (var player in this.players) 50 { 51 player.simulated = false; 52 } 53 54 // シミュレーションを1ステップ進める 55 Physics2D.Simulate(Time.fixedDeltaTime); 56 57 // 全ブロックの現在の速度・角速度を保存してから、スタティックに変更 58 foreach (var block in this.blocks) 59 { 60 block.angularVelocity = block.rigidbody2D.angularVelocity; 61 block.velocity = block.rigidbody2D.velocity; 62 block.rigidbody2D.bodyType = RigidbodyType2D.Static; 63 } 64 65 // プレイヤー側のシミュレーションを再開 66 foreach (var player in this.players) 67 { 68 player.simulated = true; 69 } 70 71 // シミュレーションを1ステップ進める 72 Physics2D.Simulate(Time.fixedDeltaTime); 73 74 // 全ブロックをダイナミックに戻し、保存しておいた速度・角速度を復元 75 foreach (var block in this.blocks) 76 { 77 block.rigidbody2D.bodyType = RigidbodyType2D.Dynamic; 78 block.rigidbody2D.angularVelocity = block.angularVelocity; 79 block.rigidbody2D.velocity = block.velocity; 80 } 81 } 82}
動かした様子はこうなりました。前回と異なりブロックの質量はプレイヤーと同程度の0.5~1kgなのですが、プレイヤーがブロックに飛び乗ってもブロックの動きに干渉しなくなりました。
投稿2018/07/20 22:29
編集2018/07/22 03:36総合スコア10816
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。