Give Page Feedback | API

Players - Moving Players

Normal Player movement involves gradual changes in the position of the Player. These changes are tracked by the Active Grid and used to shift or change the active Loading Pattern, resulting in small changes to the World layout.

This Section is focused on non-normal Player movements where the Player is transported across large distances instantaneously. In a normal game where the entire game world is loaded at all times such movement does not need to be handled in a special way, but with streaming solutions special care needs to be taken as the assets where the Player is to be transported to may not be loaded!

In this Section we will talk about both manual movement and automatic movement, and will also discuss the means by which Players are moved. To start, let's discuss some methods available to you via the Active Grid class that allow you to manually reposition the Player.

TryMovePlayerToLocation

This is the most basic of the movement methods and will simply move the player from one location to another. The benefits of using this method rather than repositioning the Player yourself is that the location that the Player is to be moved to will be pre-loaded before the movement occurs. This ensures that all assets needed at the location are present before the Player is moved. The method will also remove assets from the area the Player was previously is so long as they are no longer needed (after the Player has been moved, of course). This method can be used to perform some kind of transport effect, however because the time it takes to pre-load the new area will vary depending on several factors, so the transport effect must account for this.

An issue arises when using this method with very large worlds, however, as if the area you want to move the Player to is outside of the safe area (within a certain distance to the origin as to avoid floating point issues), the player will be moved to a point outside of the area. If auto-recentering is configured correctly on the World and Active Grid, this should automatically trigger a re-centering operation that brings the Player back inside the safe area, however for a short period of time both the Player and the world assets in the new area will be loaded outside of the safe area, which may be problematic.

TryMovePlayerAndChangeOriginCellOfWorld

This method combines an origin cell change for whatever World the Active Grid is synced to with a Player movement operation. The method removes its claim on the current area of cells surrounding the player and places a new claim on cells at the new area at the same time as initiating the origin cell change.

This means that it is likely that the assets surrounding the player's current location will be removed, which will likely be problematic if you do not disable the Player while this method is executing. There is also a good chance that the origin cell update will be very noticeable to the Player. For both of these reasons, it is recommended to use this method with a loading screen or some other technique that hides the World from the Player's view.

While the Player may still fall outside of the safe area when using this method with large Worlds, the time they fall outside the safe area will typically be much shorter than with TryMovePlayerToLocation. In addition, no World Assets will be loaded outside of the safe area when using this method, which may be more important than the Player falling outside it. The method also has the advantage of not loading or shifting more assets than needed during the origin cell change, since the old cells surrounding the Players old position are unloaded before the origin cell change is carried out.

Additional Option For Moving The Player On Large Worlds

There is a third option for moving the Player that reduces the change for Assets or the Player to fall outside the safe area:

1) Calculate a new Origin Cell for the World that would result in both the new Area that the Player is travelling to and the old area that the Player is travelling from to be in the safe area. If such a Cell does not exists (which is definitely possible), calculate an Origin Cell that limits the distance the Player is from the Origin while ensuring that the new area the Player will be moved to is within the safe area.

2) Query the World.IsOriginCellUpdateQueuedOrInProgress property and once it returns false, initiate an Origin Cell Change via the World's ChangeOriginCell method.

3) Wait for the Origin Cell Change to complete. The easiest way to do this is to create a class that implements the IWorldUser interface and then register with the World as a user via the World's Register method. Details on how to do this can be found in the Worlds Chapter.

4) Once the Origin Cell Change completes, move the player using the ActiveGrid.TryMovePlayerToLocation method.

The difference between this technique and the TryMovePlayerAndChangeOriginCellOfWorld method is that all Cells surrounding the Player's old position are retained until after the Player has been moved. This makes it more suitable to be used in-game without a loading screen or other obfuscation technique. Do note, however, that this method will use more memory than TryMovePlayerAndChangeOriginCellOfWorld and will be slower, however you are free to let the Player move around freely during the entire procedure.

TryMovePlayerToLocationOnNewWorld

This is an additional method that specializes in moving the Player to a specific location on a new World. Basically, the Active Grid is synced to the new World while also performin a movement operation. The method is intended as a means of in-game teleportation, for instance from one Planet to another.

