Wall Walking Example This guide will go over how to use the Repositioning Remote Players technique to create what appears to be an anti-gravity effect. Just know that this system is an illusion. The local player is never actually rotated, but simply teleported to a duplicate of the world that is itself rotated. Overview This guide will go over how to use the Repositioning Remote Players technique to create what appears to be an anti-gravity effect. Just know that this system is an illusion. The local player is never actually rotated, but simply teleported to a duplicate of the world that is itself rotated.  https://twitter.com/CyanLaser/status/1180655212568043520 Example Project Download All Examples for this guide can be downloaded from my GitHub. Please continue to follow the rest of the guide if you want to implement this yourself as the technique is very strange and may not make sense looking at the files alone! Overview There are 4 items required for this system: The world with duplicates for every orientation A way to move between each orientation A system to designate which orientation you are currently The RRP system + Object Pool to distribute one per player Position remote players based on your current orientation The first three items are simple. The last item is where it gets complicated. You can ignore the player pool as that has been solved through prefabs like CyanPlayerObjectPool. It all comes down to how players are positioned in the scene.  The idea is that for the local player's current world orientation, you sync their relative position. Every other player will then calculate where that position is relative to their orientation, and set the player's station there.  Preparing the World This guide will go over how to make a room where you can walk on the floor and on the wall, but you can extend this to any number of orientations. The first thing you should do is create a spawn area. This area will not be part of the wall walking room but is still needed: This will be a safe area outside of the system Some people can get sick just thinking about odd rotations A player must initiate to enter the system  You can use this as a way to catch for errors when people don't enter the station Have an area where you can drop portals Portals in the system will only be in the orientation dropped Next, create your room. This can be as complicated as you want. Ensure that everything is under one parent. This parent will be the origin of the world and how we determine relative position between orientations. When you have the room ready, duplicate it, move it out of the way, and rotate it so that the part you want to walk on is now the new floor. Note that normally you wouldn’t let room orientations see each other. Moving Between Orientations With the world ready, we need a way to enter it and change orientations. For simplicity in this guide, we will use an interact button to do all this. Create a new program in your Udon compiler of choice. Interact, Teleport the player to a destination Transform. We will add more later. Place 3 buttons in the scene using this program, One in spawn, one in first orientation, another in the rotated orientation. Create a destination transform for the buttons in each orientation. The spawn button's destination should be somewhere in the first orientation. The first orientation button's destination should be in the second orientation. The second orientation button's destination should be in the first orientation. Saving Current Orientation As mentioned in the overview, we need to save local player's current orientation to know how to sync their position as well as apply positions of all other players.  Create a new object, call it Origin, and create a new Udon program for this object. The program should have a public Transform variable that can be set, and an event that when sent, will copy the position and rotation of the transform variable and set itself to that position and rotation.  Now we need to update our original buttons to set the transform variable and send the event to this origin object. After teleporting the player, add logic to set the transform variable to the transform of the orientation we are going to. Once set, send the event. It is a good idea to set the origin’s transform to the same position and rotation as the first room orientation. This way, by default, all players will appear in the first room, instead of any other arbitrary location. Station Object At this point, we can start working with stations to start repositioning players. For now, we are only going to create one station to test the system before extending it to all players. Create a new object. Add a VRCStation component to it.  Set Mobility to Immobilize Set Seated to unchecked Set Disable Station Exit to checked Set Can Use Station From Station to unchecked Create another object and set it as a child of the station. This will be for the player's position. In the station script, set the "Player Enter Location" and "Player Exit Location" to this new child transform. Create another object as a child of the station and add VRCObjectSync. This will be how the player's relative position is synced to all players. Entering the station Now we need to create a program to perform the logic for the station. This will consist of multiple events to handle different sections. Create a new program and attach it to the station object. Create a new custom event and call it "_EnterStation". This event will be used to enter the station. There are three items that need to happen here: Set the station mobility to Mobile Take ownership over the object sync object Have the local player enter the station. Outside of the station object and in the Origin object's program, add a SendCustomEvent to enter the station. This will automatically update all buttons to properly enter the station, and enter the system. Handling Players Entering And Leaving The Station Since anyone can enter the station, we need to save the player currently in it to know what logic to perform later. Create a new VRCPlayerApi variable to store the player in the station. Add the OnStationEntered event, and save the entered player in the variable. Also add the OnStationExited event. Instead of saving the player, set the player variable to null. Also in the station exited event, set the station mobility to Immobilize. This just cleans up in case one player uses it, stops, and another player wants to use it. Update Station Position Logic Now that we know when someone is in the station, and if that player is local vs remote, we can update the station objects appropriately. Add a public Transform variable. This will be set with the origin we created earlier.  Add the Update event. The first thing we need to check is if the sitting player is valid. If they are invalid, we can stop early as no one is using the station.  Next, check if the player is the local player. In both the local and remote case, we will need to perform some math. Create a custom event for updating position for local and remote. First, updating positions for the local player. Get the local player's position and rotation, and set the station's location to these values. Having the station follow the local player removes issues with teleporting on exiting the station. Next, get the origin transform's "worldToLocalMatrix". This 4x4 matrix will convert world space coordinates into the local space coordinates relative to this origin object. Use matrix.MultiplyPoint on the player's position to get the local position. Use matrix.get Rotation and multiply this with the player's rotation to get the local rotation. Now set the ObjectSync object to this position and rotation. Now to handle updates for the remote player. We basically need to do the opposite here.  First get the position and rotation of the objectSync object. Also get the origin transform's "localToWorldMatrix". Note this is local to world and not world to local like the previous one. Use matrix.MultiplyPoint on the sync's position to get the new world position. Use matrix.get Rotation and multiply this with the sync's rotation to get the new world rotation. Finally, set the station's location to this position and rotation. With those events created, we need to go back and add two send custom events in previous areas. Since players will teleport when entering and leaving stations, we can reduce movement by updating the station position just before entering and just after exiting. In the "_EnterStation" event, just before entering the station, add SendCustomEvent to the local update position event. In the OnStationExit, check if the player was local, and also add the SendCustomEvent With this, one player can enter the station, and other players can observe them walking on the wall.  Player Object Pool Setup Right now, only one player can enter the station, and everyone else can only see that player rotating properly. The next step is to allow everyone to enter, by giving everyone their own station. Writing a way to distribute an item to all players in an instance can be extremely difficult. Luckily there are prefabs others have written to do this for us. One such prefab is CyanPlayerObjectPool. This prefab requires UdonSharp to work, but you can use it with any Udon compiler.  Install the following in this order: https://github.com/MerlinVR/UdonSharp  https://github.com/CyanLaser/CyanPlayerObjectPool  The CyanPlayerObjectPool has a readme that lists all the steps on how to create a system that uses it. Since some items are not needed for this guide, they will be skipped.  First find and drag the PlayerObjectAssigner prefab into your scene.  This will also add the PlayerObjectPool prefab into the scene.  Click the PlayerObjectPool prefab and set the pool size to double the world capacity.  The PlayerObjectAssigner system requires an object that will be duplicated and distributed to all players. Make the station object a prefab and assign the prefab into the PlayerObjectAssigner inspector.  You will notice that the assigner has spawned the prefab many times as children.  The readme says this object is required to have two methods and a variable. The station system created should work as is, but you may add these items if you want to add extra behavior outside of this guide.  Linking Origin to the ObjectPool Now when a player joins the world, they should be assigned a station object automatically. Although, the system we previously made isn't linked to the assigned object. We need to update the Origin's program to handle getting the object assigned to the local player. The CyanPlayerObjectPool has a notification system that allows an udon program to listen to specific events. We will make the Origin the pool listener. Drag the pool object into the PlayerObjectAssigner's listener field. In the Origin program, add a new event called "_OnLocalPlayerAssigned". This will automatically be called by the pool when the local player is assigned an object. In the event itself, we need to get this assigned object. You can do this by sending the "_GetPlayerPooledUdonEvent" event to the player pool. Note that the event name is different if you are using UdonSharp. One issue with working with CyanPlayerObjectPool is that assigning variables to the pool object can be tedious. In the previous system, the Origin was set as a public variable. This now needs to be set for all pool objects. You can do it manually by clicking on each object and assigning it, or we can take advantage of the object pool listener methods and assign it in Udon directly. To do this, add a new public UdonBehaviour variable named "playerAssignedPoolObject" and create a new event called "_OnPlayerAssigned". Inside this event, call SetProgramVariable on the UdonBehaviour to set the origin transform to this object's transform. At this point, the system should be fully usable. Upload a build and join it with multiple clients to test it out. Other Improvements Since this is just the basics, there are other items that can be done to improve the system:  Verify the player has a pool object before enabling buttons to enter the world. Verify the player entered the station before doing other logic.  Generic avatars and questionable clients will cause this to break for people.  Change teleporting to use player enter trigger based instead of clicking a button. Teleportation could use seamless teleport so the player’s rotation is preserved. Disable the station Udon while no one is sitting to reduce update calls On someone enter, enable the UdonBehaviour, on exit disable the UdonBehaviour. Other World Ideas This guide goes over the basics of showing how to reposition players to fake multiple gravities in a world. This can then be expanded to other forms, such as using only one world that moves with the player instead of teleporting to different orientations. This “moving the world” approach is the bases for these examples: Ring world Sphere world Free form rotation Example VRChat Worlds Here are some worlds you can try now that use this technique in VRChat: Wall Walking by CyanLaser (Private) The Sphere by CyanLaser External Sphere by CyanLaser (Private) Interplanetary Cruise Ship by S_ASAGIRI Escher by suzuki_i ANTI-GRAVITY SATELLITE by lunar eclipse