Krotox Engine › Docs › Scripting API › Physics & Input
Controlling 2D physics bodies from C#: velocity, forces, impulses and torque, teleporting, mass and sleep, changing colliders at run time, and every collision and trigger callback with the Collision2D data they carry.
var rb = GetComponent<Rigidbody2DComponent>();
rb.Type = Rigidbody2DComponent.BodyType.Dynamic; // Static, Dynamic, Kinematic
rb.LinearVelocity = new Vector2(5, 0); // units per second
rb.AngularVelocity = 1.5f; // radians per second
rb.GravityScale = 2.0f;
rb.Mass = 1.5f;
rb.FixedRotation = true; // keep characters upright
rb.Bullet = true; // continuous collision for fast bodies
| Member | Use |
|---|---|
LinearVelocity (Vector2), AngularVelocity (float, rad/s) | Read or set the speed directly. Best for responsive character control. |
ApplyLinearImpulse(Vector2 impulse) / (impulse, Vector2 worldPoint) | An instant kick: jumps, explosions, hits. |
ApplyForce(Vector2 force) / (force, Vector2 worldPoint), ApplyForceToCenter(force) | A continuous push; apply it every fixed step. |
ApplyTorque(float torque) | A continuous spin. |
AddForce, AddImpulse, AddTorque, AddForceAtPosition | Unity-style aliases of the above. |
GetVelocityAtPoint(Vector2 worldPoint) | The velocity of a point on the body. |
LinearDamping, AngularDamping | Drag. Run-time only; resets on the next Play. |
// Jump only from the ground, with a fixed jump height regardless of current fall speed
if (Input.IsKeyPressed(KeyCode.Space) && m_Grounded)
{
Vector2 v = rb.LinearVelocity;
v.Y = 0.0f;
rb.LinearVelocity = v;
rb.ApplyLinearImpulse(new Vector2(0, JumpImpulse));
}
// Thrusters: apply continuous force in OnFixedUpdate
public override void OnFixedUpdate()
{
if (Input.IsKeyDown(KeyCode.W))
rb.ApplyForce(rb.TransformDirection(Vector2.Up) * Thrust);
}
rb.Position = new Vector2(0, 5); // teleport now (no interpolation trail); velocity is kept
rb.Rotation = 0.0f; // radians; teleport the angle
Vector2 pos = rb.Position; // world position
Vector2 com = rb.WorldCenter; // centre of mass in world space
rb.MovePosition(target); // kinematic platforms: move over the next step, pushing what is in the way
rb.MoveRotation(angleRadians);
For moving platforms, use a Kinematic body and MovePosition (or set its velocity). Riders on top are carried properly, which does not happen if you write the Transform.
| Member | Meaning |
|---|---|
Mass, CenterOfMass, Inertia | Mass properties. Inertia is zero before Play. |
RecalculateMass() | Recompute after changing a collider's size or density. A Mass override stays. |
IsAwake, WakeUp(), Sleep() | Sleep state. A sleeping body ignores its motor. |
SleepEnabled, SleepThreshold | Whether the body may sleep, and the speed below which it counts as resting. |
WakeTouching() | Wake whatever this body rests on or pushes. |
Enabled | false takes the body out of the simulation and queries but keeps its shapes and joints. |
CollisionDetection | Discrete (default) or Continuous (same as Bullet). |
Interpolation | None, Interpolate (default) or Extrapolate. |
CollisionGroup | Box2D group index. See collision groups. |
Vector2 tip = rb.TransformPoint(new Vector2(0.5f, 0)); // local point to world
Vector2 local = rb.InverseTransformPoint(worldPoint); // world point to local
Vector2 dir = rb.TransformDirection(Vector2.Right); // direction only
Every 2D collider can be changed while the game runs. The shape is rebuilt on its body, which keeps its velocity and joints.
| Component | Shape members |
|---|---|
BoxCollider2DComponent | Vector2 Size, Vector2 Offset |
CircleCollider2DComponent | float Radius, Vector2 Offset |
CapsuleCollider2DComponent | float Radius, float Height, Vector2 Offset |
PolygonCollider2DComponent | Vector2[] Vertices (convex, 3–8), Vector2 Offset |
SegmentCollider2DComponent | Vector2 Point0, Vector2 Point1 |
ChainCollider2DComponent | Vector2[] Vertices (2 or more), bool Loop |
All colliders also have Friction, Restitution, IsSensor, UseLayerCollisionMatrix, CategoryBits and MaskBits; all but the chain have Density and RestitutionThreshold.
var box = GetComponent<BoxCollider2DComponent>();
box.Size = new Vector2(0.8f, 0.9f); // crouching hitbox
box.Offset = new Vector2(0, -0.35f);
var poly = GetComponent<PolygonCollider2DComponent>();
poly.Vertices = new[] { new Vector2(-0.5f, 0), new Vector2(0.5f, 0), new Vector2(0, 1) };
To change Vertices, assign a new array. Editing the array you got back has no effect.
Override these on any script whose entity (or a child collider of its body) takes part in the contact.
// A solid contact started. Collision2D has the details.
public override void OnCollisionEnter2D(Collision2D c)
{
Log.Info(quot;Hit {c.Entity?.Name} at {c.Point}, normal {c.Normal}, impulse {c.NormalImpulse}"); } // Still touching: once per physics step until it ends. public override void OnCollisionStay2D(Collision2D c) { } // The contact ended. Point, normal and impulses are zero here. public override void OnCollisionExit2D(Collision2D c) { } // Lighter versions: just the other entity. public override void OnCollisionEnter(Entity other) { } public override void OnCollisionStay(Entity other) { } public override void OnCollisionExit(Entity other) { } // Sensor (trigger) overlaps public override void OnTriggerEnter(Entity other) { if (other.HasTag("Coin")) { m_Score += 1; other.Destroy(); } } public override void OnTriggerStay(Entity other) { } public override void OnTriggerExit(Entity other) { } // A joint on this entity exceeded its break threshold public override void OnJointBreak2D(JointBreak2D info) { Log.Warn(quot;Joint to {info.ConnectedEntity?.Name} broke at {info.BreakForce} N"); }
| Member | Meaning |
|---|---|
Entity / EntityID / Collider | The entity holding the other side's collider. |
Body / BodyID | The entity with the other side's Rigidbody 2D (for a ragdoll hit on the head, the ragdoll's root). |
SelfCollider / SelfColliderID | The entity holding our collider that made the contact. |
ColliderType, SelfColliderType | Box, Circle, Capsule, Polygon, Segment, Chain, Tilemap or None. |
Point, Normal | Contact point and normal. Zero on exit. |
NormalImpulse, TangentImpulse | How hard the hit was. Zero on exit. |
// A headshot check on a ragdoll, from the arrow's script
public override void OnCollisionEnter2D(Collision2D c)
{
bool fromTip = c.SelfCollider?.Name == "Tip";
bool headshot = c.Collider?.Name == "Head";
Entity enemy = c.Body; // the whole character, whichever limb was hit
}
The entity holding the collider receives it. If that collider is on a child of the entity with the Rigidbody 2D, the body's entity receives it too, so a script on a character's root hears about contacts on its child colliders. Use c.SelfCollider to tell which one.
OnFixedUpdate. Destroy() called from a callback is applied right after the step.public override? OnTriggerEnter(Entity other), not OnTriggerEnter2D.