Because of this, the Cells belonging to the new World are always loaded before the Cells from the old World are unloaded, which may not be ideal (as it will use more memory). If you wish for the old World to be unloaded before loading the new World, you must manually desync from the current World, move the Player, then sync to the new World using the TrySyncToNewWorldAroundPlayer method. You will likely want to disable the Player while doing this and show a loading screen.

Wait For Command Before Moving Player

The three manual movement methods mentioned above all have an argument called waitForCommandBeforeMovingPlayer. When you pass in true for this argument, Cells at the Player's new location are loaded, but the Player is not automatically moved once they are loaded. Instead, the method waits for you to initiate the movement, which can done as a result of the Player pressing some in game button, or some other action.

The correct way to use this technique is to query the ActiveGrid.PlayerReadyToBeMoved property of the Active Grid after you have called one of the manual movement methods. This property will return true only after the new Cells in the area that the Player is being moved to are loaded. Once it returns true, call ActiveGrid.InitiatePendingMove, which tells the method that it's okay to move the Player.

--Special Note--
This technique is not intended to replace Player Movers (described below), as the technique cannot be used with automatic Player Movement that results from Origin Cell Changes!

Automatic Origin Cell Movement

In addition to the manual movement methods listed above, the Player may also be moved automatically as a result of an Origin Cell Change. This will occur when the Active Grids's Move Player After Origin Cell Change option is enabled, or when using a Standard Hierarchy Organizer World Shifter with the Move Players option enabled (and Active Grids assigned to it).

Only one of these options should be enabled. If both are enabled, a double movement may occur whenever the Origin Cell is changed.

How Are Players Moved?

By default, Player Movement is carried out by directly setting the Position property of the IPlayer object. The IPlayer implementation handles what actually happens to change the position, though if the Player is a Transform then the Transform.position will simply be adjusted.

Whatever the IPlayer implementation, the position change occurs in a single frame, which may be problematic if the Player is interacting with the Physics system. To get around this, we have introduced the PlayerMover abstract class. This class uses IEnumerators in order to execute the Player Movement over multiple frames. In reality, the multiple frames are not usually needed to move the player, as the movement operation is usually fairly simple and doesn't have a major performance cost.

Instead, the multiple frames are needed for timing purposes, as it gives you a chance to skip specific frames in order to wait for a frame where the Player can be moved without falling through the World or other physics objects.

This class contains two methods, MovePlayerByAmount and MovePlayerToPosition. MovePlayerToPosition is used to move the player to a set, fixed position, while MovePlayerByAmount is used for origin cell changes to offset the Player position by a specific vector amount. You may be wondering why the first method exist. Why can't we manually calculate the new position by taking the current position of the player and adding the offset? If we did so, only the MovePlayerToPosition method would be necessary, right!?

The reason has to do with how these methods execute, which is via IEnumerators which can execute over multiple frames. The IEnumerator are needed to ensure that you can wait multiple frames in order to time the movement with a specific point in your Physics cycle, to avoid the Player falling through the World or other objects. If we were to pre-calculate the final position, and then the Player moved between the frame the method was called and the frame when the player was actually moved, the pre-calculated position would actually represent the player's position in the past. This would result in the Player rubber banding, similar to what happens with low latency multiplayer games.

--Special Note--
The Player Mover methods have a return type of IEnumerator. This return type is special as it allows the method to be iterated over, possibly over multiple frames. This allows the execution's performance impact to be spread out over multiple frames, which is awesome! There are two strategies for implementing methods with this return type:

1) Simply include yield return statements within the method's body where the returned object is either null or an instance of a YieldInstruction.

The compiler will auto generate a state machine class, which will be used to iterate the method's logic. The drawback of this technique is every time the method is called, a new instance of the auto generated class is created, resulting in garbage generation throughout the lifetime of the game.

2) Implement one of the reusable enumerator classes found within the API.

This strategy is harder to implement as you will need to implement the state machine logic yourself, as well as perform some other code related task necessary to use the reusable enumerators. It is recommended for experienced coders only!

If you elect to go with option 2, you can find detailed information on how to use the Reusable Enumerator classes in the Yield Enumerator Section within the Secondary Non Components Chapter.

Yield Enumerator Classes