以下の内容はhttps://hacchi-man.hatenablog.com/entry/2025/04/05/220000より取得しました。


【Unity】OverlapSphereNonAllocが当たったり当たらなかったりする謎 〜Transform変更と物理のズレ〜

Unityで Physics.OverlapSphereNonAlloc() を使ってるとき、当たってるはずなのに当たらない、あるいは逆に当たってるはずじゃないのに当たるという現象に遭遇したことはありませんか?

これは実はバグではなく、Transformと物理エンジン(Collider)の更新タイミングのズレが原因です。

どういうこと?

Unity の物理エンジンPhysX)では、Transform の変更が 即座に物理(Collider)に反映されるとは限りません

特に重要なのは次の点です:

Collider の最終的な更新(Transform の反映)は、FixedUpdate のあとに行われる

そのため、Transform を変更してすぐに Physics.OverlapSphereNonAlloc() などの物理クエリを呼び出すと、
古い状態の Collider を使って判定が行われてしまう可能性があります

❌ ありがちな失敗例

_target.localScale = Vector3.one * 2.0f; // スケールを変更
var hits = Physics.OverlapSphere(transform.position, 1.0f); // すぐ物理クエリ

この場合、スケールが変わったにもかかわらず、OverlapSphere には反映されていないことがある。

検証:Fixed Timestep を遅くしてみるとズレが発生しやすくなる

Unityでは物理エンジンの更新タイミングが Project Settings > Time > Fixed Timestep によって制御されています。

例: 通常: Fixed Timestep = 0.02 (50fps)

実験用: Fixed Timestep = 0.1 (10fps)

この値を 大きく(遅く)すると、Transform変更からCollider反映までのラグが明確になり、 SyncTransforms() を呼ばないと物理クエリが古い状態で行われてしまう現象が再現しやすくなります。

🧪 サンプルコード:ズレを可視化する

using UnityEngine;
using UnityEngine.UI;

public class OverlapSphereTest : MonoBehaviour
{
    [SerializeField]
    private Collider _targetCollider;
    [SerializeField]
    private Transform _targetTransform;
    
    [SerializeField]
    public float _sphereRadius = 1.0f;

    [SerializeField]
    private Slider _slider;
    
    private Collider[] hits = new Collider[10];
    private Material _targetMaterial;
    
    private void Awake()
    {
        var meshRenderer = _targetCollider.GetComponent<MeshRenderer>();
        _targetMaterial = meshRenderer.material;
        _slider.onValueChanged.AddListener(v =>
        {
            _targetTransform.localScale = Vector3.one * v;
        });
    }
    
    private void Update()
    {
        var hitCount = Physics.OverlapSphereNonAlloc(transform.position, _sphereRadius, hits);
        var hitTarget = false;
        for (int i = 0; i < hitCount; i++)
        {
            if (hits[i] == _targetCollider)
            {
                hitTarget = true;
                break;
            }
        }
            
        _targetMaterial.SetColor("_BaseColor", hitTarget ? Color.red : Color.white);
    }
}

🎥 動画で確認:色が変わるタイミングのズレ

以下は、先ほどのサンプルコードを Fixed Timestep = 0.1 に設定して実行したものです。

スケールが変わっても、実際に色が変化する(Overlapが当たったと判定される)までに遅れがあることが確認できます。

このように、Transformの変更が即時Colliderに反映されないことで、物理判定との間にズレが発生するのが視覚的に分かります。

🔧 Physics.OverlapSphereNonAlloc等の前に Physics.SyncTransforms() を呼び出せばこのズレは解消され、色が即座に切り替わるようになります。

まとめ

  • Unityの物理判定は、Transformの変更をすぐに反映しない場合がある
  • FixedUpdate のタイミングとズレると、古いCollider情報で物理クエリが走る
  • Physics.SyncTransforms() を使えば、即時にTransformの変更を物理エンジンへ反映できる

補足

  • 毎フレーム SyncTransforms() を呼ぶのはコストが高いので、必要なときだけ使うのが◎
  • Rigidbody を使っている場合も、Transform変更後は SyncTransforms() を考慮する
  • Collider.enabled = false/true の切り替えや、MeshCollider の再生成もタイミングに注意



以上の内容はhttps://hacchi-man.hatenablog.com/entry/2025/04/05/220000より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14