3 - Movement & Camera
Overview
In Fusion 103 Shared the existing scene gets extended by adding player movement based on player input and a first person camera that follows the correct player object.
Player Movement
In Fusion, gameplay code that updates every tick such as movement should not run in Update / FixedUpdate
. Instead, FixedUpdateNetwork
should be used. This ensures that the movement is smooth and interpolates correctly on all clients.
Create a new script and name it PlayerMovement
. Have it inherit from NetworkBehaviour
instead of MonoBehaviour
and add the following code to the script:
C#
private CharacterController _controller;
public float PlayerSpeed = 2f;
private void Awake() {
_controller = GetComponent<CharacterController>();
}
public override void FixedUpdateNetwork()
{
// Only move own player and not every other player. Each player controls its own player object.
if (HasStateAuthority == false)
{
return;
}
Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")) * Runner.DeltaTime * PlayerSpeed;
_controller.Move(move);
if (move != Vector3.zero)
{
gameObject.transform.forward = move;
}
}
HasStateAuthority
can be used to check whether the client controls an object. NetworkTransform
only synchronizes changes from the StateAuthority
to everyone else. If another client changes the position of the object that change will only be a local change and overridden with data from the network in the future.
Add the PlayerMovement
component to the player prefab.
Camera
There are many ways of setting up a camera (third person, first person etc.) and different ways to implement them. In a multiplayer game often a single camera that is following the local player object is desired. There are two common ways to achieve this.
- When the local player object gets spawned, have it instantiate a camera.
- Have the camera as part of the scene. When the local player object gets spawned have it find the camera and set the target of the camera to it.
In this walkthrough the second approach is used.
Create a new script and name it FirstPersonCamera
. Add the following code to it:
C#
using UnityEngine;
public class FirstPersonCamera : MonoBehaviour
{
public Transform Target;
public float MouseSensitivity = 10f;
private float vertialRotation;
private float horizontalRotation;
void LateUpdate()
{
if (Target == null)
{
return;
}
transform.position = Target.position;
float mouseX = Input.GetAxis("Mouse X");
float mouseY = Input.GetAxis("Mouse Y");
vertialRotation -= mouseY * MouseSensitivity;
vertialRotation = Mathf.Clamp(vertialRotation, -70f, 70f);
horizontalRotation += mouseX * MouseSensitivity;
transform.rotation = Quaternion.Euler(vertialRotation, horizontalRotation, 0);
}
}
This is a very simple first person camera implementation. Note that the code does not contain any multiplayer elements. You can replace this camera with any other camera setup that works with a target object including Cinemachine.
Put the FirstPersonCamera
behaviour on the MainCamera
GameObject in the scene. There is no need to drag in a target. The target will be set up at runtime by code.
To assign the target, open the PlayerMovement
script. Add a variable to store the camera:
C#
public Camera Camera;
Then when the player object gets spawned find and set up the camera if it is the local player.
C#
public override void Spawned()
{
if (HasStateAuthority)
{
Camera = Camera.main;
Camera.GetComponent<FirstPersonCamera>().Target = GetComponent<NetworkTransform>().InterpolationTarget;
}
}
Make sure to always use Spawned
instead of Awake/Start
when initializing NetworkObjects
. In Awake/Start
the NetworkObject
might not be ready to use yet.
HasStateAuthority
is only true for objects the player controls, so only the local player object and not other player objects.Finally, since in first-person games the player character moves in the look direction adjust the movement a bit. Replace the line where the Vector3 move
gets calculated with:
C#
var cameraRotationY = Quaternion.Euler(0, Camera.transform.rotation.eulerAngles.y, 0);
Vector3 move = cameraRotationY * new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")) * Runner.DeltaTime * PlayerSpeed;
Handling Button Presses
One time inputs such as GetButtonDown
and up are raised in Unity during Update
. In Fusion gameplay code such as movement should be executed in FixedUpdateNetwork
. This means special handling is necessary for tracking button states accurately.
There are multiple approaches to capturing inputs like that in FixedUpdateNetwork
:
- Query for button down in
Update
. Store the result in a bool and use that for game logic. Clear it at the end ofFixedUpdateNetwork
. - Use Fusion's NetworkInput with
NetworkButtons
. - Use the new Unity Input System and set
Update Mode
toManual Update
and callInputSystem.Update
inFixedUpdateNetwork
. ()
Jumping
Let's implement an example for handling button presses by implementing a jump action for the player. In this case option 1 from the options listed in the previous chapter is used.
Start by adding the following variables to PlayerMovement
:
C#
public float JumpForce = 5f;
public float GravityValue = -9.81f;
private Vector3 velocity;
private bool _jumpPressed;
_jumpPressed
will be used to track the button state. velocity
is for adding artificial gravity to the KCC while airborne.
To poll the button down state each Update
add the following code:
C#
void Update()
{
if (Input.GetButtonDown("Jump"))
{
_jumpPressed = true;
}
}
Then at the end of FixedUpdateNetwork
clear the jump state:
C#
_jumpPressed = false;
Replace the line that calls _controller.Move
with the following:
C#
_velocity.y += GravityValue * Runner.DeltaTime;
if (_jumpPressed && _controller.isGrounded)
{
_velocity.y += JumpForce;
}
_controller.Move(move + _velocity * Runner.DeltaTime);
Finally, add the following immediately after the HasStateAuthority
check to reset the velocity when on the ground:
C#
if (_controller.isGrounded)
{
velocity = new Vector3(0, -1, 0);
}
Return to Unity and start the game and press Start Shared Client
. Press the Space
button and see the character jumping.
Next Shared Mode Basics 4 - Network Properties
Back to top