Looping skinned mesh animation with "root motion"

Game engines like Unity and Unreal seem to want animation with root motion for animation clips. My understanding is this is simply an additional bone above the hips that captures the character’s translation, i.e. if a character walks 10m in a walk cycle the bone above the hips would translate 10m and the hip bone wouldn’t have any (significant) translation. The alternative is for the animation to walk in place and rely on the game engine to translate the character as it sees fit.

I have a walk cycle with root animation that works fine in the browser, except when it loops it bounces back to the origin. I added a ‘loop’ event handler to the mixer that adds the top level bone Z translation to the skinned mesh position whenever the cycle loops. It looks like it’s working but is this the right process?

Why don’t you just use “in-place” animations? All animated characters of the official examples use this type of animation.

I’m not sure this is correct. Feels a bit like a hack…