Transform Manipulation

Header: Core/include/Core/Components.h

The Transform Component

Every entity that exists in the 3D world has a Transform:

struct Transform {
  glm::vec3 position{0.0f};   // world position (x, y, z)
  glm::vec3 rotation{0.0f};   // euler angles in degrees (pitch, yaw, roll)
  glm::vec3 scale{1.0f};      // scale per axis
};

Access it in any behavior:

auto *t = e.get_mut<Transform>();  // mutable pointer
const auto *t = e.get<Transform>(); // read-only pointer

Moving an Entity

Modify position directly. Always multiply by dt for frame-rate independence:

void OnUpdate(flecs::entity e, Engine &engine, float dt) override {
  auto *t = e.get_mut<Transform>();
  float speed = 5.0f;

  // Move forward along Z axis
  t->position.z -= speed * dt;

  // Move to a specific position
  t->position = glm::vec3(0.0f, 1.0f, 0.0f);

  // Move toward a target (smooth interpolation)
  glm::vec3 target(10.0f, 0.0f, 0.0f);
  t->position = glm::mix(t->position, target, 2.0f * dt);
}
Tip

Use glm::mix(a, b, t) for smooth interpolation between two values. The third parameter controls speed (multiply by dt to keep it frame-rate independent).

Rotating an Entity

Rotation uses euler angles in degrees: x = pitch, y = yaw, z = roll.

void OnUpdate(flecs::entity e, Engine &engine, float dt) override {
  auto *t = e.get_mut<Transform>();

  // Continuous rotation (90 degrees per second around Y)
  t->rotation.y += 90.0f * dt;

  // Set a specific rotation
  t->rotation = glm::vec3(0.0f, 45.0f, 0.0f);

  // Look in a direction
  glm::vec3 dir = glm::normalize(targetPos - t->position);
  t->rotation.x = glm::degrees(std::asin(-dir.y));       // pitch
  t->rotation.y = glm::degrees(std::atan2(dir.x, dir.z)); // yaw
}

Rotation Axes

AxisComponentEffect
Xrotation.xPitch (look up/down)
Yrotation.yYaw (turn left/right)
Zrotation.zRoll (tilt sideways)

Scaling an Entity

void OnUpdate(flecs::entity e, Engine &engine, float dt) override {
  auto *t = e.get_mut<Transform>();

  // Uniform scale
  t->scale = glm::vec3(2.0f);

  // Non-uniform scale (stretch along X)
  t->scale = glm::vec3(2.0f, 1.0f, 1.0f);

  // Pulsing animation
  float pulse = 1.0f + 0.2f * std::sin(engine.GetDeltaTime() * 3.0f);
  // Better: accumulate time in a member variable
  m_Timer += dt;
  float scale = 1.0f + 0.2f * std::sin(m_Timer * 3.0f);
  t->scale = glm::vec3(scale);
}

Direction from Rotation

Convert euler angles to a forward direction vector:

glm::vec3 GetForward(const Transform &t) {
  float pitch = glm::radians(t.rotation.x);
  float yaw = glm::radians(t.rotation.y);
  return glm::normalize(glm::vec3(
      std::cos(pitch) * std::sin(yaw),
      -std::sin(pitch),
      std::cos(pitch) * std::cos(yaw)));
}

glm::vec3 GetRight(const Transform &t) {
  float yaw = glm::radians(t.rotation.y);
  return glm::normalize(glm::vec3(std::cos(yaw), 0.0f, -std::sin(yaw)));
}

Useful glm Functions

FunctionDescription
glm::normalize(v)Make a vector length 1 (preserve direction only)
glm::length(v)Get the length of a vector
glm::distance(a, b)Distance between two points
glm::mix(a, b, t)Linear interpolation (lerp)
glm::clamp(v, min, max)Clamp a value to a range
glm::radians(degrees)Convert degrees to radians
glm::degrees(radians)Convert radians to degrees
glm::dot(a, b)Dot product
glm::cross(a, b)Cross product
Warning

Always use dt (delta time) when modifying transforms over time. Without it, movement speed depends on frame rate. speed * dt gives consistent speed regardless of FPS.