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

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

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

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

Unity

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

Q&A

解決済

1回答

1408閲覧

2Dアクションゲームで次のコードではなぜブロックにめりこんでしまうのか?

sushisuke6

総合スコア18

C#

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

Unity

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

1グッド

0クリップ

投稿2019/11/16 17:58

編集2019/11/17 17:48

C#で自前の物理演算を行って2Dアクションゲームを作っています。
次の画像に示すように、ブロックの角でめりこみが発生してしまいます。
画像はないですが、下からジャンプしてブロックの下側の角にぶつかったときにもめりこみが発生し、ブロックとのめりこみから逃げるようにキャラクターが上方向に勝手に持ち上げられてしまうような現象も起きてしまいました。
めりこみ?が発生している様子
めりこみ?が発生している様子(2)
コライダーの様子をシーンタブ内で見てみるとコライダーは衝突していないようにも見えるので、なおさら不思議です。

プレイヤーを動かしているコードは次の2つで、ブロックはTilemapを使ってColliderをGridにしています。また、そのうえでCompositeColliderにしております。プレイヤー側はBoxColliderを用いています。

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class KinematicObject : MonoBehaviour 6{ 7 Rigidbody2D rb2d; 8 9 public float minGroundNormalY = 0.65f; 10 public float gravityModifier = 1f; 11 12 protected bool isGrounded; 13 protected Vector2 groundNormal; 14 protected Vector2 targetVelocity; 15 protected Vector2 velocity; 16 protected ContactFilter2D contactFilter; 17 protected RaycastHit2D[] hitBuffer = new RaycastHit2D[16]; 18 protected List<RaycastHit2D> hitBufferList = new List<RaycastHit2D>(16); 19 20 protected const float minMoveDistance = 0.001f; 21 protected const float shellRadius = 0.01f; 22 protected const float minVelocityY = -10f; 23 24 void OnEnable() 25 { 26 rb2d = GetComponent<Rigidbody2D>(); 27 } 28 29 void Start() 30 { 31 contactFilter.useTriggers = false; 32 contactFilter.SetLayerMask(Physics2D.GetLayerCollisionMask(gameObject.layer)); 33 contactFilter.useLayerMask = true; 34 35 groundNormal = Vector2.up; 36 } 37 38 void Update() 39 { 40 targetVelocity = Vector2.zero; 41 ComputeVelocity(); 42 } 43 44 protected virtual void ComputeVelocity() 45 { 46 } 47 48 void FixedUpdate() 49 { 50 51 if (!isGrounded && velocity.y < 0) 52 { 53 velocity += 1.25f * gravityModifier * Physics2D.gravity * Time.deltaTime; 54 } 55 else 56 { 57 velocity += gravityModifier * Physics2D.gravity * Time.deltaTime; 58 } 59 60 velocity.x = targetVelocity.x; 61 velocity.y = velocity.y > minVelocityY ? velocity.y : minVelocityY; 62 63 isGrounded = false; 64 65 Vector2 deltaPosition = velocity * Time.deltaTime; 66 67 Vector2 moveAlongGround = new Vector2(groundNormal.y, -groundNormal.x); 68 69 Vector2 move = deltaPosition.x * moveAlongGround; 70 71 PerformMovement(move, false); 72 73 move = Vector2.up * deltaPosition.y; 74 PerformMovement(move, true); 75 76 } 77 78 void PerformMovement(Vector2 move, bool yMovement) 79 { 80 float distance = move.magnitude; 81 82 if (distance > minMoveDistance) 83 { 84 int count = rb2d.Cast(move, contactFilter, hitBuffer, distance + shellRadius); 85 hitBufferList.Clear(); 86 for (int i = 0; i < count; i++) 87 { 88 hitBufferList.Add(hitBuffer[i]); 89 } 90 91 for (int i = 0; i < hitBufferList.Count; i++) 92 { 93 Vector2 currentNormal = hitBufferList[i].normal; 94 if (currentNormal.y > minGroundNormalY) 95 { 96 isGrounded = true; 97 if (yMovement) 98 { 99 groundNormal = currentNormal; 100 currentNormal.x = 0; 101 } 102 } 103 float projection = Vector2.Dot(velocity, currentNormal); 104 if (projection < 0) 105 { 106 velocity = velocity - projection * currentNormal; 107 } 108 float modifiedDistance = hitBufferList[i].distance - shellRadius; 109 distance = modifiedDistance < distance ? modifiedDistance : distance; 110 } 111 } 112 rb2d.position = rb2d.position + distance * move.normalized; 113 } 114}

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class PlayerController : KinematicObject 6{ 7 public float maxSpeed = 5f; 8 public float jumpTakeOffSpeed = 8f; 9 10 private SpriteRenderer spriteRenderer; 11 private Animator animator; 12 13 void Awake() 14 { 15 spriteRenderer = GetComponent<SpriteRenderer>(); 16 animator = GetComponent<Animator>(); 17 } 18 19 protected override void ComputeVelocity() 20 { 21 Vector2 move = Vector2.zero; 22 move.x = Input.GetAxis("Horizontal"); 23 24 if (Input.GetButtonDown("Jump") && isGrounded) 25 { 26 velocity.y = jumpTakeOffSpeed; 27 } 28 else if (Input.GetButtonUp("Jump")) 29 { 30 if (velocity.y > 0) velocity.y = velocity.y * 0.5f; 31 } 32 33 bool flipSprite = (spriteRenderer.flipX ? (move.x > 0.01f) : (move.x < -0.01f)); 34 if(flipSprite){ 35 spriteRenderer.flipX = !spriteRenderer.flipX; 36 } 37 38 targetVelocity = move * maxSpeed; 39 40 animator.SetBool ("isGrounded", isGrounded); 41 animator.SetFloat ("velocityX", Mathf.Abs (velocity.x) / maxSpeed); 42 animator.SetFloat ("velocityY", velocity.y); 43 } 44}

