新增FishNet文档

This commit is contained in:
2026-09-20 15:53:11 +08:00
parent c403145db9
commit 179c282405
431 changed files with 15476 additions and 0 deletions
@@ -0,0 +1,18 @@
---
description: >-
Whether you're new to network programming or transitioning to Fish-Networking
for your latest project, this guide will provide the essential foundation you
need to get started smoothly.
cover: ../../.gitbook/assets/getting-started-graphic.jpeg
coverY: 0
---
# Getting Started
### What to expect
This section introduces key features of Fish-Networking that you'll use frequently in your development journey. You'll find dedicated resources to deepen your understanding and step-by-step guides tailored for common first-time tasks.
By following this guide, you'll gain insight into setting up your project, getting your clients connected to your server, and leveraging core networking capabilities.
If you're ready to dive in, let’s get started on building robust networked applications with Fish-Networking!
@@ -0,0 +1,23 @@
---
description: Where to go from here?
---
# Beyond the Basics
After having gone through the previous guides, it's recommended that you read the following sections to get a better understanding of how Fish-Networking works and its most commonly used features.
## Important features of FishNet
<table data-view="cards" data-full-width="false"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><i class="fa-plane-departure">:plane-departure:</i> <strong>High-Level Overview</strong></td><td>Get a high-level overview of multiplayer games with FishNet.</td><td><a href="../../guides/high-level-overview/">high-level-overview</a></td></tr><tr><td><i class="fa-vector-square">:vector-square:</i> <strong>Networked GameObjects and Scripts</strong></td><td>Learn about Networking GameObjects and Scripts.</td><td><a href="../../guides/features/networked-gameobjects-and-scripts/">networked-gameobjects-and-scripts</a></td></tr><tr><td><i class="fa-server">:server:</i> <strong>Server and Client Identification</strong></td><td>Find out how to determine if code is running on the server or client.</td><td><a href="../../guides/features/server-and-client-identification/">server-and-client-identification</a></td></tr><tr><td><i class="fa-fingerprint">:fingerprint:</i> <strong>Network Connections</strong></td><td>Understand how FishNet identifies different clients.</td><td><a href="../../guides/features/server-and-client-identification/networkconnections.md">networkconnections.md</a></td></tr><tr><td><i class="fa-at">:at:</i> <strong>Ownership</strong></td><td>Discover what owning a NetworkObject means and how clients can do so.</td><td><a href="../../guides/features/ownership/">ownership</a></td></tr><tr><td><i class="fa-location-plus">:location-plus:</i> <strong>Spawning</strong></td><td>Get to grips with spawning objects across the network.</td><td><a href="../../guides/features/networked-gameobjects-and-scripts/spawning/">spawning</a></td></tr><tr><td><i class="fa-house-signal">:house-signal:</i> <strong>Synchronized Variables</strong></td><td>Learn about FishNet's automatically synchronized variables and how to use them!</td><td><a href="../../guides/features/network-communication/synchronizing/">synchronizing</a></td></tr><tr><td><i class="fa-messages">:messages:</i> <strong>Remote Procedure Calls</strong></td><td>Find out about how to call methods remotely.</td><td><a href="../../guides/features/network-communication/remote-procedure-calls.md">remote-procedure-calls.md</a></td></tr><tr><td><i class="fa-binary-circle-check">:binary-circle-check:</i> <strong>Data Serialization</strong></td><td>Take a look at how FishNet can automatically serialize your data to send across the network, and how you can manually control it too!</td><td><a href="../../guides/features/data-serialization/">data-serialization</a></td></tr><tr><td><i class="fa-calendar-circle-exclamation">:calendar-circle-exclamation:</i> <strong>Network State Events</strong></td><td>Get information about the most commonly used network events, such as players connecting and the network starting or stopping.</td><td><a href="../../guides/features/network-callbacks.md">network-callbacks.md</a></td></tr><tr><td><i class="fa-house-tree">:house-tree:</i> <strong>Scene Management</strong></td><td>Learn about managing scenes while the network is connected.</td><td><a href="../../guides/features/scene-management/">scene-management</a></td></tr></tbody></table>
### More Feature Guides
<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><i class="fa-book-open">:book-open:</i> <strong>Feature Guides</strong></td><td>Explore guides for even more FishNet features, including Client-side Prediction, Area of Interest, Transports, Addressables, and Lag Compensation.</td><td><a href="../../guides/features/">features</a></td></tr></tbody></table>
## Moving on and getting help
After reading through those sections you are encouraged to jump in and start making projects using FishNet! There are some available demos on specific Fish-Networking features included in the FishNet asset itself, these can be a great tool for learning more about the specific features.
If you run into errors or issues you can check out the [troubleshooting](../../guides/troubleshooting/ "mention") guides to find solutions to common problems. You are also welcome to join the official Discord community and ask questions and learn from others there: [https://discord.gg/Ta9HgDh4Hj](https://discord.gg/Ta9HgDh4Hj)
_Now go create something amazing!_
@@ -0,0 +1,73 @@
---
description: >-
Learn how to connect clients to a remote FishNet server, allowing players on
different machines to join your game.
---
# Connecting to Remote Devices
Once you have a basic networked game set up and can connect as a host, the next crucial step for true multiplayer is enabling clients to connect to a server running on a different machine. This guide will walk you through the process of connecting to remote devices.
{% stepper %}
{% step %}
### **Identifying the server's IP address**
For a client to connect to a remote server, it needs to know the server's IP address.
* **Local Network (LAN):** If the server and client are on the same local network (e.g., two computers in the same house), you'll need the server machine's local IP address (e.g., `192.168.1.100`). You can usually find this through your operating system's network settings.
* **Public Network (Internet):** If clients are connecting over the internet, you'll need the server's public IP address. This is often the IP address of the router that the server machine is connected to. You might need to set up **port forwarding** on the server's router to allow incoming connections to the game server's port.
{% hint style="info" %}
For testing on a local network, you can often find your machine's IP address by opening a command prompt (Windows) and typing `ipconfig`, or a terminal (macOS/Linux) and typing `ifconfig` or `ip a`. Look for the IPv4 address associated with your active network adapter.
{% endhint %}
{% endstep %}
{% step %}
### **Configuring the client to connect**
To connect as a client to a remote server, you'll need to manually set the server's address.
1. **Locate the NetworkManager:** In your Unity scene, select the **NetworkManager** game object.
2. **Select or add the Transport Component:** In the Inspector, locate the [Transport](../../guides/high-level-overview/transports.md) component (e.g., [Tugboat](../../fishnet-building-blocks/transports/tugboat.md) if you're using the default). If there is none, add Tugboat now.
3. **Set the Address:** Find the `Client Address` field within the Transport component. Enter the IP address of your remote server here. For example, if your server's local IP is `192.168.1.100`, you would enter that.
4. **Set the Port (if different):** The default port for FishNet is `7777`. If your server is configured to use a different port, update the `Port` field accordingly.
{% hint style="info" %}
The `NetworkHudCanvas` is a convenient debugging tool. In a finished game, you would typically create your own UI for players to enter an IP address and connect.
{% endhint %}
<figure><img src="../../.gitbook/assets/tugboat-component.png" alt=""><figcaption></figcaption></figure>
{% endstep %}
{% step %}
### **Building and testing the connection**
To test connecting to a remote device, you'll either need to build your game or setup your Unity editor with your project on the other devices.
1. **Build the Client:** Go to `File > Build Settings...`. Ensure your scene is added to "Scenes In Build". Select your target platform (e.g., Windows, macOS, Android). Click `Build` and choose a location to save your executable.
2. **Start the Server:** Launch one instance of your game (either from the Unity Editor, a regular build or a dedicated server build) on the machine that will act as the server. If it's a dedicated server build then the FishNet server will start automatically, otherwise you will need to make sure you start it with the NetworkHudCanvas Server button, Autostart option, or through your own code.
3. **Launch the Client:** On a _separate_ machine, launch the client build you just created (or your project from the Unity editor on the separate machine).
4. **Connect:** If you disabled auto-start, click the "Start Client" button from the NetworkHudCanvas in the client build.
If configured correctly, the client should connect to the server, and you should see network-related messages in both the server and client consoles indicating a successful connection. You should also see players spawning as configured in the [preparing-your-player.md](preparing-your-player.md "mention") guide.
{% endstep %}
{% step %}
### **Troubleshooting common connection issues**
If your client fails to connect, consider these common issues:
* **Incorrect IP Address:** Double-check that the IP address entered in the Transport component is correct for the server machine. Also make sure you entered it in the "Client Address" field and not one of the "Server Bind Address" fields.
* **Firewall:** Ensure that the firewall on the server machine is not blocking incoming connections on the game's port (default 7777). You may need to create an inbound rule to allow traffic.
* **Port Forwarding (for Internet connections):** If connecting over the internet, the server's router needs to be configured to forward the game's port (e.g., 7777) to the server machine's local IP address. Without this, external clients cannot reach your server.
* **Server Not Running:** Verify that the server instance of your game is actually running and has successfully started its network services.
* **Different Game Versions:** Ensure both the server and client are running the exact same version of your game build. Mismatched versions can cause connection failures.
* **Network Congestion/Latency:** On public networks, high latency or packet loss can sometimes lead to connection timeouts.
By carefully checking these points, you can resolve most connection issues and get your remote clients connected.
{% hint style="info" %}
If you are trying to test over the Internet, it may be easier and safer to use an application to create a virtual LAN instead of portforwarding your router.
{% endhint %}
{% endstep %}
{% endstepper %}
@@ -0,0 +1,78 @@
---
description: >-
The first step to networking is connecting over the network. Learn about that
process here!
layout:
width: default
title:
visible: true
description:
visible: true
tableOfContents:
visible: true
outline:
visible: true
pagination:
visible: true
metadata:
visible: true
---
# Getting Connected
{% stepper %}
{% step %}
### **Creating the NetworkManager**
The **NetworkManager** is a central component of FishNet that manages the networking lifecycle, including establishing connections, hosting sessions, and handling client-server communication. For convenience, FishNet has a ready-made NetworkManager prefab that you can use. You can find the prefab located in the FishNet directory under `Demos/Prefabs/NetworkManager.prefab`.\
Drag and drop this NetworkManager prefab into your Unity scene.
<figure><img src="../../.gitbook/assets/adding-network-manager.png" alt="Adding the NetworkManager Prefab"><figcaption><p>Adding the NetworkManager prefab</p></figcaption></figure>
{% hint style="info" %}
If you imported FishNet from the git URL instead of the asset store or .unitypackage file, then the location of the FishNet directory will be `Packages/FishNet: Networking Evolved` instead of `Assets/FishNet`
{% endhint %}
{% endstep %}
{% step %}
### **Configuring FishNet to auto start**
The **NetworkHudCanvas** is a user interface provided by the example **NetworkManager** designed for quick testing of network connections. It displays buttons to start as a client and/or server. It also allows you to automatically start as a server, client, or host as soon as the **NetworkManager** loads in the game.
Expand the **NetworkManager's** fold-out icon to see its child game objects and select the **NetworkHudCanvas** game object. Find the **Network Hud Canvases** component on the object and set the **Auto Start Type** to **Host**.
This configuration will ensure that the application automatically starts as a host (a server and a client at the same time) when the scene is loaded.
<figure><img src="../../.gitbook/assets/setting-autostart-type.png" alt="Setting the Auto Start Type"><figcaption><p>Setting the Auto Start Type</p></figcaption></figure>
{% endstep %}
{% step %}
### **Testing the connection**
Once everything is set up, press the Play button in Unity's Editor to launch your scene.
The two Server and Client buttons in the top left of the game window should turn blue to indicate that the server and both active.
{% hint style="info" %}
If the buttons do not look like the example image then it just means your project is using Unity's new input system package; this is not a problem and you can simply check if the server and client are started by the button names being "Stop Server" and "Stop Client" respectively.
{% endhint %}
Additionally the console will log some network-related messages. Successful connection messages should appear, indicating that the host and client are running properly.
<figure><img src="../../.gitbook/assets/successful-connection-logs.png" alt="Successful connection logs"><figcaption><p>Successful connection logs</p></figcaption></figure>
{% endstep %}
{% endstepper %}
{% hint style="success" %}
You will see following warning: "Player prefab is empty and cannot be spawned for connection 0."
This is fine and will be dealt with in the next section.
{% endhint %}
By following these steps, you should now have a functional network setup in your Unity project using FishNet. This guide provides the foundation for more complex networking workflows, such as syncing objects and managing connections.
{% hint style="info" %}
Download the project files with these completed steps here, or explore the repository:
<a href="https://github.com/maxkratt/fish-networking-getting-started/releases/download/getting-connected-complete/getting-connected.unitypackage" class="button primary" data-icon="down-to-line">Source Files</a> <a href="https://github.com/maxkratt/fish-networking-getting-started/tree/getting-connected" class="button secondary" data-icon="github">Repository</a>
{% endhint %}
@@ -0,0 +1,89 @@
---
description: >-
Step-by-step instructions for installing Fish-Networking into your Unity
project.
---
# Installing Fish-Networking
{% hint style="info" %}
If after importing the package you get a message asking to automatically update the API, select either one of the **Yes** options.
{% endhint %}
### The Unity Asset Store
Installing via the Unity Asset Store is the easiest method and ensures you get a stable release:
1. Open your Unity project.
2. Go to **Window > Asset Store** or visit the [Unity Asset Store online](https://assetstore.unity.com/).
3. Search for **FishNet**.
4. Click **Add to My Assets** and then **Open in Unity**.
5. In Unity, go to **Package Manager** (`Window > Package Manager`).
6. Select **My Assets**, find **FishNet**, and click **Download** and then **Import**.
{% hint style="success" %}
Recommended for most users who want a stable release without version control setup.
{% endhint %}
***
### Unity package file
Use this method if you have a `.unitypackage` file, for example, downloaded from [GitHub Releases](https://github.com/FirstGearGames/FishNet/releases), from the [Fish-Networking website](https://www.fish-networking.com/), or shared by a team member.
1. Download the FishNet `.unitypackage` file.
2. In Unity, go to **Assets > Import Package > Custom Package…**
3. Select the downloaded `.unitypackage` file.
4. Click **Import** to bring all the FishNet files into your project.
{% hint style="info" %}
Best for offline installs or specific package versions not yet on the Asset Store.
{% endhint %}
***
### Git URL (via Unity Package Manager)
This method keeps FishNet up to date via a Git repository. Ideal if you want the latest updates or to contribute to development.
1. Open your Unity project.
2. Go to **Window > Package Manager**.
3. Click the **+** button in the top-left corner.
4. Select **Add package from Git URL…**
5. Enter the following URL:
```
https://github.com/FirstGearGames/FishNet.git?path=Assets/FishNet
```
6. Click **Add** to install.
{% hint style="warning" %}
Ensure you have the [Git client](https://git-scm.com/) (minimum version 2.14.0) installed on your system and added to the PATH system environment variable.
{% endhint %}
***
### Installing FishNet Pro
Installing FishNet Pro can be done in the same way as the first steps listed above.
If you do need more details though, you can find complete steps for purchasing and installing FishNet Pro at these pages:
<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>FishNet Pro, Projects, and Support</strong></td><td><mark style="color:blue;">Details about what FishNet Pro is, the available paid Projects, and Professional Support.</mark></td><td><a href="../../overview/readme/pro-projects-and-support.md">pro-projects-and-support.md</a></td></tr><tr><td><strong>Installing FishNet Pro</strong></td><td><mark style="color:blue;">Instructions for how to purchase and install FishNet Pro.</mark></td><td><a href="../../guides/updating-fishnet/upgrading-to-fishnet-pro.md">upgrading-to-fishnet-pro.md</a></td></tr></tbody></table>
***
### How to fix potential errors after importing?
After importing FishNet it is possible you will encounter errors, this can happen if you are using an unsupported version of Unity, such as one that is far too old or yet too new. [unity-compatibility.md](../../overview/readme/features/unity-compatibility.md "mention")
It can also happen if you have a conflicting asset, such as Netcode for GameObjects, or if your API compatibility level is incorrect.
For example, if you receive the following errors after importing FishNet, then make sure you don't have Netcode for GameObjects inside your project. The files it uses will clash with FishNet's files and you will need to remove it first and then import FishNet again.
```
Assets\FishNet\CodeGenerating\ILCore\ILCoreHelper.cs(18,13): error CS0246: The type or namespace name 'PostProcessorAssemblyResolver' could not be found (are you missing a using directive or an assembly reference?)
Assets\FishNet\CodeGenerating\ILCore\ILCoreHelper.cs(24,50): error CS0246: The type or namespace name 'PostProcessorReflectionImporterProvider' could not be found (are you missing a using directive or an assembly reference?)
```
If it's not one of these problems, or you are unsure what exactly it is, feel free to reach out to us in our official Discord server: [https://discord.com/invite/Ta9HgDh4Hj](https://discord.com/invite/Ta9HgDh4Hj)
@@ -0,0 +1,151 @@
---
description: Get your player objects moving around and synchronized!
---
# Moving Your Player Around
Now that your player object is properly spawned and prepared, it's time to get it moving. In this section, we'll implement a simple client-authoritative movement script and utilize FishNet's [NetworkTransform](../../fishnet-building-blocks/components/network-transform.md) component to effortlessly synchronize that movement across all connected devices.
#### Client-authoritative movement script
For many games, particularly those with real-time player input, client-authoritative movement is the most straightforward approach. This means the client directly controls its own player object and then informs the server (and other clients) about its position and rotation.
{% hint style="warning" %}
Client-authoritative movement makes it easier for the client to cheat with speed hacks, teleportation, and other movement related hacks, but it is a lot easier to implement and understand.
{% endhint %}
{% stepper %}
{% step %}
### **Creating a player movement script**
Select your **Player** prefab in the Project window. In the Inspector, click on **Add Component** and search for **New Script**. Name the script `PlayerMovement` and click **Create and Add**.
<figure><img src="../../.gitbook/assets/create-player-movement-script.png" alt="An image showing the Player prefab in the Unity Inspector and creating a new script component called &#x22;PlayerMovement&#x22;." width="377"><figcaption><p>Creating the PlayerMovement script</p></figcaption></figure>
{% endstep %}
{% step %}
### **Editing the script**
Double-click the `PlayerMovement` script to open it in your code editor. Replace the default code with the following:
{% tabs fullWidth="false" %}
{% tab title="Old Input System" %}
Unlike typical single-player scripts that use `MonoBehaviour`, this script inherits from `NetworkBehaviour`. This allows for direct access to the `IsOwner` field. Although you could still check ownership via the [NetworkObject](../../fishnet-building-blocks/components/network-object.md) component on a **MonoBehaviour**, [NetworkBehaviour](../../guides/features/networked-gameobjects-and-scripts/network-behaviour-guides.md) provides significant advantages. It enables [RPCs](../../guides/features/network-communication/remote-procedure-calls.md), [SyncVars](../../guides/features/network-communication/synchronizing/), and [specialized override methods](../../guides/features/networked-gameobjects-and-scripts/network-behaviour-guides.md#callbacks) (akin to Unity's `Start`, `Awake`, and `OnDestroy` but for networked objects).
{% code title="PlayerMovement.cs" lineNumbers="true" fullWidth="false" %}
```csharp
using FishNet.Object;
using UnityEngine;
// Inherit from NetworkBehaviour instead of MonoBehaviour
public class PlayerMovement : NetworkBehaviour
{
public float MoveSpeed = 5f;
private void Update()
{
// Only run this code on the object the local client owns.
// This prevents us from moving other players' objects.
if (!IsOwner)
return;
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
Vector3 moveDirection = new Vector3(horizontal, 0f, vertical);
if (moveDirection.magnitude > 1f)
moveDirection.Normalize();
transform.position += MoveSpeed * Time.deltaTime * moveDirection;
}
}
```
{% endcode %}
Since there will be multiple player game objects in the game, we need to determine which one is "our" local player's one and only move that with our input. The `IsOwner` guard clause handles this.
{% endtab %}
{% tab title="New Input System" %}
If using [Unity's New Input System](https://learn.unity.com/tutorial/6712ae08edbc2a05a71b5446?version=6.0), you should add the **Player Input** component to your **Player Prefab** and be sure to **disable** it
<figure><img src="../../.gitbook/assets/player-input-component-disabled.png" alt=""><figcaption><p>The PlayerInput component added to the Player Prefab and Disabled</p></figcaption></figure>
Unlike typical single-player scripts that use `MonoBehaviour`, this script inherits from `NetworkBehaviour`. This allows for direct access to the `IsOwner` field. Although you could still check ownership via the [NetworkObject](../../fishnet-building-blocks/components/network-object.md) component on a **MonoBehaviour**, [NetworkBehaviour](../../guides/features/networked-gameobjects-and-scripts/network-behaviour-guides.md) provides significant advantages. It enables [RPCs](../../guides/features/network-communication/remote-procedure-calls.md), [SyncVars](../../guides/features/network-communication/synchronizing/), and [specialized override methods](../../guides/features/networked-gameobjects-and-scripts/network-behaviour-guides.md#callbacks) (akin to Unity's `Start`, `Awake`, and `OnDestroy` but for networked objects).
{% code title="PlayerMovement.cs" lineNumbers="true" %}
```csharp
using FishNet.Object;
using UnityEngine;
using UnityEngine.InputSystem;
// Inherit from NetworkBehaviour instead of MonoBehaviour
public class PlayerMovement : NetworkBehaviour
{
public float MoveSpeed = 5f;
private Vector2 _currentMovementInput;
public override void OnStartClient()
{
if (IsOwner)
GetComponent<PlayerInput>().enabled = true;
}
public void OnMove(InputValue value)
{
_currentMovementInput = value.Get<Vector2>();
}
public void Update()
{
// Only run this code on the object the local client owns.
// This prevents us from moving other players' objects.
if (!IsOwner)
return;
Vector3 moveDirection = new Vector3(_currentMovementInput.x, 0f, _currentMovementInput.y);
if (moveDirection.magnitude > 1f)
moveDirection.Normalize();
transform.position += MoveSpeed * Time.deltaTime * moveDirection;
}
}
```
{% endcode %}
Since there will be multiple player game objects in the game, we need to determine which one is "our" local player's one and only move that with our input. The `IsOwner` guard clause in `Update` handles this. We are also using the NetworkBehaviour `OnStartClient` callback to enable the **PlayerInput** component when `IsOwner` is true. This method runs when the network object is initialized on the network and only on the client side. It's similar to Unity's `Start` callback and is used here to only send the input to our local player object.
You might wonder why we can’t just use `Awake` or `Start` here, but the reason is that FishNet doesn’t always have enough time to link the object on the network and assign its owner before those methods execute.
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### **Synchronizing the movement**
Now we have code that will allow clients to move only their respective player object, but nothing is yet synchronized over the network This means that we won't ever see other players moving, and they won't see us moving.
To fix this, we will use Fish-Networking's built-in [NetworkTransform](../../fishnet-building-blocks/components/network-transform.md) component. This component can be used to synchronize a network object's scale, rotation, position, and parent hierarchy.
Open your **Player Prefab** and add the **NetworkTransform** component to it.
<figure><img src="../../.gitbook/assets/network-transform-component.png" alt="A picture of the NetworkTransform component with its default settings."><figcaption><p>The default settings of the <strong>NetworkTransform.</strong></p></figcaption></figure>
This component has quite a few settings, but the most important ones for us right now are the **Client Authoritative** and the **Synchronize Position** fields. Both of these should be enabled, which will mean positional changes made by the owner client will automatically be synchronized to the server and then to all other clients.
{% endstep %}
{% step %}
### **Testing the player movement**
Save your scene and press the **Play** button in Unity's Editor. Your player capsule should now be visible. Use the **W, A, S, D** keys or arrow keys to move your player around the scene.
To test multi-player movement, you can build and run the game (or use [multiple editor windows](../simple/testing-with-multiple-editors.md)), which will automatically connect as a client to the editor. You should then be able to control your player in the editor, and the second instance will control its own player, seeing both players move independently.
<figure><img src="../../.gitbook/assets/player-movement-gif.gif" alt=""><figcaption></figcaption></figure>
{% endstep %}
{% endstepper %}
{% hint style="info" %}
Download the project files with these completed steps here, or explore the repository:
<a href="https://github.com/maxkratt/fish-networking-getting-started/releases/download/moving-your-player-around-complete/moving-your-player-around.unitypackage" class="button primary" data-icon="down-to-line">Source Files</a> <a href="https://github.com/maxkratt/fish-networking-getting-started/tree/moving-your-player-around" class="button secondary" data-icon="github">Repository</a>
{% endhint %}
@@ -0,0 +1,78 @@
---
description: Spawn an object over the network to represent each client's player.
---
# Preparing Your Player
For many games you will want to have a player object for each client. This guide will walk you through the process step by step.
{% hint style="success" %}
Before You Begin\
Ensure that you're **not in Play Mode** in the Unity Editor. Making changes during Play Mode will not persist after exiting.
{% endhint %}
{% stepper %}
{% step %}
### Create a player game object
In the Unity Scene Hierarchy, create a new 3D object of type **Capsule**.\
Select the capsule in the Hierarchy and rename it to **Player**.\
In the **Inspector**, click on **Add Component** and search for **NetworkObject**.\
Add the **NetworkObject** component to the capsule.
{% hint style="info" %}
The **NetworkObject** component is required for linking a game object over the network. It enables the object to synchronize its state across clients and the server.
{% endhint %}
<figure><img src="../../.gitbook/assets/add-networkobject-to-player.png" alt="NetworkObject component added to capsule"><figcaption><p>NetworkObject component added to capsule</p></figcaption></figure>
{% endstep %}
{% step %}
### Setup the player object for spawning
Drag the player game object into the project window to turn it into a prefab, then delete the game object from the scene.
<figure><img src="../../.gitbook/assets/create-player-prefab.png" alt="Turn the Player Object into a Prefab"><figcaption><p>Turn the player object into a prefab</p></figcaption></figure>
Select the NetworkManager in the scene and assign your new Player prefab into the **Player Prefab** field on the **PlayerSpawner** component.
<figure><img src="../../.gitbook/assets/assign-playerprefab-in-spawner.png" alt="Assign Player Prefab in Player Spawner"><figcaption><p>Assign player prefab in PlayerSpawner</p></figcaption></figure>
{% endstep %}
{% step %}
### Add spawn points
Create two empty Game Objects in the scene to act as spawn points. Position them where you'd like players to spawn on the map.\
Add these Game Objects as **Transform** references to the **PlayerSpawner** component in the **Spawns** list field.
{% hint style="info" %}
The **PlayerSpawner** uses these transforms to determine spawn locations, choosing them in order from top to bottom for each subsequent player and beginning again after reaching then final spawn point. If no spawn points are specified, the spawner defaults to using the prefab's transform properties for placement.
{% endhint %}
<figure><img src="../../.gitbook/assets/add-spawn-points.png" alt="Create and Assign Spawn Points"><figcaption><p>Create and assign spawn points</p></figcaption></figure>
{% endstep %}
{% step %}
### Test the player spawning
Save the scene and press the **Play** button in the Unity Editor.\
As soon as the server and client starts you should see the a capsule spawning as the player object at the location of the first spawn point.\
To see another player spawn you can build and run the game and which will then automatically connect as a client to the editor. You should then see a second player capsule spawn for the second player at the second spawn point.
<figure><img src="../../.gitbook/assets/build-connected-to-editor.png" alt="Built Game Connected to Editor"><figcaption><p>Built game connected to editor</p></figcaption></figure>
Alternatively, you can launch a second Unity Editor instance by using [Unity's Multiplayer Play Mode](https://docs-multiplayer.unity3d.com/mppm/current/about/) package or a third party package such as [ParrelSync](https://github.com/VeriorPies/ParrelSync?tab=readme-ov-file#parrelsync). Find out more about these options and how to use them with this tutorial: [testing-with-multiple-editors.md](../simple/testing-with-multiple-editors.md "mention").
<figure><img src="../../.gitbook/assets/mppm-connected-to-editor.png" alt="Multiplayer Play Mode Connected to Editor"><figcaption><p>Multiplayer Play Mode connected to editor</p></figcaption></figure>
{% hint style="info" %}
If a second game window shows _"Server failed to start"_ it just means multiple instances tried to use the same port. Only the first becomes the server; the rest run as clients, so the error is safe to ignore during local testing. If players don’t spawn correctly, check that **Error Pause** is disabled in the Unity console.
{% endhint %}
{% endstep %}
{% endstepper %}
{% hint style="info" %}
Download the project files with these completed steps here, or explore the repository:
<a href="https://github.com/maxkratt/fish-networking-getting-started/releases/download/preparing-your-player-complete/preparing-your-player.unitypackage" class="button primary" data-icon="down-to-line">Source Files</a> <a href="https://github.com/maxkratt/fish-networking-getting-started/tree/preparing-your-player" class="button secondary" data-icon="github">Repository</a>
{% endhint %}
@@ -0,0 +1,15 @@
---
description: How to setup a player camera for your multiplayer setup.
---
# Setting Up a Camera
When it comes to multiplayer, a player's camera setup can seem quite tricky to understand. In reality it's often quite simple, only one[^1] camera should exist for your local player.&#x20;
The most reliable method is to create a camera prefab and instantiate it locally when the player's character spawns. Alternatively, you can place a camera in the scene ahead of time and have the player’s object "claim" it upon spawning.
Things get a bit more nuanced when working with [Cinemachine](https://unity.com/features/cinemachine). We'll briefly explore how to manage this setup effectively.
<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="files"></th></tr></thead><tbody><tr><td>Instantiating a Local Camera</td><td><a href="instantiating-a-local-camera.md">instantiating-a-local-camera.md</a></td><td><a href="../../../.gitbook/assets/instantiated-camera-card.png">instantiated-camera-card.png</a></td></tr><tr><td>Using the Scene Camera</td><td><a href="using-the-scene-camera.md">using-the-scene-camera.md</a></td><td><a href="../../../.gitbook/assets/scene-camera-card.png">scene-camera-card.png</a></td></tr><tr><td>Basic Setup with Cinemachine</td><td><a href="basic-setup-with-cinemachine.md">basic-setup-with-cinemachine.md</a></td><td><a href="../../../.gitbook/assets/cinemachine-camera-card.png">cinemachine-camera-card.png</a></td></tr></tbody></table>
[^1]: This is excluding cameras used for minimaps, rear view mirrors, etc.
@@ -0,0 +1,92 @@
---
description: Managing a Cinemachine Camera in multiplayer.
---
# Basic Setup with Cinemachine
This guide will show you one way you can set-up your player camera for multiplayer when you are using the Cinemachine package.
{% stepper %}
{% step %}
### **Installing Cinemachine**
The first step — if you haven't done so already — is to install [Cinemachine](https://unity.com/features/cinemachine) through the [Unity Package Manager](https://learn.unity.com/tutorial/the-package-manager).
<figure><img src="../../../.gitbook/assets/installing-cinemachine.png" alt=""><figcaption><p>Cinemachine installing</p></figcaption></figure>
{% endstep %}
{% step %}
### **Adding the Cinemachine Brain**
Now that we have **Cinemachine** installed we can set it up. Add the **Cinemachine Brain** component to the **Main Camera** game object in the scene.
<figure><img src="../../../.gitbook/assets/added-cinemachine-brain.png" alt=""><figcaption><p><strong>Cinemachine Brain</strong> added to <strong>Main Camera</strong></p></figcaption></figure>
{% endstep %}
{% step %}
### **Giving the player a camera holder**
Now create an empty Game Object on your **Player Prefab** and position it where you'd like. This will be our container for the **Cinemachine** **Camera**.
<figure><img src="../../../.gitbook/assets/camera-holder-setup.png" alt=""><figcaption><p>Camera Holder created on the Player Prefab</p></figcaption></figure>
{% endstep %}
{% step %}
### **Adding the Cinemachine Camera**
Now simply add the **Cinemachine Camera** component to the newly created **CameraHolder** game object.
<figure><img src="../../../.gitbook/assets/added-cinemachine-camera.png" alt=""><figcaption><p>The <strong>Cinemachine Camera</strong> added to the <strong>Camera Holder</strong></p></figcaption></figure>
{% endstep %}
{% step %}
### **Writing a PlayerCamera script**
Let's now add the following `PlayerCamera` script to the **Player Prefab** that we will use to take control of our **Camera** once our player spawns in.
{% code title="PlayerCamera.cs" lineNumbers="true" %}
```csharp
using FishNet.Object;
using Unity.Cinemachine;
using UnityEngine;
// This script will be a NetworkBehaviour so that we can use the OnStartClient override.
public class PlayerCamera : NetworkBehaviour
{
[SerializeField] private CinemachineCamera _cinemachineCamera;
// This method is called on the client after the object is spawned in.
public override void OnStartClient()
{
// Simply enable our local cinemachine camera on the object if we are the owner.
_cinemachineCamera.enabled = IsOwner;
}
}
```
{% endcode %}
This script uses the [OnStartClient](../../../guides/features/networked-gameobjects-and-scripts/network-behaviour-guides.md#onstartclient) override method from [NetworkBehaviour](../../../guides/features/networked-gameobjects-and-scripts/network-behaviour-guides.md) to enable or disable the **Cinemachine Camera** component on the player objects depending on if they are our local player or not.
{% endstep %}
{% step %}
### **Assign your references to the script**
Now select the **Player Camera** component in your **Player Prefab** and add drag the **Camera Holder** game object into the _Cinemachine Camera_ field to assign it the component.
<figure><img src="../../../.gitbook/assets/assigned-cinemachine-camera-to-player.png" alt=""><figcaption><p>The <strong>Player</strong> prefab with the <strong>Cinemachine Camera</strong> field filled in</p></figcaption></figure>
{% endstep %}
{% step %}
### **Test the camera in-game**
With all that set you should be able to run the game and see how the camera from the scene is controlled by only your local player.
<figure><img src="../../../.gitbook/assets/player-camera-demonstration.gif" alt=""><figcaption><p>Demonstration of the local camera</p></figcaption></figure>
{% endstep %}
{% endstepper %}
{% hint style="info" %}
Download the project files with these completed steps here, or explore the repository:
<a href="https://github.com/maxkratt/fish-networking-getting-started/releases/download/basic-setup-with-cinemachine/basic-setup-with-cinemachine.unitypackage" class="button primary" data-icon="down-to-line">Source Files</a> <a href="https://github.com/maxkratt/fish-networking-getting-started/tree/cinemachine-camera" class="button secondary" data-icon="github">Repository</a>
{% endhint %}
@@ -0,0 +1,77 @@
---
description: Using a Camera Prefab to instantiate your local player's camera.
---
# Instantiating a Local Camera
With this method of camera management we will instantiate a camera from a prefab we create and assign it to our local player object when it spawns in.
{% stepper %}
{% step %}
### **Creating a camera prefab**
Let's start by dragging the **Main Camera** in the scene into our **Project Window** to create a prefab out of it. You can now delete the **Main Camera** from the **Scene Hierarchy**.
{% endstep %}
{% step %}
### **Giving the player a camera holder**
Now create an empty Game Object on your **Player Prefab** and position it where you'd like. This will be where we instantiate and position the **Camera** object.
<figure><img src="../../../.gitbook/assets/camera-holder-setup.png" alt=""><figcaption><p>Camera Holder created on the player prefab</p></figcaption></figure>
{% endstep %}
{% step %}
### **Writing a PlayerCamera script**
Let's now add the following `PlayerCamera` script to the **Player Prefab** that we will use to instantiate our **Camera Prefab** once our player spawns in.
{% code title="PlayerCamera.cs" lineNumbers="true" %}
```csharp
using FishNet.Object;
using UnityEngine;
// This script will be a NetworkBehaviour so that we can use the
// OnStartClient override.
public class PlayerCamera : NetworkBehaviour
{
[SerializeField] private Camera _cameraPrefab;
[SerializeField] private Transform _cameraHolder;
// This method will run on the client once this object is spawned.
public override void OnStartClient()
{
// Since this will run on all clients that this object spawns for
// we need to only instantiate the camera for the object we own.
if (IsOwner)
Instantiate(_cameraPrefab, _cameraHolder.position, _cameraHolder.rotation, _cameraHolder);
}
}
```
{% endcode %}
This script uses the [OnStartClient](../../../guides/features/networked-gameobjects-and-scripts/network-behaviour-guides.md#onstartclient) override method from [NetworkBehaviour](../../../guides/features/networked-gameobjects-and-scripts/network-behaviour-guides.md) to instantiate the camera prefab for our local player.
{% endstep %}
{% step %}
### **Assign your references to the script**
Now select the **Player Camera** component in your **Player Prefab** and add the **Camera Prefab** we made to the _Camera Prefab_ field. Also select the **CameraHolder** game object in the _Camera Holder_ field.
<figure><img src="../../../.gitbook/assets/assigned-player-camera-to-player.png" alt=""><figcaption><p>The player prefab with the Main Camera filled in</p></figcaption></figure>
{% endstep %}
{% step %}
### **Test the camera in-game**
With all that set you should be able to run the game and see how the camera is created for only your local player.
<figure><img src="../../../.gitbook/assets/player-camera-demonstration.gif" alt=""><figcaption><p>Demonstration of the local camera</p></figcaption></figure>
{% endstep %}
{% endstepper %}
{% hint style="info" %}
Download the project files with these completed steps here, or explore the repository:
<a href="https://github.com/maxkratt/fish-networking-getting-started/releases/download/instantiating-a-local-camera/instantiating-a-local-camera.unitypackage" class="button primary" data-icon="down-to-line">Source Files</a> <a href="https://github.com/maxkratt/fish-networking-getting-started/tree/local-camera" class="button secondary" data-icon="github">Repository</a>
{% endhint %}
@@ -0,0 +1,87 @@
---
description: Using the camera in the scene for your local player.
---
# Using the Scene Camera
With this method our local player will take control of the Main Camera it finds in the scene.
{% stepper %}
{% step %}
### **The main camera tag**
Before we start we need to ensure our camera object in the scene is tagged as the "Main Camera"
<figure><img src="../../../.gitbook/assets/main-camera-in-scene.png" alt=""><figcaption><p>Main Camera in scene with the tag</p></figcaption></figure>
{% endstep %}
{% step %}
### **Giving the player a camera holder**
Now create an empty Game Object on your **Player Prefab** and position it where you'd like. This will be where we parent and position the **Camera** object.
<figure><img src="../../../.gitbook/assets/camera-holder-setup.png" alt=""><figcaption><p>Camera Holder created on the Player Prefab</p></figcaption></figure>
{% endstep %}
{% step %}
### **Writing a PlayerCamera script**
Let's now add the following `PlayerCamera` script to the **Player Prefab** that we will use to take control of our **Camera** once our player spawns in.
{% code title="PlayerCamera.cs" lineNumbers="true" %}
```csharp
using FishNet.Connection;
using FishNet.Object;
using UnityEngine;
// This script will be a NetworkBehaviour so that we can use the
// OnOwnershipClient override.
public class PlayerCamera : NetworkBehaviour
{
[SerializeField] private Transform _cameraHolder;
// This method is called on the client after gaining or losing ownership of the object.
// We could have used OnStartClient instead, as we did in the previous example, but using OnOwnershipClient
// means this will work for a player object we don't initially own but are given ownership to later.
public override void OnOwnershipClient(NetworkConnection prevOwner)
{
if (Camera.main == null)
return;
// If we are the new owner of this object, then take control of the camera by parenting it
// and moving it to our camera holder.
if (IsOwner)
{
Camera.main.transform.SetPositionAndRotation(_cameraHolder.position, _cameraHolder.rotation);
Camera.main.transform.SetParent(_cameraHolder);
}
}
}
```
{% endcode %}
This script uses the [OnOwnershipClient](../../../guides/features/networked-gameobjects-and-scripts/network-behaviour-guides.md#onownershipclient) override method from [NetworkBehaviour](../../../guides/features/networked-gameobjects-and-scripts/network-behaviour-guides.md) to instantiate the camera prefab for our local player as soon as he gets ownership of the object. We could just have easily used the [OnStartClient](../../../guides/features/networked-gameobjects-and-scripts/network-behaviour-guides.md#onstartclient) override method like the [instantiating-a-local-camera.md](instantiating-a-local-camera.md "mention") example shows.
{% endstep %}
{% step %}
### **Assign your references to the script**
Now select the **Player Camera** component in your **Player Prefab** and add the **Camera Prefab** we made to the _Camera Prefab_ field. Also select the **CameraHolder** game object in the _Camera Holder_ field.
<figure><img src="../../../.gitbook/assets/assigned-camera-holder-to-player.png" alt=""><figcaption><p>The Player prefab with the <strong>Camera Holder</strong> filled in</p></figcaption></figure>
{% endstep %}
{% step %}
### **Test the camera in-game**
With all that set you should be able to run the game and see how the camera from the scene is controlled by only your local player.
<figure><img src="../../../.gitbook/assets/player-camera-demonstration.gif" alt=""><figcaption><p>Demonstration of the local camera</p></figcaption></figure>
{% endstep %}
{% endstepper %}
{% hint style="info" %}
Download the project files with these completed steps here, or explore the repository:
<a href="https://github.com/maxkratt/fish-networking-getting-started/releases/download/using-the-scene-camera/using-the-scene-camera.unitypackage" class="button primary" data-icon="down-to-line">Source Files</a> <a href="https://github.com/maxkratt/fish-networking-getting-started/tree/scene-camera" class="button secondary" data-icon="github">Repository</a>
{% endhint %}
@@ -0,0 +1,217 @@
---
description: Learn about spawning and despawning by having your players throw cubes around!
---
# Spawning and Despawning Items
Now that we have players moving around, let's learn how to _spawn_ and _despawn_ [NetworkObjects](../../guides/features/networked-gameobjects-and-scripts/networkobjects/).
While you can [Instantiate](https://docs.unity3d.com/ScriptReference/Object.Instantiate.html) and [Destroy](https://docs.unity3d.com/ScriptReference/Object.Destroy.html) regular Game Objects, Network Objects need to be Spawned and Despawned, this tells FishNet to synchronize the action over the network. FishNet stores all network object prefabs in a collection called the [Spawnable Prefabs](../../fishnet-building-blocks/scriptableobjects/spawnableprefabs/). You can see this and customize it on the [NetworkManager](../../fishnet-building-blocks/components/managers/network-manager.md).
{% hint style="success" %}
We have added a large green cube to act as the floor which you can do too, but this isn't a vital step.
{% endhint %}
{% stepper %}
{% step %}
### **Creating the item network object prefab**
Let's create a **Cube** in the scene hierarchy, and add a **NetworkObject** component to it. We've also decreased it's scale by half so that it doesn't look too large.\
Then drag it into the project window to turn it into a prefab; you can destroy the original game object in the scene hierarchy now.
<figure><img src="../../.gitbook/assets/added-network-object-to-cube.png" alt="An image of a Cube Game Object set to half scale and with a NetworkObject component added."><figcaption><p>The Cube with size adjusted and NetworkObject added</p></figcaption></figure>
{% endstep %}
{% step %}
### **Creating a script to spawn the cube**
Now create a script called `PlayerCubeCreator` and add it to your **Player Prefab**. This script is going to be responsible for spawning our new cube item across the network.
{% tabs %}
{% tab title="Old Input System" %}
{% code title="PlayerCubeCreator.cs" lineNumbers="true" %}
```csharp
using FishNet.Object;
using UnityEngine;
public class PlayerCubeCreator : NetworkBehaviour
{
public NetworkObject CubePrefab;
private void Update()
{
// Only the local player object should perform these actions.
if (!IsOwner)
return;
if (Input.GetButtonDown("Fire1"))
SpawnCube();
}
// We are using a ServerRpc here because the Server needs to do all network object spawning.
[ServerRpc]
private void SpawnCube()
{
NetworkObject obj = Instantiate(CubePrefab, transform.position, Quaternion.identity);
Spawn(obj); // NetworkBehaviour shortcut for ServerManager.Spawn(obj);
}
}
```
{% endcode %}
{% endtab %}
{% tab title="New Input System" %}
{% code title="PlayerCubeCreator.cs" lineNumbers="true" %}
```csharp
using FishNet.Object;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerCubeCreator : NetworkBehaviour
{
public NetworkObject CubePrefab;
public override void OnStartClient()
{
if (IsOwner)
GetComponent<PlayerInput>().enabled = true;
}
public void OnAttack(InputValue value)
{
if (value.isPressed)
SpawnCube();
}
// We are using a ServerRpc here because the Server needs to do all network object spawning.
[ServerRpc]
private void SpawnCube()
{
NetworkObject obj = Instantiate(CubePrefab, transform.position, Quaternion.identity);
Spawn(obj); // NetworkBehaviour shortcut for ServerManager.Spawn(obj);
}
}
```
{% endcode %}
{% endtab %}
{% endtabs %}
{% hint style="info" %}
This may be a great time to read [this page](../../guides/features/network-communication/remote-procedure-calls.md) about RPCs[^1] such as the [ServerRpc](../../guides/features/network-communication/remote-procedure-calls.md#serverrpc) we use here.
{% endhint %}
Because this script contains a ServerRpc, it will also need to be a **NetworkBehaviour**. The `Update` method listens for the user to press the <kbd>Fire1</kbd>/<kbd>Attack</kbd> button (typically the left mouse button) and then calls `SpawnCube`.
**Understanding `SpawnCube`:**
* The **ServerRpc** attribute: This is crucial. It means this method is intended to be called by a client, but it will _execute only on the server_. The client sends a message to the server, requesting this method to be run.
* `Spawn(obj);`: This is the most critical line for networked objects. Calling `Spawn(obj)` on the `NetworkObject` tells FishNet's `ServerManager` to "spawn" this object over the network. This means the server will now instruct all currently connected clients (and any clients that connect later) to instantiate their own replica of this object.
{% hint style="info" %}
You can read our more in-depth page about **Spawning** [here](../../guides/features/networked-gameobjects-and-scripts/spawning/).
{% endhint %}
{% endstep %}
{% step %}
### **Assigning the prefab to your script**
Now select your **Player Prefab** and assign your **Cube Prefab** to the "Cube Prefab" field in your newly created **Player Cube Creator** script.
<figure><img src="../../.gitbook/assets/player-cube-creator-set.png" alt=""><figcaption><p>PlayerCubeCreator with cube prefab assigned</p></figcaption></figure>
{% hint style="info" %}
You may have noticed that an **Empty Network Behaviour** component was automatically added to your game objects with a **NetworkObject** component. This happens because each FishNet requires every NetworkObject to also have a NetworkBehaviour on it, and it will automatically add an empty one if it doesn't detect any. You can safely remove this empty component as you have added multiple other **NetworkBehaviour** components, but it's not a problem either way.
{% endhint %}
{% endstep %}
{% step %}
### **Test if the spawning works**
Now launch a couple instances of your game and see if you can run around and spawn cube items by pressing the <kbd>Fire1</kbd>/<kbd>Attack</kbd> button. The cubes should be visible on all devices connected to each other.
<figure><img src="../../.gitbook/assets/test-cube-spawning.gif" alt=""><figcaption><p>Player spawning cubes</p></figcaption></figure>
{% endstep %}
{% step %}
### **Add some physics**
Static cubes are boring! Let's add a **Rigidbody** component to the **Cube Prefab** to enable basic physics interactions with them.
<figure><img src="../../.gitbook/assets/rigidbody-added-to-cube.png" alt=""><figcaption><p>Rigidbody added to the cube</p></figcaption></figure>
Now the cubes should roll around and collide with the ground. Let's add a **Rigidbody** to the **Player** now and set it to **Is Kinematic**.
<figure><img src="../../.gitbook/assets/player-with-rigidbody.png" alt=""><figcaption><p>Player with kinematic rigidbody added</p></figcaption></figure>
{% endstep %}
{% step %}
### **Test if the physics works**
If you launch the game now, the cubes should have basics physics working.
<figure><img src="../../.gitbook/assets/players-spawning-physics-cubes.gif" alt=""><figcaption><p>Players spawning physics cubes</p></figcaption></figure>
You may notice the cubes' positions don't always sync up after they are moved around, that's because we haven't done any positional syncing besides the initial position syncing that FishNet automatically did when we called `Spawn`.
{% hint style="info" %}
Networked Rigidbodies are a complex topic, and better suited to a different guide, but if you want to test a really basic setup up, you can add a [NetworkTransform](../../fishnet-building-blocks/components/network-transform.md) to your **Cube Prefab** and set its **Component Configuration** to **Rigidbody.**
{% endhint %}
{% endstep %}
{% step %}
### **Script to despawn cubes on a timer**
Now that we can `Spawn` cubes successfully, let's `Despawn` them after a few seconds so that we don't end up with too many objects at once.
Create a new script called `DespawnAfterTime`:
{% code title="DespawnAfterTime.cs" lineNumbers="true" %}
```csharp
using FishNet.Object;
using System.Collections;
using UnityEngine;
public class DespawnAfterTime : NetworkBehaviour
{
public float SecondsBeforeDespawn = 3f;
public override void OnStartServer()
{
StartCoroutine(DespawnAfterSeconds());
}
private IEnumerator DespawnAfterSeconds()
{
yield return new WaitForSeconds(SecondsBeforeDespawn);
Despawn(); // NetworkBehaviour shortcut for ServerManager.Despawn(gameObject);
}
}
```
{% endcode %}
This script uses the [OnStartServer](../../guides/features/networked-gameobjects-and-scripts/network-behaviour-guides.md#onstartserver) NetworkBehaviour override method to start a Coroutine which will `Despawn` the object. OnStartServer will run on the server when the object is initialized with the network.
`Despawn` needs to be called on the server, and it will destroy the game object locally as well as on all clients automatically for you.
{% endstep %}
{% step %}
### **Assigning the script and testing**
Open your **Cube Prefab** now and add the **Despawn After Time** script you just made to the object.
Launch the game again and observe the objects being Despawned successfully on the server and all clients after a few seconds.
<figure><img src="../../.gitbook/assets/cubes-despawning.gif" alt=""><figcaption><p>Cubes despawning after 3 seconds</p></figcaption></figure>
{% endstep %}
{% endstepper %}
{% hint style="info" %}
Download the project files with these completed steps here, or explore the repository:
<a href="https://github.com/maxkratt/fish-networking-getting-started/releases/download/spawning-and-despawning-complete/spawning-and-despawning.unitypackage" class="button primary" data-icon="down-to-line">Source Files</a> <a href="https://github.com/maxkratt/fish-networking-getting-started/tree/spawning-and-despawning-items" class="button secondary" data-icon="github">Repository</a>
{% endhint %}
[^1]: Remote Procedure Calls
@@ -0,0 +1,160 @@
---
description: Synchronizing color with synchronized variables!
---
# Using SyncVars to Sync Colors
We've got quite a few things synchronized now, but how might you go about synchronizing a specific variable in one of your scripts? SyncVars[^1] are one answer! A [SyncVar](../../guides/features/network-communication/synchronizing/syncvar.md) is a FishNet generic type that can be used within [NetworkBehaviours](../../guides/features/networked-gameobjects-and-scripts/network-behaviour-guides.md) that automatically synchronize their value from the server to all clients.
Let's liven up the colors in our game and synchronize them with SyncVars.
{% stepper %}
{% step %}
### **Creating a script to sync color**
Create a new script called `SyncMaterialColor`. This will be used to synchronize the **Material** color of our **MeshRenderers**.
{% code title="SyncMaterialColor.cs" lineNumbers="true" %}
```csharp
using FishNet.Object;
using FishNet.Object.Synchronizing;
using UnityEngine;
[RequireComponent(typeof(MeshRenderer))]
public class SyncMaterialColor : NetworkBehaviour
{
public readonly SyncVar<Color> Color = new SyncVar<Color>();
private void Awake()
{
Color.OnChange += OnColorChanged;
}
private void OnColorChanged(Color previous, Color next, bool asServer)
{
GetComponent<MeshRenderer>().material.color = Color.Value;
}
}
```
{% endcode %}
This simple script has a `Color` variable of the type `SyncVar<Color>`. It needs be set to `readonly`, but don't worry, we can still get and set its `Value`.
{% hint style="info" %}
Setting your **SyncVar** as `readonly` will cause it to be hidden from the Unity Inspector. You can read about a work-around for this [here](../../guides/features/network-communication/synchronizing/customizing-behavior.md).
{% endhint %}
In `Awake` we subscribe to the SyncVar's `OnChange` event. This event is very useful as it will be invoked as soon as the SyncVar changes, even on a client when the server changes it and it's synced.
`OnColorChanged` is our method that we subscribed to the OnChange event. This method simply gets the MeshRenderer and sets its material color to the SyncVar's Color Value, thus updating the visuals to match on all devices.
{% hint style="success" %}
You may want to read more about **SyncVars** and the other available **SyncTypes** that FishNet has, such as **SyncLists.** You can find those pages [here](../../guides/features/network-communication/synchronizing/).
{% endhint %}
{% endstep %}
{% step %}
### **Add the script component**
Now add your newly created script to your **Cube Prefab**. The script won't currently do anything unless we change the `Color` **SyncVar** in it, so let's do that next.
<figure><img src="../../.gitbook/assets/cube-with-sync-color.png" alt=""><figcaption><p>The Sync Material Color component added</p></figcaption></figure>
{% endstep %}
{% step %}
### **Give the cubes random colors**
Let's give the cubes some color now as soon as we instantiate them.
Reopen the `PlayerCubeCreator.cs` script and make the following addition after the call to `Instantiate` and before the call to `Spawn`:
```csharp
obj.GetComponent<SyncMaterialColor>().Color.Value = Random.ColorHSV();
```
Here is the complete script with the change made:
{% tabs %}
{% tab title="Old Input System" %}
<pre class="language-csharp" data-title="PlayerCubeCreator.cs" data-line-numbers><code class="lang-csharp">using FishNet.Object;
using UnityEngine;
public class PlayerCubeCreator : NetworkBehaviour
{
public NetworkObject CubePrefab;
void Update()
{
// Only the local player object should perform these actions.
if (!IsOwner)
return;
if (Input.GetButtonDown("Fire1"))
SpawnCube();
}
// We are using a ServerRpc here because the Server needs to do all network object spawning.
[ServerRpc]
private void SpawnCube()
{
NetworkObject obj = Instantiate(CubePrefab, transform.position, Quaternion.identity);
<strong> obj.GetComponent&#x3C;SyncMaterialColor>().Color.Value = Random.ColorHSV();
</strong> Spawn(obj); // NetworkBehaviour shortcut for ServerManager.Spawn(obj);
}
}
</code></pre>
{% endtab %}
{% tab title="New Input System" %}
<pre class="language-csharp" data-title="PlayerCubeCreator.cs" data-line-numbers><code class="lang-csharp">using FishNet.Object;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerCubeCreator : NetworkBehaviour
{
public NetworkObject CubePrefab;
public override void OnStartClient()
{
if (IsOwner)
GetComponent&#x3C;PlayerInput>().enabled = true;
}
public void OnAttack(InputValue value)
{
if (value.isPressed)
SpawnCube();
}
// We are using a ServerRpc here because the Server needs to do all network object spawning.
[ServerRpc]
private void SpawnCube()
{
NetworkObject obj = Instantiate(CubePrefab, transform.position, Quaternion.identity);
<strong> obj.GetComponent&#x3C;SyncMaterialColor>().Color.Value = Random.ColorHSV();
</strong> Spawn(obj); // NetworkBehaviour shortcut for ServerManager.Spawn(obj);
}
}
</code></pre>
{% endtab %}
{% endtabs %}
This line of code gets the **SyncMaterialColor** component and sets the SyncVar Value to a random color as soon as it's instantiated. Once Spawn is called on the next line, FishNet will spawn this object for all clients with the color SyncVar state synced.
{% endstep %}
{% step %}
### **Test the synchronized cubes**
Now all you need to do is run your game again and see if the cubes spawn with a random color and if that color is synchronized across the network.
<figure><img src="../../.gitbook/assets/synced-cube-colors.gif" alt=""><figcaption><p>Cube color synchronized!</p></figcaption></figure>
{% endstep %}
{% endstepper %}
{% hint style="info" %}
Download the project files with these completed steps here, or explore the repository:
<a href="https://github.com/maxkratt/fish-networking-getting-started/releases/download/using-syncvars-to-sync-colors/using-syncvars-to-sync-colors.unitypackage" class="button primary" data-icon="down-to-line">Source Files</a> <a href="https://github.com/maxkratt/fish-networking-getting-started/tree/syncing-colors" class="button secondary" data-icon="github">Repository</a>
{% endhint %}
[^1]: synchronized variables