Working with Components¶
Components hold the data and behaviour attached to an entity: its transform, mesh,
physics body, camera, lights, audio, robot policy, and more. Scripts read and modify
them through a small generic API on Entity.
The component API¶
| Method | Purpose |
|---|---|
GetComponent<T>() |
Returns the component of type T, or null if the entity does not have one. |
HasComponent<T>() |
true if the entity has a component of type T. |
CreateComponent<T>() |
Adds and returns a new component of type T. |
RemoveComponent<T>() |
Removes the component of type T. |
GetComponent<T>() returns a nullable reference, so check before using it:
protected override void OnCreate()
{
m_Mesh = GetComponent<MeshComponent>();
if (m_Mesh is null)
return;
// ... configure m_Mesh
}
Cache components in OnCreate rather than calling GetComponent every frame.
Transforms¶
Every entity has a TransformComponent.
Entity exposes shortcuts for the local-space transform, so the component itself rarely
needs to be fetched for everyday work:
// Shortcuts on Entity. All are LOCAL space, relative to the parent.
Translation = new Vector3(0.0f, 1.0f, 0.0f); // metres
Rotation = new Vector3(0.0f, 3.14f, 0.0f); // euler angles, radians
Scale = Vector3.One;
RotationQuat = Quaternion.Identity; // quaternion form of Rotation
For world-space values, or for the raw transform matrix, fetch the component:
TransformComponent t = GetComponent<TransformComponent>();
Vector3 worldPos = t.WorldTranslation;
Quaternion worldRot = t.WorldRotationQuat;
Matrix4 matrix = t.WorldTransformMatrix;
Transform world = t.World; // packed local-to-world transform
| What | Local (shortcut on Entity) |
World (on TransformComponent) |
|---|---|---|
| Position | Translation |
WorldTranslation |
| Euler rotation | Rotation (radians) |
WorldRotation |
| Quaternion rotation | RotationQuat |
WorldRotationQuat |
| Scale | Scale |
WorldScale |
| Matrix | TransformComponent.Matrix |
WorldTransformMatrix |
Physics¶
Lucky Engine ships three physics solvers. The right one for an entity is chosen by which body and collider components the entity carries. All three can run in the same scene at the same time, on disjoint entity sets.
-
Mujoco
Robotics simulation: articulated chains, joints, actuators, contact-rich manipulation. The solver behind UnitreeG1, UnitreeGo2, the Piper arm, SO100, and the rest of the robotics samples.
-
Jolt
Game-style 3D rigid body physics: rigid props, character controllers, simple constraints. Fast and stable for non-articulated bodies.
-
Box2D
2D physics for 2D scenes.
Mujoco bodies (robotics)¶
A robot is anchored by a
MujocoSceneComponent and shapes its
geometry through Mujoco*ColliderComponent parts. Its trained policies, motion graphs,
and joint actuators are all reached through
RobotControllerComponent.
Driving a policy¶
A trained policy is activated by slot ID; its commands are written by ID each step. For the G1 walker, slot 1 with commands 1, 2, 3 = forward velocity, strafe velocity, yaw rate:
private const uint k_WalkerSlot = 1u;
private const uint k_SetVx = 1u;
private const uint k_SetYawRate = 3u;
private RobotControllerComponent? m_Robot;
protected override void OnCreate()
{
m_Robot = GetComponent<RobotControllerComponent>();
m_Robot?.SetPolicyActive(k_WalkerSlot, true);
}
protected override void OnUpdate(float ts)
{
if (m_Robot is null)
return;
m_Robot.SetFloat(k_WalkerSlot, k_SetVx, 0.5f); // m/s
m_Robot.SetFloat(k_WalkerSlot, k_SetYawRate, 0.0f); // rad/s
}
Driving IK targets through a motion graph¶
If the robot carries a motion-graph asset, IK targets are exposed as named inputs on the
same RobotControllerComponent. The script writes a target pose and fires a trigger to
start the move; the graph interpolates the arm over the requested duration.
public class ArmReacher : Entity
{
// Input names must match those declared in the robot's motion-graph asset.
private static readonly Identifier k_TargetPosition = new Identifier("Target_Position");
private static readonly Identifier k_TargetOrientation = new Identifier("Target_Orientation");
private static readonly Identifier k_Duration = new Identifier("Duration");
private static readonly Identifier k_Start = new Identifier("Start");
private RobotControllerComponent? m_Robot;
protected override void OnCreate()
{
m_Robot = GetComponent<RobotControllerComponent>();
}
[Button("Reach to test pose")]
public void ReachToTestPose()
{
if (m_Robot is null)
return;
Vector3 worldPosition = new Vector3(0.3f, 0.5f, 0.1f); // metres
Vector3 orientationRad = Vector3.Zero; // identity rotation
m_Robot.SetInputVector3(k_TargetPosition, worldPosition);
m_Robot.SetInputVector3(k_TargetOrientation, orientationRad * Mathf.Rad2Deg); // graph reads degrees
m_Robot.SetInputFloat (k_Duration, 2.0f); // seconds
m_Robot.SetInputTrigger(k_Start); // fires one move
}
}
Position is in metres in world space, orientation in degrees as Euler XYZ. Each
SetInputTrigger(k_Start) cancels any in-flight move and starts a fresh interpolation
to the most recently written target.
Policies and IK share the same component
RobotControllerComponent runs trained policies and motion-graph IK side by side.
Joint ownership is partitioned with SetDrivenJoints(slotId, jointNames[]). Each
joint can be written by one active policy, by the motion graph's IK, or by the
script directly, but never by more than one in the same step.
The Mujoco component family includes MujocoBodyComponent, MujocoGeomComponent,
MujocoBoxColliderComponent, MujocoSphereColliderComponent,
MujocoCapsuleColliderComponent, MujocoCylinderColliderComponent,
MujocoMeshColliderComponent, MujocoJointComponent, and MujocoEqualityComponent.
Real examples
The ContentVault projects under UnitreeG1, UnitreeGo2, Piper,
PiperPatternStacking, and SO100PickAndPlace are all Mujoco-driven robotics
setups. They are good references for how a real scene composes Mujoco bodies,
colliders, joints, policy slots, and motion-graph IK targets.
Jolt rigid bodies (game-style 3D)¶
RigidBodyComponent plus a collider
(BoxColliderComponent, SphereColliderComponent,
CapsuleColliderComponent, or MeshColliderComponent) makes an entity dynamic under
Jolt. Forces and impulses are applied through the body, on the physics step:
private RigidBodyComponent? m_Body;
protected override void OnCreate()
{
m_Body = GetComponent<RigidBodyComponent>();
}
protected override void OnPhysicsUpdate(float ts)
{
if (m_Body is null)
return;
m_Body.AddForce(Vector3.Up * 25.0f);
Log.Info($"speed = {m_Body.LinearVelocity.Length()}");
}
For character movement and stair stepping, use
CharacterControllerComponent
rather than a raw rigid body.
Box2D rigid bodies (2D)¶
RigidBody2DComponent with
BoxCollider2DComponent or
CircleCollider2DComponent drives 2D physics.
Common components by area¶
| Area | Components |
|---|---|
| Transform | TransformComponent |
| Rendering | MeshComponent, StaticMeshComponent, CameraComponent, DirectionalLightComponent, PointLightComponent, SpotLightComponent, SkyLightComponent |
| Mujoco (robotics) | MujocoSceneComponent, MujocoBodyComponent, Mujoco*ColliderComponent, MujocoJointComponent, MujocoEqualityComponent, RobotControllerComponent |
| Jolt (3D physics) | RigidBodyComponent, BoxColliderComponent, SphereColliderComponent, CapsuleColliderComponent, MeshColliderComponent, CharacterControllerComponent |
| Box2D (2D physics) | RigidBody2DComponent, BoxCollider2DComponent |
| Audio | AudioComponent, AudioListenerComponent |
| Animation | AnimationComponent |
Browse them all in the Components reference.