How to rotate a quanternion and calculate direction vector in Unity (ECS)

Viewed 4811

I started to work with ECS and I have a Rotation component (quaternion) and a Translation component (float3). I am writing a System that will handle user input and rotate and/or move forward the player.

The problem is I don't know how to do it. The existing API is lacking in my opinion.

// Before it was easy
if (Input.GetKey(KeyCode.W))
    var newPostion = new Vector3(player.Position.x, player.Position.y, 0.3f)
                    + _playerTransform.up * Time.deltaTime * PlayerSpeed;

if (Input.GetKey(KeyCode.A))
    _playerTransform.Rotate(new Vector3(0, 0, PlayerRotationFactor));

// now it is not - that's part of the system's code
Entities.With(query).ForEach((Entity entity, ref Translation translation, ref Rotation rotation) =>
{
    float3 tmpTranslation = translation.Value;
    quaternion tmpRotation = rotation.Value;

    if (Input.GetKey(KeyCode.W))
        // how to retrieve forward vector from the quaternion
        tmpTranslation.y += Time.deltaTime * PlayerSpeed; 

    if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
        // this always set the same angle
        tmpRotation = quaternion.RotateZ(PlayerRotationFactor); 

    // ECS need this
    var newTranslation = new Translation { Value = tmpTranslation };
    PostUpdateCommands.SetComponent(entity, newTranslation);
    var newRotation = new Rotation { Value = tmpRotation };
    PostUpdateCommands.SetComponent(entity, newRotation);
});
3 Answers

To get the vector from a quaternion you can simply multiply it with a forward vector, example:

if (Input.GetKey(KeyCode.W))
{
    Vector forward = tmpRotation * float3(0,0,1);
    tmpTranslation += forward * Time.deltaTime * PlayerSpeed;
}

For the rotation part, as you say // this always set the same angle. Looks like you're creating a new angle of static degree. Try change to multiply with the current angle, as with quaternions you multiply to combine. Example:

if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
{
    tmpRotation = mul(tmpRotation, quaternion.RotateZ(PlayerRotationFactor));
}

Sidenote: I'm also a bit new to Unity ECS, and I've not tested above codes.

// correct forward
if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow))
{
    float3 forwardVector = math.mul(rotation.Value, new float3(0, 1, 0));
    newTranslation += forwardVector * Time.deltaTime * PlayerSpeed;
}
// correct rotation
if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
{
    quaternion newRotation = math.mul(rotation.Value, quaternion.RotateZ(PlayerRotationFactor * Time.deltaTime));
    // insert new value to the component
    PostUpdateCommands.SetComponent(entity, new Rotation { Value = newRotation });
}
// thx akaJag

NOTICE to other viewers.

That code does not use the delegate variable references as they should be.

Setting the component in PostUpdateCommands (or EntityManager, or any other CommandBufferSystem) is utterly unnecessary as the translation / rotation is directly present - in the delegate function - to be manipulated by ref.

AKA:

rotation.Value = newRotation
translation.Value = newTranslation

NOT

EntityManager(CommandBuffer).SetComponentData(entity, new Rotation{ Value = newRotation})
Related