版本: Unity 6 (6000.0)
语言英语
  • C#

Physics.ContactEvent

建议修改

成功!

感谢您帮助我们提高 Unity 文档的质量。尽管我们无法接受所有提交,但我们确实会阅读用户提出的每项修改建议,并在适用的情况下进行更新。

关闭

提交失败

由于某些原因,您的修改建议无法提交。请<a>稍后再试</a>。感谢您抽出时间帮助我们提高 Unity 文档的质量。

关闭

取消

参数

要调用的委托。

描述

订阅此事件以读取物理模拟步骤期间发生的所有碰撞。

此事件的每个订阅者都会使用物理场景和 ContactPairHeader 的原生数组进行调用。每个 ContactPairHeader 包含一个 ContactPair 数组,每个 ContactPair 包含一个 ContactPairPoint 数组。

您可以使用此事件来加速碰撞处理,因为它比 MonoBehaviour.OnCollisionEnter 和其他消息快得多。您还可以使用此事件来调度使用提供的原生数组的作业。从此事件调度的作业必须在下一个 Physics.SimulatePhysicsScene.SimulatePhysicsScene.RunSimulationStagesRunSimulation 阶段调用之前完成。默认情况下,完成这些作业的理想位置是 MonoBehaviour.FixedUpdate

备注

  • 此事件中仅报告碰撞体碰撞,提供的缓冲区中不会出现任何触发事件。
  • 提供的缓冲区中的所有数据都是只读的。不允许写入。
  • 此事件在变换同步后调用。
  • 要接收来自碰撞体的碰撞,请将 Collider.providesContacts 属性设置为 true 或附加具有 OnCollisionStay 方法的 MonoBehaviour 脚本。
using System.Collections.Generic;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;

public class BounceScipt : MonoBehaviour { [SerializeField] private float m_ImpulseMultiplier = 5f;

private struct JobResultStruct { public int thisInstanceID; public int otherInstanceID; public Vector3 averageNormal; }

private NativeArray<JobResultStruct> m_ResultsArray; private int m_Count; private JobHandle m_JobHandle;

private readonly Dictionary<int, Rigidbody> m_RigidbodyMapping = new Dictionary<int, Rigidbody>();

private void OnEnable() { m_ResultsArray = new NativeArray<JobResultStruct>(16, Allocator.Persistent);

Physics.ContactEvent += Physics_ContactEvent;

var allRBs = GameObject.FindObjectsOfType<Rigidbody>(); foreach (var rb in allRBs) m_RigidbodyMapping.Add(rb.GetInstanceID(), rb); }

private void OnDisable() { m_JobHandle.Complete(); m_ResultsArray.Dispose();

Physics.ContactEvent -= Physics_ContactEvent;

m_RigidbodyMapping.Clear(); }

private void FixedUpdate() { m_JobHandle.Complete(); // The buffer is valid until the next Physics.Simulate() call. Be it internal or manual

// Do something with the contact data. // E.g. Add force based on the average contact normal for that body for (int i = 0; i < m_Count; i++) { var thisInstanceID = m_ResultsArray[i].thisInstanceID; var otherInstanceID = m_ResultsArray[i].otherInstanceID;

var rb0 = thisInstanceID != 0 ? m_RigidbodyMapping[thisInstanceID] : null; var rb1 = otherInstanceID != 0 ? m_RigidbodyMapping[otherInstanceID] : null;

if (rb0) rb0.AddForce(m_ResultsArray[i].averageNormal * m_ImpulseMultiplier, ForceMode.Impulse); if (rb1) rb1.AddForce(m_ResultsArray[i].averageNormal * -m_ImpulseMultiplier, ForceMode.Impulse); } }

private void Physics_ContactEvent(PhysicsScene scene, NativeArray<ContactPairHeader>.ReadOnly pairHeaders) { int n = pairHeaders.Length;

if (m_ResultsArray.Length < n) { m_ResultsArray.Dispose(); m_ResultsArray = new NativeArray<JobResultStruct>(Mathf.NextPowerOfTwo(n), Allocator.Persistent); }

m_Count = n;

AddForceJob job = new AddForceJob() { pairHeaders = pairHeaders, resultsArray = m_ResultsArray };

m_JobHandle = job.Schedule(n, 256); }

private struct AddForceJob : IJobParallelFor { [ReadOnly] public NativeArray<ContactPairHeader>.ReadOnly pairHeaders;

public NativeArray<JobResultStruct> resultsArray;

public void Execute(int index) { Vector3 averageNormal = Vector3.zero; int count = 0;

for (int j = 0; j < pairHeaders[index].pairCount; j++) { ref readonly var pair = ref pairHeaders[index].GetContactPair(j);

if (pair.IsCollisionExit) continue;

for (int k = 0; k < pair.ContactCount; k++) { ref readonly var contact = ref pair.GetContactPoint(k); averageNormal += contact.Normal; }

count += pair.ContactCount; }

if (count != 0) averageNormal /= (float)count;

JobResultStruct result = new JobResultStruct() { thisInstanceID = pairHeaders[index].bodyInstanceID, otherInstanceID = pairHeaders[index].otherBodyInstanceID, averageNormal = averageNormal };

resultsArray[index] = result; } } }

此脚本读取缓冲区中的所有碰撞,并计算每个 ContactPairHeader 的平均法线。然后根据结果应用力。