# Repositioning Remote Players

How to make a world that lets you walk on walls

# Intro

<span style="font-weight:400;">Repositioning Remote Players is an advanced technique for VRChat where you can change where players appear in a world. The core of this technique uses VRC Stations in unintended ways. Combined with other techniques, this is the basis for the Walk-Walking illusion used in worlds. See tweets below for example worlds</span>

> Testing the gravity changing system with [@LakuzaVR](https://twitter.com/LakuzaVR?ref_src=twsrc%5Etfw) and [@jendaviswilson](https://twitter.com/jendaviswilson?ref_src=twsrc%5Etfw). Even with this simple environment it is still a lot fun. [\#VRChat](https://twitter.com/hashtag/VRChat?src=hash&ref_src=twsrc%5Etfw) [pic.twitter.com/6KNZ2yahDS](https://t.co/6KNZ2yahDS)
> 
> — CyanLaser (@CyanLaser) [October 6, 2019](https://twitter.com/CyanLaser/status/1180655212568043520?ref_src=twsrc%5Etfw)

> Tired of all the box clones in VRChat? Well, it's now time for The Sphere! The only place where you can explore the contents of a sphere! Available now on PC and [\#OculusQuest](https://twitter.com/hashtag/OculusQuest?src=hash&ref_src=twsrc%5Etfw) for [\#VRChat](https://twitter.com/hashtag/VRChat?src=hash&ref_src=twsrc%5Etfw). [\#VRChat\_world紹介](https://twitter.com/hashtag/VRChat_world%E7%B4%B9%E4%BB%8B?src=hash&ref_src=twsrc%5Etfw) [pic.twitter.com/eQUJQ9SbEa](https://t.co/eQUJQ9SbEa)
> 
> — CyanLaser (@CyanLaser) [March 14, 2020](https://twitter.com/CyanLaser/status/1238918516490690560?ref_src=twsrc%5Etfw)

## <span style="font-weight:400;">Udon Compiler</span>

<span style="font-weight:400;">This guide will show how to create the logic using three different Udon compilers. The logic shown in each type will all do the same thing. Pick your preference:</span>

1. [<span style="font-weight:400;">UdonGraph</span>](https://vrchat.com/home/download)
2. [<span style="font-weight:400;">UdonSharp</span>](https://github.com/MerlinVR/UdonSharp)
3. [<span style="font-weight:400;">CyanTrigger</span>](https://booth.pm/en/items/3194594)

# Station Setup

This section will go over the concepts of the technique and how it works, but not a specific example using it. See examples chapter for how to make something specific using the technique.

# Default Chair

[![VRCChair inspector](https://i.imgur.com/oFKkyyC.png)](https://i.imgur.com/oFKkyyC.png)

<span style="font-weight: 400;">In VRChat, there is a component called VRC Station. This is the basis for all chairs in worlds. The default setting allows a player to enter and sit in the station. At a technical level, when a player is in a station, the player's position is set to the station's position. This leads us to the first observation: Player positions are normally forced synced, but while in a station, the station does not need to be in the same location for everyone. The sitting player will still follow the station where you see it. </span>

<span style="font-weight: 400;">The most basic version of repositioning remote players is a default chair with some additional logic to move it. As an example, you can create a simple Udon program such that when a remote player is in the chair, the station is moved -1000 below the floor. This creates a situation where the player sitting in the station is still in the same location, but for everyone else, the player has vanished. Privacy concerns aside, the player sitting in the station is no longer at the same location for everyone. </span>

[![Desynced Chair](https://i.imgur.com/CQkk8rg.gif)](https://i.imgur.com/CQkk8rg.gif)

##### UdonGraph

[![Desynced Chair UdonGraph](https://i.imgur.com/n1qNxEc.png)](https://i.imgur.com/n1qNxEc.png)

##### UdonSharp

[![Desynced Chair UdonSharp](https://i.imgur.com/h4gfuVp.png)](https://i.imgur.com/h4gfuVp.png)

##### CyanTrigger

[![Desynced Chair CyanTrigger](https://i.imgur.com/DQ6u0c7.png)](https://i.imgur.com/DQ6u0c7.png)

# Station Mobility

<span style="font-weight: 400;">The default settings for a station force the player into sitting animation and move that player's position to the station's location. There are additional settings to change this behavior.</span>

[![Default VRCStation Inspector](https://i.imgur.com/hTAmzVE.png)](https://i.imgur.com/hTAmzVE.png)

### <span style="font-weight: 400;">1. Player Mobility</span>

<span style="font-weight: 400;">The first is the "Player Mobility" setting. Default value is immobile. There are three total settings:</span>

1. <span style="font-weight: 400;">Immobilize</span>
2. <span style="font-weight: 400;">Immobilize For Vehicle</span>
3. <span style="font-weight: 400;">Mobile</span>

<span style="font-weight: 400;">The first two options, Immobilize and Immobilize for Vehicle, are very similar (I don't actually know the differences). Both options will force the player's position to the station's position. Note that the station's position is actually the "Player Enter Location" transform. This can be set to the station, but it can also be any transform in the scene. This transform can be moved to any position and rotation and the player will be moved to it. Outside of stations, players cannot be rotated on the x or z axes, but only the y axis. While in an immobile station, players can be rotated along all axes. </span>

<span style="font-weight: 400;">The last option, Mobile, allows the player to "enter" the station, but the player's position is not forced to the station's. This option alone is not enough to allow the player to move around while in the station though. The "seated" setting must be disabled and the "Disable Station Exit" must be enabled.</span>

### <span style="font-weight: 400;">2. Seated</span>

<span style="font-weight: 400;">The seated option determines if the player's animations should change while in the station. By default, Seated is enabled, which will force the player to perform seated animations. When seated is disabled, animations are not changed, which allow the player to continue standing. </span>

### <span style="font-weight: 400;">3. Disable Station Exit</span>

<span style="font-weight: 400;">This setting determines if the player will automatically exit the station when moving around. It defaults off, meaning attempting to move will exit the station. </span>

<span style="font-weight: 400;">To have a "mobile" station, the station is required to have player mobility set to "Mobile", seated unchecked, and Disable Station Exit checked. This will allow the player to move while staying in the station.</span>

[![Mobile VRCStation Inspector](https://i.imgur.com/1ng3gBa.png)](https://i.imgur.com/1ng3gBa.png)

# "Desynced Station"

<span style="font-weight: 400;">By themselves, mobile stations don't offer much. The power of a mobile station is when you "desync" the values, or have different settings for the local player compared to remote. As mentioned in part 1, you can move a station to different locations if you are the one sitting vs everyone else watching. With mobile stations, this won't do anything since the player isn't moving with the station. The biggest trick here is that you can set the station to mobile for the player sitting, and for all other players, set the station to immobile. In this case, the player in the station will be able to move around locally (allowing Network IK to show their animations to everyone else too), but all other players will see the one in the station stuck at the station's position. </span>

<span style="font-weight: 400;">From here, it is up to you and your specific world on how the player should be moved around. </span>

<span style="font-weight: 400;">[![VRChat&#95;qVxhn6TztP.gif](https://vrclibrary.com/wiki/uploads/images/gallery/2022-02/vrchat-qvxhn6tztp.gif)](https://vrclibrary.com/wiki/uploads/images/gallery/2022-02/vrchat-qvxhn6tztp.gif)</span>

[![Desynced Mobile Station UdonGraph](https://i.imgur.com/vXmJZZ1.png)](https://i.imgur.com/vXmJZZ1.png)[![Desynced Mobile Station UdonSharp](https://i.imgur.com/90DwOFM.png)](https://i.imgur.com/90DwOFM.png)[![Desynced Mobile Station CyanTrigger](https://i.imgur.com/70f1VBm.png)](https://i.imgur.com/70f1VBm.png)

# Station Annoyances

<span style="font-weight: 400;">In SDK3, teleporting the player will force exit stations. Depending on your setup and uses, you may want to have a check for if the player exits the station at an unexpected moment. If you initiate the teleport, then you can try to force the player back into the station. Make sure to do this in a delayed way since you cannot force a player back into a station in the same event as OnStationExited. Note that Respawn will also force exit the station, but it is not recommended to force the player back into the station as this is a good way to exit any unusual situations. </span>

<span style="font-weight: 400;">Force entering stations only work for humanoid avatars. If a player is in a generic avatar (or questionable client user), then you will need to check for this.</span>

<span style="font-weight: 400;">Stations only allow one user to be in them at a time. If you want to apply this technique to everyone in the instance, you will need to give every player their own unique station. This can be achieved using object pools. It is recommended to use a system like CyanLaser's </span>[<span style="font-weight: 400;">CyanPlayerObjectPool</span>](https://github.com/CyanLaser/CyanPlayerObjectPool)<span style="font-weight: 400;">.</span>

# 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

<span style="font-weight: 400;">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. </span>

<span style="font-weight: 400;">[https://twitter.com/CyanLaser/status/1180655212568043520](https://twitter.com/CyanLaser/status/1180655212568043520)</span>

#### Example Project Download

All Examples for this guide can be [downloaded from my GitHub](https://github.com/CyanLaser/WallWalkingExamples). 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!

#### <span style="font-weight: 400;">Overview</span>

<span style="font-weight: 400;">There are 4 items required for this system:</span>

1. <span style="font-weight: 400;">The world with duplicates for every orientation</span>
2. <span style="font-weight: 400;">A way to move between each orientation</span>
3. <span style="font-weight: 400;">A system to designate which orientation you are currently</span>
4. <span style="font-weight: 400;">The RRP system + Object Pool to distribute one per player</span>
1. <span style="font-weight: 400;">Position remote players based on your current orientation</span>


<span style="font-weight: 400;">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 </span>*<span style="font-weight: 400;">how </span>*<span style="font-weight: 400;">players are positioned in the scene. </span>

<span style="font-weight: 400;">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. </span>

[![Diagram showing Wall Walking concept](https://i.imgur.com/sjWb0K0.png)](https://i.imgur.com/sjWb0K0.png)

# Preparing the World

<span style="font-weight: 400;">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.</span>

<span style="font-weight: 400;">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:</span>

1. <span style="font-weight: 400;">This will be a safe area outside of the system</span>
1. <span style="font-weight: 400;">Some people can get sick just thinking about odd rotations</span>

3. <span style="font-weight: 400;">A player must initiate to enter the system </span>
1. <span style="font-weight: 400;">You can use this as a way to catch for errors when people don't enter the station</span>

5. <span style="font-weight: 400;">Have an area where you can drop portals</span>
1. <span style="font-weight: 400;">Portals in the system will only be in the orientation dropped</span>


[![Spawn Area](https://i.imgur.com/IyUUz5k.png)](https://i.imgur.com/IyUUz5k.png)

<span style="font-weight: 400;">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.</span>

[![First Room with positive Y up.](https://i.imgur.com/APViH37.png)](https://i.imgur.com/APViH37.png)

<span style="font-weight: 400;">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.  
</span>

[![Second Room with positive X up.](https://i.imgur.com/iPC14Ew.png)](https://i.imgur.com/iPC14Ew.png)

# Moving Between Orientations

<span style="font-weight: 400;">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. </span><span style="font-weight: 400;">Create a new program in your Udon compiler of choice. Interact, Teleport the player to a destination Transform. We will add more later.</span>

[![Teleport Button UdonGraph](https://i.imgur.com/bDfdUvA.png)](https://i.imgur.com/bDfdUvA.png)[![Teleport Button UdonSharp](https://i.imgur.com/jk106Tn.png)](https://i.imgur.com/jk106Tn.png)[![Teleport Button CyanTrigger](https://i.imgur.com/l8CF2Ku.png)](https://i.imgur.com/l8CF2Ku.png)

<span style="font-weight: 400;">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.</span><span style="font-weight: 400;">  
</span>

[![World button layout](https://i.imgur.com/wBL2LZr.png)](https://i.imgur.com/wBL2LZr.png)

# Saving Current Orientation

<span style="font-weight: 400;">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. </span>

<span style="font-weight: 400;">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. </span>

[![Origin UdonGraph](https://i.imgur.com/4VDpix3.png)](https://i.imgur.com/4VDpix3.png)[![Origin UdonSharp](https://i.imgur.com/5T7dkhy.png)](https://i.imgur.com/5T7dkhy.png)[![Origin CyanTrigger](https://i.imgur.com/xojB5ON.png)](https://i.imgur.com/xojB5ON.png)

<span style="font-weight: 400;">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.</span>

[![Updated Button UdonGraph](https://i.imgur.com/H4QYW9w.png)](https://i.imgur.com/H4QYW9w.png)[![Updated Button UdonSharp](https://i.imgur.com/G0t351g.png)](https://i.imgur.com/G0t351g.png)[![Updated Button CyanTrigger](https://i.imgur.com/4Ij26sS.png)](https://i.imgur.com/4Ij26sS.png)

<span style="font-weight: 400;">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.</span>

# Station Object

<span style="font-weight: 400;">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.</span>

<span style="font-weight: 400;">Create a new object. Add a VRCStation component to it. </span>

1. <span style="font-weight: 400;">Set Mobility to Immobilize</span>
2. <span style="font-weight: 400;">Set Seated to unchecked</span>
3. <span style="font-weight: 400;">Set Disable Station Exit to checked</span>
4. <span style="font-weight: 400;">Set Can Use Station From Station to unchecked</span>

<span style="font-weight: 400;">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.</span>

[![Station Hierarchy](https://i.imgur.com/PEV76r4.png)](https://i.imgur.com/PEV76r4.png)[![VRCStation Component](https://i.imgur.com/vWg21if.png)](https://i.imgur.com/vWg21if.png)[![PlayerSync object](https://i.imgur.com/VuGXfHq.png)](https://i.imgur.com/VuGXfHq.png)

# Entering the station

<span style="font-weight: 400;">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.</span>

<span style="font-weight: 400;">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:</span>

1. <span style="font-weight: 400;">Set the station mobility to Mobile</span>
2. <span style="font-weight: 400;">Take ownership over the object sync object</span>
3. <span style="font-weight: 400;">Have the local player enter the station.</span>

[![EnterStation UdonGraph](https://i.imgur.com/OXvvreJ.png)](https://i.imgur.com/OXvvreJ.png)[![EnterStation UdonSharp](https://i.imgur.com/KLPh73Z.png)](https://i.imgur.com/KLPh73Z.png)[![EnterStation CyanTrigger](https://i.imgur.com/tiwse5o.png)](https://i.imgur.com/tiwse5o.png)

<span style="font-weight: 400;">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.</span>

[![Update Origin Program UdonGraph](https://i.imgur.com/ukGfVhd.png)](https://i.imgur.com/ukGfVhd.png)[![Update Origin Program UdonSharp](https://i.imgur.com/27wsoLB.png)](https://i.imgur.com/27wsoLB.png)[![Update Origin Program CyanTrigger](https://i.imgur.com/K7wUpgl.png)](https://i.imgur.com/K7wUpgl.png)

# Handling Players Entering And Leaving The Station

<span style="font-weight: 400;">Since anyone can enter the station, we need to save the player currently in it to know what logic to perform later.</span>

<span style="font-weight: 400;">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.</span>

[![StationEnterExit UdonGraph](https://i.imgur.com/bRPTXt6.png)](https://i.imgur.com/bRPTXt6.png)[![StationEnterExit  UdonSharp](https://i.imgur.com/TBKu2B5.png)](https://i.imgur.com/TBKu2B5.png)[![StationEnterExit  CyanTrigger](https://i.imgur.com/KXWoQAr.png)](https://i.imgur.com/KXWoQAr.png)

# Update Station Position Logic

<span style="font-weight: 400;">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.</span>

1. <span style="font-weight: 400;">Add a public Transform variable. This will be set with the origin we created earlier. </span>
2. <span style="font-weight: 400;">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. </span>
3. <span style="font-weight: 400;">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.</span>

[![Update Loop UdonGraph](https://i.imgur.com/ip0h4Rj.png)](https://i.imgur.com/ip0h4Rj.png)[![Update Loop UdonSharp](https://i.imgur.com/p9I4syE.png)](https://i.imgur.com/p9I4syE.png)[![Update Loop CyanTrigger](https://i.imgur.com/2fslbvw.png)](https://i.imgur.com/2fslbvw.png)

<span style="font-weight: 400;">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.</span>

<span style="font-weight: 400;">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.</span>

[![Updated Variables UdonGraph](https://i.imgur.com/FPVRfst.png)](https://i.imgur.com/FPVRfst.png)[![Update Local UdonGraph](https://i.imgur.com/TtWnqvn.png)](https://i.imgur.com/TtWnqvn.png)[![Update Local UdonSharp](https://i.imgur.com/lrfyk10.png)](https://i.imgur.com/lrfyk10.png)[![Updated Variables CyanTrigger](https://i.imgur.com/Gm1AHQQ.png)](https://i.imgur.com/Gm1AHQQ.png)[![Update Local CyanTrigger](https://i.imgur.com/aJuAvGx.png)](https://i.imgur.com/aJuAvGx.png)

<span style="font-weight: 400;">Now to handle updates for the remote player. We basically need to do the opposite here. </span>

<span style="font-weight: 400;">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.</span>

[![Update Remote UdonGraph](https://i.imgur.com/Ujc6038.png)](https://i.imgur.com/Ujc6038.png)[![Update Remote UdonSharp](https://i.imgur.com/5iJO4Rw.png)](https://i.imgur.com/5iJO4Rw.png)[![Update Remote CyanTrigger](https://i.imgur.com/2lkWJ8T.png)](https://i.imgur.com/2lkWJ8T.png)

<span style="font-weight: 400;">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</span>

[![Update EnterStation UdonGraph](https://i.imgur.com/ynHgAuL.png)](https://i.imgur.com/ynHgAuL.png)[![Update OnStationExited UdonGraph](https://i.imgur.com/qnidKog.png)](https://i.imgur.com/qnidKog.png)[![Update EnterStation UdonSharp](https://i.imgur.com/sAFXLz0.png)](https://i.imgur.com/sAFXLz0.png)[![Update OnStationExited UdonSharp](https://i.imgur.com/6txYpPs.png)](https://i.imgur.com/6txYpPs.png)[![Update EnterStation CyanTrigger](https://i.imgur.com/n1nNYUh.png)](https://i.imgur.com/n1nNYUh.png)[![Update OnStationExited CyanTrigger](https://i.imgur.com/4xkDvBp.png)](https://i.imgur.com/4xkDvBp.png)

<span style="font-weight: 400;">With this, one player can enter the station, and other players can observe them walking on the wall. </span>

<span style="font-weight: 400;">[![VRChat&#95;4QFrDRku9h.gif](https://vrclibrary.com/wiki/uploads/images/gallery/2022-02/vrchat-4qfrdrku9h.gif)](https://vrclibrary.com/wiki/uploads/images/gallery/2022-02/vrchat-4qfrdrku9h.gif)</span>

# Player Object Pool Setup

<span style="font-weight: 400;">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. </span>

<span style="font-weight: 400;">Install the following in this order:</span>

1. [<span style="font-weight: 400;">https://github.com/MerlinVR/UdonSharp</span>](https://github.com/MerlinVR/UdonSharp)<span style="font-weight: 400;"> </span>
2. [<span style="font-weight: 400;">https://github.com/CyanLaser/CyanPlayerObjectPool</span>](https://github.com/CyanLaser/CyanPlayerObjectPool)<span style="font-weight: 400;"> </span>

<span style="font-weight: 400;">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. </span>

1. <span style="font-weight: 400;">First find and drag the PlayerObjectAssigner prefab into your scene. </span>
1. <span style="font-weight: 400;">This will also add the PlayerObjectPool prefab into the scene. </span>

3. <span style="font-weight: 400;">Click the PlayerObjectPool prefab and set the pool size to double the world capacity. </span>
4. <span style="font-weight: 400;">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. </span>
1. <span style="font-weight: 400;">You will notice that the assigner has spawned the prefab many times as children. </span>
2. <span style="font-weight: 400;">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. </span>

# Linking Origin to the ObjectPool

<span style="font-weight: 400;">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.</span>

<span style="font-weight: 400;">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.</span>

[![LocalPlayerAssigned UdonGraph](https://i.imgur.com/6yKA8Il.png)](https://i.imgur.com/6yKA8Il.png)[![LocalPlayerAssigned UdonSharp](https://i.imgur.com/sZXNsQn.png)](https://i.imgur.com/sZXNsQn.png)[![LocalPlayerAssigned CyanTrigger](https://i.imgur.com/USsPeYE.png)](https://i.imgur.com/USsPeYE.png)

<span style="font-weight: 400;">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.</span>

<span style="font-weight: 400;">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.</span>

[![OnPlayerAssigned UdonGraph](https://i.imgur.com/ZTFAkPj.png)](https://i.imgur.com/ZTFAkPj.png)[![OnPlayerAssigned UdonSharp](https://i.imgur.com/hcItfem.png)](https://i.imgur.com/hcItfem.png)[![OnPlayerAssigned CyanTrigger](https://i.imgur.com/nOhLrya.png)](https://i.imgur.com/nOhLrya.png)

<span style="font-weight: 400;">At this point, the system should be fully usable. Upload a build and join it with multiple clients to test it out.</span>

[![Multiple Players Wall Walking](https://i.imgur.com/Vj0iXbn.gif)](https://i.imgur.com/Vj0iXbn.gif)

# Other Improvements

<span style="font-weight: 400;">Since this is just the basics, there are other items that can be done to improve the system: </span>

1. <span style="font-weight: 400;">Verify the player has a pool object before enabling buttons to enter the world.</span>
2. <span style="font-weight: 400;">Verify the player entered the station before doing other logic. </span>
1. <span style="font-weight: 400;">Generic avatars and </span>*<span style="font-weight: 400;">questionable clients</span>*<span style="font-weight: 400;"> will cause this to break for people. </span>

4. <span style="font-weight: 400;">Change teleporting to use player enter trigger based instead of clicking a button.</span>
1. <span style="font-weight: 400;">Teleportation could use seamless teleport so the player’s rotation is preserved.</span>

6. <span style="font-weight: 400;">Disable the station Udon while no one is sitting to reduce update calls</span>
1. <span style="font-weight: 400;">On someone enter, enable the UdonBehaviour, on exit disable the UdonBehaviour.</span>

# Other World Ideas

<span style="font-weight: 400;">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:</span>

1. <span style="font-weight: 400;">Ring world</span>
2. [<span style="font-weight: 400;">Sphere world</span>](https://twitter.com/CyanLaser/status/1238918516490690560)
3. <span style="font-weight: 400;">Free form rotation</span>

# Example VRChat Worlds

<span style="font-weight: 400;">Here are some worlds you can try now that use this technique in VRChat:</span>

1. [<span style="font-weight: 400;">Wall Walking by CyanLaser (Private)</span>](https://vrchat.com/home/launch?worldId=wrld_ded1f112-d647-4fea-bf83-cbe033c2aad8)
2. [<span style="font-weight: 400;">The Sphere by CyanLaser</span>](https://vrchat.com/home/launch?worldId=wrld_bff9dcbd-d43a-4815-8dea-ef0d9455feba)
3. [<span style="font-weight: 400;">External Sphere by CyanLaser (Private)</span>](https://vrchat.com/home/launch?worldId=wrld_68ccc0e1-3ee6-4319-8efb-4b787abf5340&instanceId=92514~hidden(usr_cc00ac2c-e64e-4111-be5b-c852a44ff2f1)~region(us)~nonce(7839d8e8-b452-4943-ba94-84ce74daccf1))
4. [<span style="font-weight: 400;">Interplanetary Cruise Ship by S\_ASAGIRI</span>](https://vrchat.com/home/launch?worldId=wrld_182dcca9-c877-4272-b8e8-8f265d419591)
5. [<span style="font-weight: 400;">Escher by suzuki\_i</span>](https://vrchat.com/home/launch?worldId=wrld_24a47657-0ee2-4130-b3f2-32fd6c8950d4)
6. [<span style="font-weight: 400;">ANTI-GRAVITY SATELLITE by lunar eclipse</span>](https://vrchat.com/home/launch?worldId=wrld_fbb65a01-6f43-4d0c-b818-048a43591966)