summaryrefslogtreecommitdiff
path: root/Runtime/Physics2D/Physics2DManager.cpp
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Physics2D/Physics2DManager.cpp
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/Physics2D/Physics2DManager.cpp')
-rw-r--r--Runtime/Physics2D/Physics2DManager.cpp1228
1 files changed, 1228 insertions, 0 deletions
diff --git a/Runtime/Physics2D/Physics2DManager.cpp b/Runtime/Physics2D/Physics2DManager.cpp
new file mode 100644
index 0000000..cf6dcd3
--- /dev/null
+++ b/Runtime/Physics2D/Physics2DManager.cpp
@@ -0,0 +1,1228 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Physics2D/CollisionListener2D.h"
+#include "Runtime/Physics2D/Physics2DSettings.h"
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+#include "Runtime/Core/Callbacks/PlayerLoopCallbacks.h"
+#include "Runtime/Geometry/Ray.h"
+#include "Runtime/Geometry/Plane.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/BaseClasses/MessageHandler.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Interfaces/IPhysics2D.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Math/Simd/math.h"
+#include "Runtime/Profiler/ProfilerStats.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+
+
+PROFILER_INFORMATION(gPhysics2DDynamicUpdateProfile, "Physics2D.DynamicUpdate", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DFixedUpdateProfile, "Physics2D.FixedUpdate", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DInterpolationsProfile, "Physics2D.Interpolation", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DSimProfile, "Physics2D.Simulate", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DUpdateTransformsProfile, "Physics2D.UpdateTransforms", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DCallbacksProfile, "Physics2D.Callbacks", kProfilerPhysics)
+PROFILER_INFORMATION(gLinecast2DProfile, "Physics2D.Linecast", kProfilerPhysics)
+PROFILER_INFORMATION(gLinecastAll2DProfile, "Physics2D.LinecastAll", kProfilerPhysics)
+PROFILER_INFORMATION(gRaycast2DProfile, "Physics2D.Raycast", kProfilerPhysics)
+PROFILER_INFORMATION(gRaycastAll2DProfile, "Physics2D.RaycastAll", kProfilerPhysics)
+PROFILER_INFORMATION(gOverlapPoint2DProfile, "Physics2D.OverlapPoint", kProfilerPhysics)
+PROFILER_INFORMATION(gOverlapPointAll2DProfile, "Physics2D.OverlapPointAll", kProfilerPhysics)
+PROFILER_INFORMATION(gOverlapCircle2DProfile, "Physics2D.OverlapCircle", kProfilerPhysics)
+PROFILER_INFORMATION(gOverlapCircleAll2DProfile, "Physics2D.OverlapCircleAll", kProfilerPhysics)
+PROFILER_INFORMATION(gOverlapArea2DProfile, "Physics2D.OverlapArea", kProfilerPhysics)
+PROFILER_INFORMATION(gOverlapAreaAll2DProfile, "Physics2D.OverlapAreaAll", kProfilerPhysics)
+PROFILER_INFORMATION(gGetRayIntersection2DProfile, "Physics2D.GetRayIntersection", kProfilerPhysics)
+PROFILER_INFORMATION(gGetRayIntersectionAll2DProfile, "Physics2D.GetRayIntersectionAll", kProfilerPhysics)
+
+
+// --------------------------------------------------------------------------
+
+
+inline bool CheckFixtureLayer(b2Fixture* fixture, int layerMask)
+{
+ DebugAssert(fixture);
+
+ Collider2D* col = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ if (!col)
+ return false; // no collider
+
+ GameObject* go = col->GetGameObjectPtr();
+ if (!go)
+ return false; // no GO
+
+ int goMask = go->GetLayerMask();
+ if ((goMask & layerMask) == 0)
+ return false; // ignoring this layer
+
+ return true; // all passed
+}
+
+// --------------------------------------------------------------------------
+
+
+inline bool CheckColliderDepth(Collider2D* collider, const float minDepth, const float maxDepth)
+{
+ DebugAssert(collider);
+
+ // Fetch depth of the collider.
+ const float depth = collider->GetComponent(Transform).GetPosition ().z;
+
+ // Check if in the depth range.
+ return !(depth < minDepth || depth > maxDepth);
+}
+
+
+// --------------------------------------------------------------------------
+
+
+inline void NormalizeDepthRange(float& minDepth, float& maxDepth)
+{
+ // Clamp any range bounds specified as +- infinity to real values.
+ minDepth = (minDepth == -std::numeric_limits<float>::infinity ()) ? -std::numeric_limits<float>::max () : minDepth;
+ maxDepth = (maxDepth == std::numeric_limits<float>::infinity ()) ? std::numeric_limits<float>::max () : maxDepth;
+
+ if (minDepth < maxDepth)
+ return;
+
+ std::swap (minDepth, maxDepth);
+}
+
+
+// --------------------------------------------------------------------------
+
+
+struct ContactFilter2D : public b2ContactFilter
+{
+ virtual bool ShouldCollide(b2Fixture* fixtureA, b2Fixture* fixtureB)
+ {
+ Collider2D* colliderA = reinterpret_cast<Collider2D*>(fixtureA->GetUserData ());
+ Collider2D* colliderB = reinterpret_cast<Collider2D*>(fixtureB->GetUserData ());
+
+ if (!colliderA->GetEnabled () || !colliderB->GetEnabled ())
+ return false;
+
+ // Fetch collider layers.
+ const int layerA = colliderA->GetGameObject ().GetLayer();
+ const int layerB = colliderB->GetGameObject ().GetLayer();
+
+ // Decide on the contact based upon the layer collision mask.
+ return GetPhysics2DSettings().GetLayerCollisionMask(layerA) & (1<<layerB);
+ }
+};
+
+
+// --------------------------------------------------------------------------
+
+
+struct ColliderHitsByDepthComparitor
+{
+ inline bool operator()(const Collider2D* a, const Collider2D* b)
+ {
+ return a->GetComponent(Transform).GetPosition ().z < b->GetComponent(Transform).GetPosition ().z;
+ }
+
+ inline int CompareDepth(const Collider2D* a, const Collider2D* b)
+ {
+ const float depthA = a->GetComponent(Transform).GetPosition ().z;
+ const float depthB = b->GetComponent(Transform).GetPosition ().z;
+
+ if (depthA < depthB) return -1;
+ if (depthA > depthB) return 1;
+ return 0;
+ }
+} m_CompareDepth;
+
+
+// --------------------------------------------------------------------------
+
+
+struct RayHitsByDepthComparitor
+{
+ inline bool operator()(const RaycastHit2D& a, const RaycastHit2D& b)
+ {
+ return a.collider->GetComponent(Transform).GetPosition ().z < b.collider->GetComponent(Transform).GetPosition ().z;
+ }
+};
+
+struct RayHitsByInverseDepthComparitor
+{
+ inline bool operator()(const RaycastHit2D& a, const RaycastHit2D& b)
+ {
+ return a.collider->GetComponent(Transform).GetPosition ().z > b.collider->GetComponent(Transform).GetPosition ().z;
+ }
+};
+
+
+// --------------------------------------------------------------------------
+
+
+class Raycast2DQuery : public b2RayCastCallback
+{
+public:
+ Raycast2DQuery(const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<RaycastHit2D>& outHits)
+ : m_PointA(pointA)
+ , m_PointB(pointB)
+ , m_LayerMask(layerMask)
+ , m_MinDepth(minDepth)
+ , m_MaxDepth(maxDepth)
+ , m_Hits(outHits)
+ {
+ NormalizeDepthRange(m_MinDepth, m_MaxDepth);
+ }
+
+ int RunQuery ()
+ {
+ // Calculate if the ray has zero-length or not.
+ const bool rayHasMagnitude = SqrMagnitude (m_PointB - m_PointA) > b2_epsilon * b2_epsilon;
+
+ // Perform a discrete check at the start-point (Box2D does not discover these for a ray-cast).
+ dynamic_array<Collider2D*> colliderHits(kMemTempAlloc);
+ if (GetPhysics2DManager ().OverlapPointAll (m_PointA, m_LayerMask, m_MinDepth, m_MaxDepth, &colliderHits) > 0)
+ {
+ const Vector2f collisionNormal = rayHasMagnitude ? NormalizeFast (m_PointA-m_PointB) : Vector2f::zero;
+ for (dynamic_array<Collider2D*>::iterator colliderItr = colliderHits.begin (); colliderItr != colliderHits.end (); ++colliderItr)
+ {
+ RaycastHit2D rayHit;
+ rayHit.collider = *colliderItr;
+ rayHit.point = m_PointA;
+ rayHit.normal = collisionNormal;
+ rayHit.fraction = 0.0f;
+ m_Hits.push_back (rayHit);
+ }
+ }
+
+ if (rayHasMagnitude)
+ {
+ // Perform the ray-cast.
+ GetPhysics2DWorld ()->RayCast (this, b2Vec2(m_PointA.x, m_PointA.y), b2Vec2(m_PointB.x, m_PointB.y));
+
+ // Sort the hits by fraction.
+ std::sort (m_Hits.begin(), m_Hits.end(), RaycastHitsByFractionComparitor());
+ }
+
+ return m_Hits.size ();
+ }
+
+ virtual float32 ReportFixture (b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float32 fraction)
+ {
+ // Handle whether ray-casts are hitting triggers or not.
+ if (fixture->IsSensor () && !GetPhysics2DSettings ().GetRaycastsHitTriggers ())
+ return -1.0f;
+
+ // Ignore if not in the selected fixture layer.
+ if (!CheckFixtureLayer (fixture, m_LayerMask))
+ return -1.0f; // ignore and continue
+
+ // Ignore if not in the selected depth-range.
+ Collider2D* collider = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ if (!CheckColliderDepth (collider, m_MinDepth, m_MaxDepth))
+ return -1.0f; // ignore and continue;
+
+ RaycastHit2D hit;
+ hit.point.Set (point.x, point.y);
+ hit.normal.Set (normal.x, normal.y);
+ hit.fraction = fraction;
+ hit.collider = collider;
+
+ // For chain colliders, Box2D will report all individual segments that are hit; also similar situation
+ // when using composite convex colliders. Try searching for a hit with same Collider2D, and replace info
+ // if closer hit. Unfortunately, this is is O(n).
+ for (size_t i = 0, n = m_Hits.size(); i != n; ++i)
+ {
+ if (m_Hits[i].collider == collider)
+ {
+ if (fraction < m_Hits[i].fraction)
+ m_Hits[i] = hit;
+
+ return 1.0f; // continue
+ }
+ }
+
+ // Add new hit
+ m_Hits.push_back (hit);
+
+ return 1.0f; // continue
+ }
+
+private:
+ struct RaycastHitsByFractionComparitor
+ {
+ inline bool operator()(const RaycastHit2D& a, const RaycastHit2D& b)
+ {
+ return a.fraction < b.fraction;
+ }
+ };
+
+private:
+ int m_LayerMask;
+ float m_MinDepth;
+ float m_MaxDepth;
+ Vector2f m_PointA;
+ Vector2f m_PointB;
+ dynamic_array<RaycastHit2D>& m_Hits;
+};
+
+
+// --------------------------------------------------------------------------
+
+
+class OverlapPointQuery2D : public b2QueryCallback
+{
+public:
+ OverlapPointQuery2D(const Vector2f& point, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>& outHits)
+ : m_LayerMask(layerMask)
+ , m_MinDepth(minDepth)
+ , m_MaxDepth(maxDepth)
+ , m_Hits(outHits)
+ {
+ NormalizeDepthRange(m_MinDepth, m_MaxDepth);
+
+ m_Point.Set (point.x, point.y);
+ }
+
+ int RunQuery ()
+ {
+ // Reset results.
+ m_Hits.clear();
+
+ b2AABB aabb;
+ aabb.lowerBound = aabb.upperBound = m_Point;
+ GetPhysics2DWorld ()->QueryAABB (this, aabb);
+
+ // Sort the hits by depth.
+ std::sort (m_Hits.begin(), m_Hits.end(), ColliderHitsByDepthComparitor());
+
+ return m_Hits.size ();
+ }
+
+ virtual bool ReportFixture (b2Fixture* fixture)
+ {
+ // Handle whether ray-casts are hitting triggers or not.
+ if (fixture->IsSensor () && !GetPhysics2DSettings ().GetRaycastsHitTriggers ())
+ return true;
+
+ // Ignore if not in the selected fixture layer.
+ if (!CheckFixtureLayer (fixture, m_LayerMask))
+ return true; // ignore and continue
+
+ // Ignore if not in the selected depth-range.
+ Collider2D* collider = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ if (!CheckColliderDepth (collider, m_MinDepth, m_MaxDepth))
+ return true; // ignore and continue;
+
+ // Ignore if we've already selected this collider and it has a higher depth.
+ for (size_t i = 0; i != m_Hits.size (); ++i)
+ {
+ if (m_Hits[i] == collider)
+ {
+ if (m_CompareDepth.CompareDepth (m_Hits[i], collider) == 1)
+ m_Hits[i] = collider;
+ return true;
+ }
+ }
+
+ // Test point against fixture.
+ if (!fixture->TestPoint (m_Point))
+ return true;
+
+ // Add new hit
+ m_Hits.push_back (collider);
+
+ return true;
+ }
+
+private:
+ b2Vec2 m_Point;
+ int m_LayerMask;
+ float m_MinDepth;
+ float m_MaxDepth;
+ dynamic_array<Collider2D*>& m_Hits;
+};
+
+
+// --------------------------------------------------------------------------
+
+
+class OverlapCircleQuery2D : public b2QueryCallback
+{
+public:
+ OverlapCircleQuery2D(const Vector2f& point, const float radius, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>& outHits)
+ : m_LayerMask(layerMask)
+ , m_MinDepth(minDepth)
+ , m_MaxDepth(maxDepth)
+ , m_Hits(outHits)
+ {
+ NormalizeDepthRange(m_MinDepth, m_MaxDepth);
+
+ // Extremely small radii indicates a point query.
+ if (radius < 0.00001f)
+ {
+ m_Point.Set (point.x, point.y);
+ m_AABB.lowerBound = m_AABB.upperBound = m_Point;
+ m_PointQuery = true;
+ }
+ else
+ {
+ // Calculate circle shape and its AABB.
+ m_CircleShape.m_p.Set (point.x, point.y);
+ m_CircleShape.m_radius = radius;
+ m_QueryTransform.SetIdentity ();
+ m_CircleShape.ComputeAABB( &m_AABB, m_QueryTransform, 0 );
+ m_PointQuery = false;
+ }
+ }
+
+
+ int RunQuery ()
+ {
+ // Reset results.
+ m_Hits.clear();
+
+ GetPhysics2DWorld ()->QueryAABB (this, m_AABB);
+
+ // Sort the hits by depth.
+ std::sort (m_Hits.begin(), m_Hits.end(), ColliderHitsByDepthComparitor());
+
+ return m_Hits.size ();
+ }
+
+ virtual bool ReportFixture (b2Fixture* fixture)
+ {
+ // Handle whether ray-casts are hitting triggers or not.
+ if (fixture->IsSensor () && !GetPhysics2DSettings ().GetRaycastsHitTriggers ())
+ return true;
+
+ // Ignore if not in the selected fixture layer.
+ if (!CheckFixtureLayer (fixture, m_LayerMask))
+ return true; // ignore and continue
+
+ // Ignore if not in the selected depth-range.
+ Collider2D* collider = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ if (!CheckColliderDepth (collider, m_MinDepth, m_MaxDepth))
+ return true; // ignore and continue;
+
+ // Ignore if we've already selected this collider and it has a higher depth.
+ for (size_t i = 0; i != m_Hits.size (); ++i)
+ {
+ if (m_Hits[i] == collider)
+ {
+ if (m_CompareDepth.CompareDepth (m_Hits[i], collider) == 1)
+ m_Hits[i] = collider;
+ return true;
+ }
+ }
+
+ if (m_PointQuery)
+ {
+ // Test point against fixture.
+ if (!fixture->TestPoint (m_Point))
+ return true;
+ }
+ else
+ {
+ // Test circle against fixture.
+ if ( !b2TestOverlap( &m_CircleShape, 0, fixture->GetShape (), 0, m_QueryTransform, fixture->GetBody ()->GetTransform () ) )
+ return true;
+ }
+
+ // Add new hit
+ m_Hits.push_back (collider);
+
+ return true;
+ }
+
+private:
+ int m_LayerMask;
+ float m_MinDepth;
+ float m_MaxDepth;
+ b2Vec2 m_Point;
+ b2CircleShape m_CircleShape;
+ b2AABB m_AABB;
+ b2Transform m_QueryTransform;
+ dynamic_array<Collider2D*>& m_Hits;
+ bool m_PointQuery;
+};
+
+
+// --------------------------------------------------------------------------
+
+
+class OverlapAreaQuery2D : public b2QueryCallback
+{
+public:
+ OverlapAreaQuery2D(Vector2f pointA, Vector2f pointB, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>& outHits)
+ : m_LayerMask(layerMask)
+ , m_MinDepth(minDepth)
+ , m_MaxDepth(maxDepth)
+ , m_Hits(outHits)
+ {
+ NormalizeDepthRange(m_MinDepth, m_MaxDepth);
+
+ // Normalize the points.
+ if (pointA.x > pointB.x)
+ std::swap (pointA.x, pointB.x);
+ if (pointA.y > pointB.y)
+ std::swap (pointA.y, pointB.y);
+
+ // Calculate the AABB.
+ m_PolygonAABB.lowerBound.Set (pointA.x, pointA.y);
+ m_PolygonAABB.upperBound.Set (pointB.x, pointB.y);
+
+ // Calculate polygon shape.
+ b2Vec2 verts[4];
+ verts[0].Set( pointA.x, pointA.y );
+ verts[1].Set( pointB.x, pointA.y );
+ verts[2].Set( pointB.x, pointB.y );
+ verts[3].Set( pointA.x, pointB.y );
+ m_PolygonShape.Set( verts, 4 );
+
+ m_QueryTransform.SetIdentity ();
+ }
+
+ int RunQuery ()
+ {
+ // Reset results.
+ m_Hits.clear();
+
+ // Finish if AABB is invalid.
+ if (!m_PolygonAABB.IsValid())
+ return 0;
+
+ GetPhysics2DWorld ()->QueryAABB (this, m_PolygonAABB);
+
+ // Sort the hits by depth.
+ std::sort (m_Hits.begin(), m_Hits.end(), ColliderHitsByDepthComparitor());
+
+ return m_Hits.size ();
+ }
+
+ virtual bool ReportFixture (b2Fixture* fixture)
+ {
+ // Handle whether ray-casts are hitting triggers or not.
+ if (fixture->IsSensor () && !GetPhysics2DSettings ().GetRaycastsHitTriggers ())
+ return true;
+
+ // Ignore if not in the selected fixture layer.
+ if (!CheckFixtureLayer (fixture, m_LayerMask))
+ return true; // ignore and continue
+
+ // Ignore if not in the selected depth-range.
+ Collider2D* collider = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ if (!CheckColliderDepth (collider, m_MinDepth, m_MaxDepth))
+ return true; // ignore and continue;
+
+ // Ignore if we've already selected this collider and it has a higher depth.
+ for (size_t i = 0; i != m_Hits.size (); ++i)
+ {
+ if (m_Hits[i] == collider)
+ {
+ if (m_CompareDepth.CompareDepth (m_Hits[i], collider) == 1)
+ m_Hits[i] = collider;
+ return true;
+ }
+ }
+
+ // Test polygon against fixture.
+ if ( !b2TestOverlap( &m_PolygonShape, 0, fixture->GetShape (), 0, m_QueryTransform, fixture->GetBody ()->GetTransform () ) )
+ return true;
+
+ // Add new hit
+ m_Hits.push_back (collider);
+
+ return true;
+ }
+
+private:
+ int m_LayerMask;
+ float m_MinDepth;
+ float m_MaxDepth;
+ b2PolygonShape m_PolygonShape;
+ b2AABB m_PolygonAABB;
+ b2Transform m_QueryTransform;
+ dynamic_array<Collider2D*>& m_Hits;
+};
+
+
+// --------------------------------------------------------------------------
+
+
+struct Physics2DState
+{
+ Physics2DState() :
+ m_PhysicsManager(NULL),
+ m_PhysicsWorld(NULL),
+ m_PhysicsGroundBody(NULL) { }
+
+ void Initialize();
+ void Cleanup();
+
+ Physics2DManager* m_PhysicsManager;
+ b2World* m_PhysicsWorld;
+ b2Body* m_PhysicsGroundBody;
+ CollisionListener2D m_Collisions;
+ ContactFilter2D m_ContactFilter;
+};
+
+static Physics2DState g_Physics2DState;
+
+
+// --------------------------------------------------------------------------
+
+void Physics2DState::Initialize()
+{
+ Assert (m_PhysicsManager == NULL);
+ Assert (m_PhysicsWorld == NULL);
+
+ m_PhysicsManager = new Physics2DManager ();
+ SetIPhysics2D (m_PhysicsManager);
+
+ // Initialize the Box2D physics world.
+ b2Vec2 gravity(0.0f, -9.81f);
+ m_PhysicsWorld = new b2World (gravity);
+ m_PhysicsWorld->SetContactListener (&m_Collisions);
+ m_PhysicsWorld->SetContactFilter (&m_ContactFilter);
+
+ // Initialize the static ground-body.
+ b2BodyDef groundBodyDef;
+ m_PhysicsGroundBody = GetPhysics2DWorld()->CreateBody (&groundBodyDef);
+
+ // Register physics updates.
+ REGISTER_PLAYERLOOP_CALL (Physics2DFixedUpdate, GetPhysics2DManager().FixedUpdate());
+ REGISTER_PLAYERLOOP_CALL (Physics2DUpdate, GetPhysics2DManager().DynamicUpdate());
+ REGISTER_PLAYERLOOP_CALL (Physics2DResetInterpolatedTransformPosition, GetPhysics2DManager().ResetInterpolations());
+}
+
+
+void Physics2DState::Cleanup()
+{
+ delete m_PhysicsWorld;
+ m_PhysicsWorld = NULL;
+
+ delete m_PhysicsManager;
+ m_PhysicsManager = NULL;
+ SetIPhysics2D (NULL);
+}
+
+
+void InitializePhysics2DManager ()
+{
+ g_Physics2DState.Initialize ();
+}
+
+
+void CleanupPhysics2DManager ()
+{
+ g_Physics2DState.Cleanup ();
+}
+
+
+b2World* GetPhysics2DWorld ()
+{
+ Assert (g_Physics2DState.m_PhysicsWorld);
+ return g_Physics2DState.m_PhysicsWorld;
+}
+
+
+b2Body* GetPhysicsGroundBody ()
+{
+ Assert (g_Physics2DState.m_PhysicsGroundBody);
+ return g_Physics2DState.m_PhysicsGroundBody;
+}
+
+
+Physics2DManager& GetPhysics2DManager()
+{
+ Assert (g_Physics2DState.m_PhysicsManager);
+ return *g_Physics2DState.m_PhysicsManager;
+}
+
+
+// --------------------------------------------------------------------------
+
+
+Physics2DManager::Physics2DManager()
+ : m_RigidbodyTransformMessageEnabled(true)
+{
+ Object::FindAllDerivedClasses (ClassID (Collider2D), &m_AllCollider2DTypes);
+}
+
+
+void Physics2DManager::FixedUpdate()
+{
+ PROFILER_AUTO(gPhysics2DFixedUpdateProfile, NULL)
+
+ // Gather interpolation info.
+ {
+ PROFILER_AUTO(gPhysics2DInterpolationsProfile, NULL)
+
+ // Store interpolated position
+ for (InterpolatedBodiesIterator i=m_InterpolatedBodies.begin();i!=m_InterpolatedBodies.end();++i)
+ {
+ Rigidbody2D* rigidBody = i->body;
+ i->disabled = 0;
+
+ if ( rigidBody->GetBody() == NULL )
+ continue;
+
+ if (rigidBody->GetInterpolation() == kInterpolate2D)
+ {
+ i->position = rigidBody->GetBodyPosition();
+ i->rotation = rigidBody->GetBodyRotation();
+ }
+ }
+ }
+
+ // simulate
+ {
+ PROFILER_AUTO(gPhysics2DSimProfile, NULL)
+
+ const Physics2DSettings& settings = GetPhysics2DSettings();
+ g_Physics2DState.m_PhysicsWorld->Step (GetTimeManager().GetFixedDeltaTime(), settings.GetVelocityIterations(), settings.GetPositionIterations());
+ }
+
+ // update data back from simulation
+ {
+ PROFILER_AUTO(gPhysics2DUpdateTransformsProfile, NULL)
+
+ // Disable rigid body, collider & joint transform changed message handler.
+ // Turns off setting the pose while we are fetching the state.
+ SetTransformMessageEnabled (false);
+
+ // Update position / rotation of all rigid bodies
+ b2Body* body = GetPhysics2DWorld()->GetBodyList();
+ while (body != NULL)
+ {
+ Rigidbody2D* rb = (Rigidbody2D*)body->GetUserData();
+ if (rb != NULL && body->GetType() != b2_staticBody && body->IsAwake())
+ {
+ GameObject& go = rb->GetGameObject();
+ Transform& transform = go.GetComponent (Transform);
+
+ // Calculate new position.
+ const b2Vec2& pos2 = body->GetPosition();
+ Vector3f pos3 = transform.GetPosition();
+ pos3.x = pos2.x;
+ pos3.y = pos2.y;
+
+ // Calculate new rotation.
+ Vector3f localEuler = QuaternionToEuler (transform.GetLocalRotation ());
+ localEuler.z = body->GetAngle ();
+
+ // Update position and rotation.
+ transform.SetPositionAndRotation (pos3, EulerToQuaternion(localEuler));
+ }
+
+ body = body->GetNext();
+ }
+
+ // Enable transform change notifications back.
+ SetTransformMessageEnabled (true);
+ }
+
+ // do script callbacks
+ {
+ PROFILER_AUTO(gPhysics2DCallbacksProfile, NULL)
+
+ const bool oldDisableDestruction = GetDisableImmediateDestruction();
+ SetDisableImmediateDestruction(true);
+
+ // report collisions and triggers
+ g_Physics2DState.m_Collisions.ReportCollisions();
+
+ SetDisableImmediateDestruction(oldDisableDestruction);
+ }
+}
+
+
+void Physics2DManager::DynamicUpdate()
+{
+ PROFILER_AUTO(gPhysics2DDynamicUpdateProfile, NULL);
+
+ {
+ PROFILER_AUTO(gPhysics2DInterpolationsProfile, NULL)
+
+ // Disable rigid body / collider transform changed message handler.
+ // Turns off setting the pose while we are fetching the state.
+ SetTransformMessageEnabled (false);
+
+ // Also disable rigidbody (2D) transform changed message, otherwise interpolation will affect physics results.
+ // This is not done in SetTransformMessageEnabled, as we need the rigidbody (2D) messages in the physics fixed update
+ // so kinematic child rigid-bodies (2D) are moved with their parents.
+ GameObject::GetMessageHandler ().SetMessageEnabled (ClassID(Rigidbody2D), kTransformChanged.messageID, false);
+
+ // Interpolation time is [0...1] between the two steps
+ // Extrapolation time the delta time since the last fixed step
+ const float dynamicTime = GetTimeManager().GetCurTime();
+ const float step = GetTimeManager().GetFixedDeltaTime();
+ const float fixedTime = GetTimeManager().GetFixedTime();
+
+ // Update interpolated position
+ for (InterpolatedBodiesIterator i=m_InterpolatedBodies.begin ();i!=m_InterpolatedBodies.end ();++i)
+ {
+ Rigidbody2D* rigidBody = i->body;
+ const RigidbodyInterpolation2D interpolation = rigidBody->GetInterpolation ();
+
+ // Ignore if disabled, no interpolation is specified or the body is sleeping.
+ if (i->disabled || interpolation == kNoInterpolation2D || rigidBody->IsSleeping ())
+ continue;
+
+ // Interpolate between this physics and last physics frame.
+ if (interpolation == kInterpolate2D)
+ {
+ const float interpolationTime = clamp01 ((dynamicTime - fixedTime) / step);
+
+ // Interpolate position.
+ const Vector3f position = Lerp (i->position, rigidBody->GetBodyPosition(), interpolationTime);
+
+ // Interpolate rotation.
+ const Quaternionf rotation = Slerp (i->rotation, rigidBody->GetBodyRotation (), interpolationTime);
+
+ // Update position/rotation.
+ rigidBody->GetComponent(Transform).SetPositionAndRotationSafe (position, rotation);
+ continue;
+ }
+
+ // Extrapolate current position using velocity.
+ else if (interpolation == kExtrapolate2D)
+ {
+ const float extrapolationTime = dynamicTime - fixedTime;
+
+ // Interpolate position.
+ const Vector2f linearVelocity2D = rigidBody->GetVelocity();
+ const Vector3f linearVelocity3D(linearVelocity2D.x * extrapolationTime, linearVelocity2D.y * extrapolationTime, 0.0f);
+ const Vector3f position = rigidBody->GetBodyPosition () + linearVelocity3D;
+
+ // Interpolate rotation.
+ const float angularVelocity2D = rigidBody->GetAngularVelocity ();
+ const Quaternionf rotation = CompareApproximately (angularVelocity2D, 0.0f) ? AngularVelocityToQuaternion(Vector3f(0.0f, 0.0f, angularVelocity2D), extrapolationTime) * rigidBody->GetBodyRotation() : rigidBody->GetBodyRotation();
+
+ // Update position/rotation.
+ rigidBody->GetComponent(Transform).SetPositionAndRotationSafe (position, rotation);
+ continue;
+ }
+ }
+
+ // Re-enable rigidbody (2D) transform changed messages.
+ GameObject::GetMessageHandler ().SetMessageEnabled (ClassID(Rigidbody2D), kTransformChanged.messageID, true);
+
+ // Enable transform change notifications back.
+ SetTransformMessageEnabled (true);
+ }
+}
+
+
+void Physics2DManager::ResetInterpolations ()
+{
+ PROFILER_AUTO(gPhysics2DInterpolationsProfile, NULL)
+
+ for (InterpolatedBodiesIterator i=m_InterpolatedBodies.begin ();i!=m_InterpolatedBodies.end ();++i)
+ {
+ Rigidbody2D* rigidBody = i->body;
+ if (rigidBody->GetBody() == NULL || rigidBody->IsSleeping ())
+ continue;
+
+ Transform& transform = rigidBody->GetComponent(Transform);
+
+ Vector3f pos = rigidBody->GetBodyPosition();
+ Quaternionf rot = rigidBody->GetBodyRotation();
+ transform.SetPositionAndRotationSafeWithoutNotification (pos, rot);
+ }
+}
+
+
+int Physics2DManager::Linecast (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, RaycastHit2D* outHits, const int outHitsSize)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gLinecast2DProfile, NULL)
+
+ // Finish if no available hits capacity.
+ if (outHitsSize == 0)
+ return 0;
+
+ dynamic_array<RaycastHit2D> raycastHits(kMemTempAlloc);
+ Raycast2DQuery query (pointA, pointB, layerMask, minDepth, maxDepth, raycastHits);
+ const int resultCount = query.RunQuery ();
+
+ // Transfer the first n-results.
+ const int allowedResultCount = std::min (resultCount, outHitsSize);
+ for (int index = 0; index < allowedResultCount; ++index)
+ *(outHits++) = raycastHits[index];
+
+ return allowedResultCount;
+}
+
+
+int Physics2DManager::LinecastAll (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<RaycastHit2D>* outHits)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gLinecastAll2DProfile, NULL)
+
+ Raycast2DQuery query (pointA, pointB, layerMask, minDepth, maxDepth, *outHits);
+ return query.RunQuery ();
+}
+
+
+int Physics2DManager::Raycast (const Vector2f& origin, const Vector2f& direction, const float distance, const int layerMask, const float minDepth, const float maxDepth, RaycastHit2D* outHits, const int outHitsSize)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gRaycast2DProfile, NULL)
+
+ // Finish if no available hits capacity.
+ if (outHitsSize == 0)
+ return 0;
+
+ // Calculate destination point.
+ const bool isInfiniteDistance = distance == std::numeric_limits<float>::infinity();
+ Vector2f normalizedDirection = NormalizeFast (direction);
+ const Vector2f pointB = origin + (normalizedDirection * (isInfiniteDistance ? PHYSICS_2D_RAYCAST_DISTANCE : distance));
+
+ dynamic_array<RaycastHit2D> raycastHits(kMemTempAlloc);
+ Raycast2DQuery query (origin, pointB, layerMask, minDepth, maxDepth, raycastHits);
+ const int resultCount = query.RunQuery ();
+
+ // Transfer the first n-results.
+ const int allowedResultCount = std::min (resultCount, outHitsSize);
+ for (int index = 0; index < allowedResultCount; ++index)
+ {
+ RaycastHit2D& hit = raycastHits[index];
+ if (isInfiniteDistance)
+ hit.fraction *= PHYSICS_2D_RAYCAST_DISTANCE;
+
+ *(outHits++) = hit;
+ }
+
+ return allowedResultCount;
+}
+
+
+int Physics2DManager::RaycastAll (const Vector2f& origin, const Vector2f& direction, const float distance, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<RaycastHit2D>* outHits)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gRaycastAll2DProfile, NULL)
+
+ // Calculate points.
+ const bool isInfiniteDistance = distance == std::numeric_limits<float>::infinity();
+ Vector2f normalizedDirection = NormalizeFast (direction);
+ const Vector2f pointB = origin + (normalizedDirection * (isInfiniteDistance ? PHYSICS_2D_RAYCAST_DISTANCE : distance));
+
+ Raycast2DQuery query (origin, pointB, layerMask, minDepth, maxDepth, *outHits);
+ const int resultCount = query.RunQuery ();
+
+ // Finish if not infinite distance.
+ if (!isInfiniteDistance || resultCount == 0)
+ return resultCount;
+
+ // Change fraction to distance.
+ for (dynamic_array<RaycastHit2D>::iterator hitItr = outHits->begin(); hitItr != outHits->end(); ++hitItr)
+ hitItr->fraction *= PHYSICS_2D_RAYCAST_DISTANCE;
+
+ return resultCount;
+}
+
+
+int Physics2DManager::GetRayIntersection(const Vector3f& origin, const Vector3f& direction, const float distance, const int layerMask, RaycastHit2D* outHits, const int outHitsSize)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gGetRayIntersection2DProfile, NULL)
+
+ // Finish if no available hits capacity.
+ if (outHitsSize == 0)
+ return 0;
+
+ dynamic_array<RaycastHit2D> raycastHits(kMemTempAlloc);
+ const int resultCount = GetRayIntersectionAll (origin, direction, distance, layerMask, &raycastHits);
+
+ // Transfer the first n-results.
+ const int allowedResultCount = std::min (resultCount, outHitsSize);
+ for (int index = 0; index < allowedResultCount; ++index)
+ *(outHits++) = raycastHits[index];
+
+ return allowedResultCount;
+}
+
+
+int Physics2DManager::GetRayIntersectionAll(const Vector3f& origin, const Vector3f& direction, const float distance, const int layerMask, dynamic_array<RaycastHit2D>* outHits)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gGetRayIntersectionAll2DProfile, NULL)
+
+ // Clear hits.
+ outHits->clear ();
+
+ // Set ray.
+ Ray ray;
+ ray.SetOrigin (origin);
+ ray.SetApproxDirection (direction);
+
+ // Calculate destination point.
+ const bool isInfiniteDistance = distance == std::numeric_limits<float>::infinity();
+ const float rayDistanceScale = isInfiniteDistance ? 1.0f : 1.0f / distance;
+ const Vector3f destination = ray.GetPoint (isInfiniteDistance ? PHYSICS_2D_RAYCAST_DISTANCE : distance);
+
+ // If the ray is parallel to the X/Y plane then no intersections are possible.
+ if (CompareApproximately (origin.z, destination.z))
+ return 0;
+
+ // Calculate 2D start/end points.
+ const Vector2f pointA = Vector2f(origin.x, origin.y);
+ const Vector2f pointB = Vector2f(destination.x, destination.y);
+
+ // Find all the colliders that hit somewhere along the ray.
+ // NOTE:- These will all be unique colliders (not duplicates).
+ dynamic_array<RaycastHit2D> potentialHits(kMemTempAlloc);
+ if (LinecastAll (pointA, pointB, layerMask, origin.z, destination.z, &potentialHits) == 0)
+ return 0;
+
+ // Sort the hits by depth.
+ const bool isForwardDepth = origin.z < direction.z;
+ if (isForwardDepth)
+ std::sort (potentialHits.begin(), potentialHits.end(), RayHitsByDepthComparitor());
+ else
+ std::sort (potentialHits.begin(), potentialHits.end(), RayHitsByInverseDepthComparitor());
+
+ // Calculate the plane normal.
+ const Vector3f planeNormal(0.0f, 0.0f, isForwardDepth ? 1.0f : -1.0f);
+
+ // Iterate all hits and check collider intersections.
+ for (dynamic_array<RaycastHit2D>::iterator hitItr = potentialHits.begin (); hitItr != potentialHits.end (); ++hitItr)
+ {
+ // Fetch the hit.
+ RaycastHit2D& hit = *hitItr;
+
+ // Fetch the collider depth.
+ const Collider2D* collider = hit.collider;
+ const float depth = collider->GetGameObject ().GetComponent (Transform).GetPosition ().z;
+
+ // Configure a collider plane.
+ Plane colliderPlane;
+ colliderPlane.SetNormalAndPosition (planeNormal, Vector3f(0.0f, 0.0f, depth));
+
+ // Test ray for intersection position.
+ float intersectionDistance;
+ if (!IntersectRayPlane (ray, colliderPlane, &intersectionDistance))
+ continue;
+
+ // Test if the intersection point overlaps the collider.
+ const Vector3f intersectionPoint3 = ray.GetPoint (intersectionDistance);
+ const Vector2f intersectionPoint2 = Vector2f(intersectionPoint3.x, intersectionPoint3.y);
+ if (!collider->OverlapPoint (intersectionPoint2))
+ continue;
+
+ // Update the hit with the 3D intersection details.
+ // NOTE: We leave the normal in 2D space as we can't use the plane normal.
+ hit.point = intersectionPoint2;
+ hit.fraction = intersectionDistance * rayDistanceScale;
+
+ // Add hit result.
+ outHits->push_back (hit);
+ }
+
+ return outHits->size ();
+}
+
+
+int Physics2DManager::OverlapPoint (const Vector2f& point, const int layerMask, const float minDepth, const float maxDepth, Collider2D** outHits, const int outHitsSize)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gOverlapPoint2DProfile, NULL)
+
+ dynamic_array<Collider2D*> colliderHits(kMemTempAlloc);
+ OverlapPointQuery2D query (point, layerMask, minDepth, maxDepth, colliderHits);
+ const int resultCount = query.RunQuery ();
+
+ // Transfer the first n-results.
+ const int allowedResultCount = std::min (resultCount, outHitsSize);
+ for (int index = 0; index < allowedResultCount; ++index)
+ *(outHits++) = colliderHits[index];
+
+ return allowedResultCount;
+}
+
+
+int Physics2DManager::OverlapPointAll (const Vector2f& point, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>* outHits)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gOverlapPointAll2DProfile, NULL)
+
+ OverlapPointQuery2D query (point, layerMask, minDepth, maxDepth, *outHits);
+ return query.RunQuery ();
+}
+
+
+int Physics2DManager::OverlapCircle (const Vector2f& point, const float radius, const int layerMask, const float minDepth, const float maxDepth, Collider2D** outHits, const int outHitsSize)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gOverlapCircle2DProfile, NULL)
+
+ dynamic_array<Collider2D*> colliderHits(kMemTempAlloc);
+ OverlapCircleQuery2D query (point, radius, layerMask, minDepth, maxDepth, colliderHits);
+ const int resultCount = query.RunQuery ();
+
+ // Transfer the first n-results.
+ const int allowedResultCount = std::min (resultCount, outHitsSize);
+ for (int index = 0; index < allowedResultCount; ++index)
+ *(outHits++) = colliderHits[index];
+
+ return allowedResultCount;
+}
+
+
+int Physics2DManager::OverlapCircleAll (const Vector2f& point, const float radius, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>* outHits)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gOverlapCircleAll2DProfile, NULL)
+
+ OverlapCircleQuery2D query (point, radius, layerMask, minDepth, maxDepth, *outHits);
+ return query.RunQuery ();
+}
+
+
+int Physics2DManager::OverlapArea (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, Collider2D** outHits, const int outHitsSize)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gOverlapArea2DProfile, NULL)
+
+ dynamic_array<Collider2D*> colliderHits(kMemTempAlloc);
+ OverlapAreaQuery2D query (pointA, pointB, layerMask, minDepth, maxDepth, colliderHits);
+ const int resultCount = query.RunQuery ();
+
+ // Transfer the first n-results.
+ const int allowedResultCount = std::min (resultCount, outHitsSize);
+ for (int index = 0; index < allowedResultCount; ++index)
+ *(outHits++) = colliderHits[index];
+
+ return allowedResultCount;
+}
+
+
+int Physics2DManager::OverlapAreaAll (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>* outHits)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gOverlapAreaAll2DProfile, NULL)
+
+ OverlapAreaQuery2D query (pointA, pointB, layerMask, minDepth, maxDepth, *outHits);
+ return query.RunQuery ();
+}
+
+void Physics2DManager::InvalidateColliderCollisions (Collider2D* collider)
+{
+ g_Physics2DState.m_Collisions.InvalidateColliderCollisions (collider);
+}
+
+
+void Physics2DManager::DestroyColliderCollisions (Collider2D* collider)
+{
+ g_Physics2DState.m_Collisions.DestroyColliderCollisions (collider);
+}
+
+
+#if ENABLE_PROFILER
+void Physics2DManager::GetProfilerStats (Physics2DStats& stats)
+{
+ // Fetch the physics world.
+ b2World* world = g_Physics2DState.m_PhysicsWorld;
+
+ // Cannot populate stats without a world.
+ if (world == NULL)
+ return;
+
+ // Calculate body metrics.
+ int dynamicBodyCount = 0;
+ int kinematicBodyCount = 0;
+ int activeBodyCount = 0;
+ int sleepingBodyCount = 0;
+ int discreteBodyCount = 0;
+ int continuousBodyCount = 0;
+ int activeColliderShapesCount = 0;
+ int sleepingColliderShapesCount = 0;
+
+ for (b2Body* body = world->GetBodyList (); body; body = body->GetNext ())
+ {
+ // Check body type.
+ const b2BodyType bodyType = body->GetType ();
+ if (bodyType == b2_staticBody)
+ continue;
+ else if (bodyType == b2_dynamicBody)
+ dynamicBodyCount++;
+ else if (bodyType == b2_kinematicBody)
+ kinematicBodyCount++;
+
+ // Check sleep state.
+ if (body->IsAwake ())
+ {
+ activeBodyCount++;
+ activeColliderShapesCount += body->GetFixtureCount ();
+ }
+ else
+ {
+ sleepingBodyCount++;
+ sleepingColliderShapesCount += body->GetFixtureCount ();
+ }
+
+ // Check CCD state.
+ if (body->IsBullet ())
+ continuousBodyCount++;
+ else
+ discreteBodyCount++;
+ }
+
+ // Populate profile counts.
+ stats.m_TotalBodyCount = world->GetBodyCount () - 1; // Ignore the hidden static ground-body.
+ stats.m_ActiveBodyCount = activeBodyCount;
+ stats.m_SleepingBodyCount = sleepingBodyCount;
+ stats.m_DynamicBodyCount = dynamicBodyCount;
+ stats.m_KinematicBodyCount = kinematicBodyCount;
+ stats.m_DiscreteBodyCount = discreteBodyCount;
+ stats.m_ContinuousBodyCount = continuousBodyCount;
+ stats.m_ActiveColliderShapesCount = activeColliderShapesCount;
+ stats.m_SleepingColliderShapesCount = sleepingColliderShapesCount;
+ stats.m_JointCount = world->GetJointCount ();
+ stats.m_ContactCount = world->GetContactCount ();
+
+ // Populate profile times.
+ const b2Profile& timeProfile = world->GetProfile ();
+ const float millisecondUpscale = 1000000.0f;
+ stats.m_StepTime = (int)(timeProfile.step * millisecondUpscale);
+ stats.m_CollideTime = (int)(timeProfile.collide * millisecondUpscale);
+ stats.m_SolveTime = (int)(timeProfile.solve * millisecondUpscale);
+ stats.m_SolveInitialization = (int)(timeProfile.solveInit * millisecondUpscale);
+ stats.m_SolveVelocity = (int)(timeProfile.solveVelocity * millisecondUpscale);
+ stats.m_SolvePosition = (int)(timeProfile.solvePosition * millisecondUpscale);
+ stats.m_SolveBroadphase = (int)(timeProfile.broadphase * millisecondUpscale);
+ stats.m_SolveTimeOfImpact = (int)(timeProfile.solveTOI * millisecondUpscale);
+}
+
+#endif
+
+// --------------------------------------------------------------------------
+
+
+void Physics2DManager::SetTransformMessageEnabled (const bool enable)
+{
+ for (size_t i = 0, n = m_AllCollider2DTypes.size(); i < n; ++i)
+ GameObject::GetMessageHandler ().SetMessageEnabled (m_AllCollider2DTypes[i], kTransformChanged.messageID, enable);
+
+ m_RigidbodyTransformMessageEnabled = enable;
+}
+
+#endif // #if ENABLE_2D_PHYSICS