teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

1

プレイヤーのブロックへの干渉を完全になくせるかどうか検討

2018/07/22 03:36

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -3,4 +3,110 @@
3
3
 
4
4
  ![プレビュー](7edd6e6b6da1ae0e0afbee853893b56f.gif)
5
5
 
6
- ただし、ブロックが重いので、動いているブロックはかなり大きい運動量を持っていることになります。反発係数を調整したり、あるいはプレイヤーの速度に上限を設けるなど対策を施さないと、ブロックがゆっくりぶつかっただけなのにプレイヤーが遠くまで吹っ飛ばされる...なんてことになるかもしれません。
6
+ ただし、ブロックが重いので、動いているブロックはかなり大きい運動量を持っていることになります。反発係数を調整したり、あるいはプレイヤーの速度に上限を設けるなど対策を施さないと、ブロックがゆっくりぶつかっただけなのにプレイヤーが遠くまで吹っ飛ばされる...なんてことになるかもしれません。
7
+
8
+ ### 追記
9
+ ブロックへの干渉をなくす手はないか検討してみたのですが、ちょっと無理やり感のある方法になってしまいました...
10
+ 概要としては、[FixedUpdate](https://docs.unity3d.com/ja/current/ScriptReference/MonoBehaviour.FixedUpdate.html)内で...
11
+
12
+ - プレイヤーの[simulated](https://docs.unity3d.com/ja/current/ScriptReference/Rigidbody2D-simulated.html)を切ってから[Physics2D.Simulate](https://docs.unity3d.com/ja/current/ScriptReference/Physics2D.Simulate.html)で物理状態を1ステップ進める。
13
+ ...ブロックがプレイヤーの存在を無視した動きで移動・回転する。
14
+ - 全ブロックの[bodyType](https://docs.unity3d.com/ja/current/ScriptReference/Rigidbody2D-bodyType.html)をダイナミックからスタティックに切り替える。
15
+ ...ブロックを空間内に固定してしまう。なお、その結果ブロックの速度・角速度が失われてしまうので、後で復元するためにこれらを覚えておく。
16
+ - プレイヤーのsimulatedをtrueに戻して、物理状態を1ステップ進める。
17
+ ...このとき、プレイヤーにとってブロックは壁と同様の静的物体に見える。触れることができるが、押しても上に乗っても動かない。
18
+ - 全ブロックをスタティックからダイナミックに戻す。
19
+ ...ここで先ほど覚えておいた速度・角速度をRigidbody2Dに書き戻し、次回のFixedUpdateに備える。
20
+
21
+ といったような案です。先に述べたプレイヤーとブロックの質量差を大きくする方法と比べると、1回のFixedUpdateの中で2回もシミュレーションを実行させたり、スタティック・ダイナミックをころころ切り替えたり...と、シミュレーションエンジンには少々優しくない気はしますが、オブジェクトが多すぎなければなんとかなりそうでした。
22
+
23
+ 下記のようなスクリプトを空のゲームオブジェクトにアタッチしてシーンに置き、
24
+
25
+ ```C#
26
+ using System.Linq;
27
+ using UnityEngine;
28
+
29
+ public class BlockVersusPlayerPhysicsController : MonoBehaviour
30
+ {
31
+ // Rigidbody2Dと、その速度・角速度をペアで覚えておくための型
32
+ private class Rigidbody2DData
33
+ {
34
+ public float angularVelocity;
35
+ public Rigidbody2D rigidbody2D;
36
+ public Vector3 velocity;
37
+ }
38
+
39
+ private Rigidbody2DData[] blocks;
40
+ private Rigidbody2D[] players;
41
+
42
+ private void Start()
43
+ {
44
+ // シーン上のアクティブなブロック、プレイヤーを取得しておく
45
+ // さしあたりブロックやプレイヤーは増減しないと仮定して、Startで1回だけ取得していますが
46
+ // これらが新たに出現・消滅する場合は、その際にblocks・playersにも追加・削除して
47
+ // 配列を実際のシーン上のオブジェクトを反映するよう更新してやる必要があるでしょう
48
+ // また、私の場合ではブロックを別スクリプトのStart内で生成しているので、
49
+ // このスクリプトの実行はそれよりも後になるよう実行順を調整しています
50
+ var activeRigidbodies = FindObjectsOfType<Rigidbody2D>();
51
+ var blockLayer = LayerMask.NameToLayer("Block");
52
+ this.blocks = activeRigidbodies
53
+ .Where(r => r.gameObject.layer == blockLayer)
54
+ .Select(
55
+ r => new Rigidbody2DData
56
+ {
57
+ rigidbody2D = r,
58
+ angularVelocity = r.angularVelocity,
59
+ velocity = r.velocity
60
+ })
61
+ .ToArray();
62
+ var playerLayer = LayerMask.NameToLayer("Player");
63
+ this.players = activeRigidbodies
64
+ .Where(r => r.gameObject.layer == playerLayer)
65
+ .ToArray();
66
+
67
+ // Physics2Dの自動シミュレーションは切っておき、挙動はFixedUpdate内でコントロールする
68
+ Physics2D.autoSimulation = false;
69
+ }
70
+
71
+ private void FixedUpdate()
72
+ {
73
+ // プレイヤー側のシミュレーションを停止
74
+ foreach (var player in this.players)
75
+ {
76
+ player.simulated = false;
77
+ }
78
+
79
+ // シミュレーションを1ステップ進める
80
+ Physics2D.Simulate(Time.fixedDeltaTime);
81
+
82
+ // 全ブロックの現在の速度・角速度を保存してから、スタティックに変更
83
+ foreach (var block in this.blocks)
84
+ {
85
+ block.angularVelocity = block.rigidbody2D.angularVelocity;
86
+ block.velocity = block.rigidbody2D.velocity;
87
+ block.rigidbody2D.bodyType = RigidbodyType2D.Static;
88
+ }
89
+
90
+ // プレイヤー側のシミュレーションを再開
91
+ foreach (var player in this.players)
92
+ {
93
+ player.simulated = true;
94
+ }
95
+
96
+ // シミュレーションを1ステップ進める
97
+ Physics2D.Simulate(Time.fixedDeltaTime);
98
+
99
+ // 全ブロックをダイナミックに戻し、保存しておいた速度・角速度を復元
100
+ foreach (var block in this.blocks)
101
+ {
102
+ block.rigidbody2D.bodyType = RigidbodyType2D.Dynamic;
103
+ block.rigidbody2D.angularVelocity = block.angularVelocity;
104
+ block.rigidbody2D.velocity = block.velocity;
105
+ }
106
+ }
107
+ }
108
+ ```
109
+
110
+ 動かした様子はこうなりました。前回と異なりブロックの質量はプレイヤーと同程度の0.5~1kgなのですが、プレイヤーがブロックに飛び乗ってもブロックの動きに干渉しなくなりました。
111
+
112
+ ![プレビュー](c7b49da54a8eaa3cbfe662d4e39a6cbe.gif)