わかりにくいコードでしたら申し訳ありません。以上のような現象の原因がコードにあるのかもわからずに困っております。
原因がわかる方がおりましたら回答をよろしくお願いします。

退会済みユーザー👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

コードをちゃんと読んでませんが怪しいのは多分以下。
rb2d.position = rb2d.position + distance * move.normalized;

positionの書き換えはいわばワープです。当たり判定を考慮しません。
なのでこの座標がブロックの内側を指していたらめり込みます。

恒常的なrigidbody.valocityの変更ももししているならあまり宜しくないので、全体的に見直した方がいいかもです。

投稿2019/11/17 06:41

sakura_hana

総合スコア11427

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

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

sushisuke6

2019/11/17 14:12

実際のところ、rigidbody2d.velocityは一切いじっておりません。 先に移動距離を計算しぶつかるcolliderがあった場合に、、PerformMovement関数で、colliderとの距離を計算してめり込まないように移動距離を再計算しています。なのでもしかするとこの距離計算のあたりが怪しいのですかね。ご指摘のおかげで思考が整理できました。ありがとうございます。 しかしながら本質的にどのような現象なのか、まだイマイチ把握できておりませんので、なにかありましたら、引き続き回答をよろしくお願いします。
sakura_hana

2019/11/17 15:03

rb2d.position = 〜 の方ですが、 「if (distance > minMoveDistance)」を満たさない限り衝突判定は行われていません。 なので(minMoveDistanceが極小とは言え)distanceがゼロでなければ、ワープ移動が発生することになります。 rigidbody.valocityは「もししているなら」という仮定の話なので(たまにそういう変なコードを見るので)やってないなら問題無いです。
sushisuke6

2019/11/17 17:21

なるほど!と思い、 if(distance > minMoveDistance)の{}内にrb2d.position = ~の記述を移してみましたが、現象は改善されませんでした。もしかするとBoxCollider同士がぶつかっているのが判定する数値計算の関係上、minMoveDistanceをもうすこし大きく設定するほうが安定するのでしょうか?それともrb2d.position以外にいい移動法があるのでしょうかね。
sakura_hana

2019/11/18 06:49

とすると衝突判定側の問題ですかね。Debug.Logなど駆使して何がどうなっているかの確認をしていくかしかないかなと思います。 画像を見た感じ、ぎりぎり側面が当たってそうです。 衝突位置のnormal取ってるんで条件分岐されているような気もするのですが、実際に数値がどうなっているか確かめてみてください。
sushisuke6

2019/11/25 23:43

いろいろとDebug.Logなどのデバッグ機能を用いて確認していきましたが、今の自分の技術力ではどうにも解決が難しいのと、私のプログラムのような衝突判定では、動くもの同士の衝突判定が致命的に難しいと感じたのでRigidbody2D.MovePositionを使うようなプログラムに書き換えることにしました。ご丁寧に回答をいただきましてありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問