Skip to content

Room-scale movement synchronization#464

Merged
vincentfretin merged 5 commits intoc-frame:masterfrom
KickSuch:feat-room-scale-movement-sync
Apr 1, 2026
Merged

Room-scale movement synchronization#464
vincentfretin merged 5 commits intoc-frame:masterfrom
KickSuch:feat-room-scale-movement-sync

Conversation

@KickSuch
Copy link
Copy Markdown
Contributor

The movement-controls component requires the use of a camera "rig" wrapping the camera element. But you use room-scale VR setup, your movement in real life will change the X and Z position of the camera element. This will make camera not be centered in the rig, which creates issues with navmesh, because you are not where the rig thinks you are. For this problem I created 2 components.

  • room-scale-movement-stabilizer: stops camera movement, applied to a camera wrapper that is not the rig.
  • room-scale-movement-adjuster: simulates the movement from camera to the rig.

So when you need to have the camera centered in the rig, or need to restrict the camera movement even in room-scale VR, then instead of regular navigation mesh setup:

<a-entity id="rig" movement-controls="constrainToNavMesh: true">
  <a-entity camera
            position="0 1.6 0"
            look-controls="pointerLockEnabled: true">
  </a-entity>
</a-entity>

You will use:

<a-entity id="rig" 
          movement-controls="constrainToNavMesh: true"
          room-scale-movement-adjuster="constrainToNavMesh: true">
  <a-entity room-scale-movement-stabilizer>
    <a-entity camera
              position="0 1.6 0"
              look-controls="pointerLockEnabled: true">
    </a-entity>
  </a-entity>
</a-entity>

Along with the components this PR has README updates with the components' usage and example page that I used to debug this issue. You can try replacing the player in the example for the previous one and you will see how you can move outside of the navigation mesh.

@vincentfretin
Copy link
Copy Markdown
Member

Adding other components to fix the issue doesn't make sense to me. If there is a fix to do it should be directly in movement-controls tick, probably something similar to jure/aframe-blink-controls#27

@KickSuch
Copy link
Copy Markdown
Contributor Author

Adding 1 component is necessary if we want to have the camera stay above the player rig. We can not just take the X, Z position from the camera and reset the camera to 0 0, because the VR headset overrides the camera position with the room-scale position. Something like the room-scale-movement-stabilizer component on a camera wrapper entity is necessary to adjust the camera's position without editing the position of the camera entity.

But if we wanted just the navmesh clamping to take into consideration the camera's room-scale position, then we can do it without making new components. But this will not have the camera above the player rig which may confuse people thinking that the rig position is the player position when it in fact is the camera's world position.

Do you still think that not adding new components would be better?

@vincentfretin
Copy link
Copy Markdown
Member

Yeah I understand the player position is the camera's world position. We can adjust the camera rig position to offset the threejs camera position, that's what is done in the blink-controls PR. You shouldn't need an extra entity between the rig and camera.
Your room-scale-movement-adjuster component seems to do a similar thing than the movement-controls with the clamping, so that work is done twice and override each other setting the element position. It seems you're just lucky that the tick of your room-scale-movement-adjuster component is executed after movement-controls tick so that works.

@vincentfretin
Copy link
Copy Markdown
Member

Nevermind my comment about both components setting the position, it's actually on two different entities.
But yes I think it could be simplified just doing the logic in movement-controls tick unless there is really a thing I missed.

@KickSuch
Copy link
Copy Markdown
Contributor Author

I reverted the previous changes and made changes only to the movement-controls.

Comment thread src/controls/movement-controls.js Outdated
const velocity = this.velocity;

if (!velocityCtrl) return;
if (!velocityCtrl && !data.constrainToNavMesh) return;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's probably wrong to add that condition here. The movement-controls still need to work with constrainToNavMesh=false

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part needs to stay. Without it the navmesh clamping is ignored until next movement input. It still works when the constrainToNavMesh=false, it only skips the tick if there is also no movement input. Alternatively we can remove this line entirely

Comment thread src/controls/movement-controls.js Outdated

if (data.constrainToNavMesh
&& velocityCtrl.isNavMeshConstrained !== false) {
&& (!velocityCtrl || velocityCtrl.isNavMeshConstrained !== false)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can revert that change if you revert the first change.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first change must stay

Comment thread src/controls/movement-controls.js Outdated
cameraOffset.y = 0;

start.copy(el.object3D.position).add(cameraOffset);
if (velocityCtrl) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to check for velocityCtrl if you revert the first line change

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first change must stay

Comment thread src/controls/movement-controls.js Outdated
@KickSuch
Copy link
Copy Markdown
Contributor Author

In my last commit I implemented some of the changes you mentioned in the code review. I moved the vector creation and made the calculation only in VR mode. But as I mentioned in the code review, the first change must stay because without it the navmesh clamping does not work when there is no movement input. Do you have any feedback for that?

@KickSuch
Copy link
Copy Markdown
Contributor Author

@vincentfretin could you take another look at these changes. I’ve gone through your comments and incorporated most of the changes, but some of my changes must stay to keep this functionality working even without controller input.

If you would like, I would be down to have a call with you to review these changes together. I would really like to have this change merged and pushed to a new realease, so I can simply use the default library in my projects.

@vincentfretin
Copy link
Copy Markdown
Member

Sorry, I've been swamped with a project lately. I'll test that with my Quest 3 soon to see if there is no issue.

@vincentfretin
Copy link
Copy Markdown
Member

I tested your version on the castle example. There was an issue that my head was moving the castle when I tried to look at the window. I modified the code to just #465 I believe it works correctly now.

@KickSuch
Copy link
Copy Markdown
Contributor Author

I tested your version and it does not restrict the movement beyond navmesh when moving without controllers.

Steps to reproduce: Position yourself using controllers in front of the wall (or the door or anything similar). Walk few steps forward without using controllers.

Your version will allow you to move beyond the wall and outside of the castle. You will get clamped back when you try to move with the controllers again, but without them you are not restricted.

My version will restrict the movement and you will walking in place. The difference between our versions is that I allow tick function to run even without controller input (velocityCtrl), so it can clamp the movement on every tick. Unfortunately it can create the issue you mentioned, as the looking at the window can be simmilar to the walking in place, just with the movement of your head.

…velocityCtrl active and constrainToNavMesh true and not in VR
@vincentfretin
Copy link
Copy Markdown
Member

Yes got it, I considered it an issue and you a feature :D I'm testing at home where I only have a zone of 2m² to test so I'm not walking miles to traverse walls. I'm fine with your behavior. I modified a bit the conditions in your PR to not do extra calculation on desktop. Looks good for you?

@KickSuch
Copy link
Copy Markdown
Contributor Author

KickSuch commented Apr 1, 2026

I am glad we are on the same page now. I tested it on my personal scenes and it works as expected. Looks good to merge.

@vincentfretin vincentfretin merged commit 83e298b into c-frame:master Apr 1, 2026
@vincentfretin
Copy link
Copy Markdown
Member

Thanks for your patience. I released 7.7.0 with the changes. Congrats for your first contribution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants