提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
Netcode for GameObjects 是 Unity 官方推出的一套用于开发多人网络游戏的框架,旨在简化基于 Unity 引擎的多人游戏开发流程。它允许开发者轻松地在多个客户端之间同步游戏对象(GameObject)的状态、处理网络通信以及管理玩家连接等核心功能,尤其适合中小型多人游戏项目。
链接: 官方地址
提示:官方unityHub 创建项目时 提供的模板 就有。超级好用。
一、安装 Netcode for GameObjects
打开 Unity 项目(建议使用 Unity 2021.3 或更高版本)。
进入 Package Manager(Window → Package Manager)。
点击左上角 “+” 图标 → Add package by name…
输入包名 com.unity.netcode.gameobjects 并安装(确保勾选 “Enable Preview Packages” 以获取最新版本)。
直接搜索下载也可以。

二、做个小Dome
doem的内容就是:一个客户端随机移动圆球位置,其他客户端会同步显示变化。
1.NetcodeManageNet
创建一个场景,在场景创建一个空物体挂载NetcodeManage组件,并选择UnityTransport,选择后会自动挂载UnityTransport脚本

2.创建UI
创建4个按钮:,一个text和InputField 。并挂载脚本,如下图


3.创建预制体
创建一个圆球,挂载NetwrokObject和NetworkTransform
NetworkTransform 的pocition Rotation Scale全部勾选(多人同步:位置、旋转、大小)

NetworkManage 添加这个预制件,如下图

4.代码介绍
UI代码
using System.Net;
using System.Net.NetworkInformation;
using Unity.Netcode;
using Unity.Netcode.Samples;
using Unity.Netcode.Transports.UTP;
using UnityEngine;
using UnityEngine.UI;
namespace Twq
{
public class UI_Init : MonoBehaviour
{
public Transform NetworkManager_;
public Text text_IP;
public Button HostBtn;
public Button ServerBtn;
public InputField inputField;
public Button ClientBtn;
public GameObject obj01;
public GameObject obj02;
public Button RandomBtn;
private void Awake()
{
text_IP.text = GetIP();
var networkManager = NetworkManager_.GetComponent<NetworkManager>();
UnityTransport unityTransport = networkManager.GetComponent<UnityTransport>();
//服务器地址填写:0.0.0.0 本地可连接,外网也可链接(放在云服务器上才行)
//其他客户端连接时 输入 服务器IP 即可
HostBtn.onClick.AddListener(() => {
unityTransport.SetConnectionData("0.0.0.0", 7777);
networkManager.StartHost();
obj01.SetActive(false);
obj02.SetActive(true);
});
ServerBtn.onClick.AddListener(() => {
unityTransport.SetConnectionData("0.0.0.0", 7777);
networkManager.StartServer();
obj01.SetActive(false);
});
ClientBtn.onClick.AddListener(() => {
if (inputField.text != null && inputField.text != "")
{
unityTransport.SetConnectionData(inputField.text, 7777);
networkManager.StartClient();
obj01.SetActive(false);
obj02.SetActive(true);
}
else
{
Debug.Log("请输入服务器的IP");
}
});
RandomBtn.onClick.AddListener(() => {
if (!networkManager.IsClient)
{
Debug.Log("没有 连上");
}
if (networkManager.LocalClient != null)
{
// Get `BootstrapPlayer` component from the player's `PlayerObject`
if (networkManager.LocalClient.PlayerObject.TryGetComponent(out BootstrapPlayer bootstrapPlayer))
{
// Invoke a `ServerRpc` from client-side to teleport player to a random position on the server-side
bootstrapPlayer.RandomTeleportServerRpc();
}
}
});
}
private string GetIP()
{
string ipv4 = "";
foreach (NetworkInterface item in NetworkInterface.GetAllNetworkInterfaces())
{
NetworkInterfaceType _type1 = NetworkInterfaceType.Wireless80211;
NetworkInterfaceType _type2 = NetworkInterfaceType.Ethernet;
if (item.NetworkInterfaceType == _type1 || item.NetworkInterfaceType == _type2 && item.OperationalStatus == OperationalStatus.Up)
{
foreach (UnicastIPAddressInformation ip in item.GetIPProperties().UnicastAddresses)
{
if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
ipv4 = ip.Address.ToString();
}
}
}
}
return ipv4;
}
}
}
随机位置代码
using UnityEngine;
namespace Unity.Netcode.Samples
{
/// <summary>
/// Component attached to the "Player Prefab" on the `NetworkManager`.
/// </summary>
public class BootstrapPlayer : NetworkBehaviour
{
/// <summary>
/// If this method is invoked on the client instance of this player, it will invoke a `ServerRpc` on the server-side.
/// If this method is invoked on the server instance of this player, it will teleport player to a random position.
/// </summary>
/// <remarks>
/// Since a `NetworkTransform` component is attached to this player, and the authority on that component is set to "Server",
/// this transform's position modification can only be performed on the server, where it will then be replicated down to all clients through `NetworkTransform`.
/// </remarks>
[ServerRpc]
public void RandomTeleportServerRpc()
{
var oldPosition = transform.position;
transform.position = GetRandomPositionOnXYPlane();
var newPosition = transform.position;
print($"{nameof(RandomTeleportServerRpc)}() -> {nameof(OwnerClientId)}: {OwnerClientId} --- {nameof(oldPosition)}: {oldPosition} --- {nameof(newPosition)}: {newPosition}");
}
private static Vector3 GetRandomPositionOnXYPlane()
{
return new Vector3(Random.Range(-3f, 3f), Random.Range(-3f, 3f), 0f);
}
}
}
添加一个服务器和客户端互相通信
using System;
using Twq;
using Unity.Netcode;
using UnityEditor.PackageManager;
using UnityEngine;
using UnityEngine.UI;
public class ConnectionManager : NetworkBehaviour
{
public GameObject Mask;
[HideInInspector]
public static ConnectionManager Singleton_;
private void Start()
{
Singleton_ = this;
DontDestroyOnLoad(this);
}
public override void OnNetworkSpawn()
{
// 注册连接回调(只在首次生成时注册一次)
if (IsServer)
{
// 服务器端监听客户端连接
NetworkManager.Singleton.OnClientConnectedCallback += OnClientConnected;
// 服务器端监听客户端断开连接
NetworkManager.Singleton.OnClientDisconnectCallback += OnClientDisconnected;
}
if (IsClient)
{
// 客户端监听自己是否成功连接到服务器
NetworkManager.Singleton.OnClientConnectedCallback += OnLocalClientConnected;
// 客户端监听与服务器的连接是否断开
NetworkManager.Singleton.OnClientDisconnectCallback += OnLocalClientDisconnected;
}
}
public override void OnNetworkDespawn()
{
// 取消注册回调,避免内存泄漏
if (NetworkManager.Singleton != null)
{
NetworkManager.Singleton.OnClientConnectedCallback -= OnClientConnected;
NetworkManager.Singleton.OnClientDisconnectCallback -= OnClientDisconnected;
NetworkManager.Singleton.OnClientConnectedCallback -= OnLocalClientConnected;
NetworkManager.Singleton.OnClientDisconnectCallback -= OnLocalClientDisconnected;
}
}
// 服务器端:当有客户端连接时调用
private void OnClientConnected(ulong clientId)
{
Debug.Log($"服务器: 客户端 {clientId} 已连接");
// 可以在这里执行:
// - 生成玩家对象
// - 发送欢迎消息
// - 同步初始数据给新连接的客户端
SendToClient(clientId);
}
// 服务器端:当有客户端断开连接时调用
private void OnClientDisconnected(ulong clientId)
{
Debug.Log($"服务器: 客户端 {clientId} 已断开连接");
// 可以在这里执行:
// - 清理玩家数据
// - 通知其他玩家该玩家已离开
// - 保存玩家进度
}
// 客户端:当本地客户端成功连接到服务器时调用
private void OnLocalClientConnected(ulong clientId)
{
// 验证是否是本地客户端的连接事件
if (clientId == NetworkManager.Singleton.LocalClientId)
{
Debug.Log("客户端: 已成功连接到服务器");
Mask.SetActive(false);
UIManager.In_.Mask.SetActive(true);
// 可以在这里执行:
// - 显示连接成功UI
// - 请求初始数据
// - 发送玩家信息到服务器
}
}
// 客户端:当本地客户端与服务器断开连接时调用
private void OnLocalClientDisconnected(ulong clientId)
{
// 验证是否是本地客户端的断开事件
if (clientId == NetworkManager.Singleton.LocalClientId)
{
Debug.Log("客户端: 与服务器断开连接");
// 可以在这里执行:
// - 显示断开连接提示
// - 返回到主菜单
// - 清理本地玩家数据
}
}
public void SendToClient(ulong clientId)
{
SendToSpecificClient( clientId, "------服务器:客户端你好呀!");
}
//服务器调用,在所有客户端上执行
[ClientRpc]
public void ServerToAllClientsMessageClientRpc(string message, ClientRpcParams clientRpcParams = default)
{
Debug.Log($"客户端收到服务器的广播消息: {message}");
}
//服务器向特定客户端发送消息
public void SendToSpecificClient(ulong clientId, string message)
{
if (!IsServer) return; // 确保只有服务器可以调用
var clientRpcParams = new ClientRpcParams
{
Send = new ClientRpcSendParams
{
TargetClientIds = new ulong[] { clientId }
}
};
ServerToSpecificClientMessageClientRpc(message, clientRpcParams);
}
//客户端收到服务器的 私有消息
[ClientRpc]
private void ServerToSpecificClientMessageClientRpc(string message, ClientRpcParams clientRpcParams)
{
Debug.Log($"客户端收到服务器的私有消息: {message}");
}
// 客户端调用此方法,消息会发送到服务器 并在 服务器执行
[ServerRpc(RequireOwnership = false)]//ServerRpc 是客户端向服务器发送请求的特殊方法。默认情况下,ServerRpc 具有 RequireOwnership = true 属性,这意味着:只有拥有该 NetworkObject 所有权的客户端,才能调用这个 ServerRpc。
private void SendMessageToServerRpc(string message, ServerRpcParams serverRpcParams = default)
{
// 此逻辑在服务器端执行
Debug.Log($"服务器收到来自客户端 {serverRpcParams.Receive.SenderClientId} 的消息: {message}");
// 服务器可以在这里处理消息(如广播给所有客户端等)
// ServerToAllClientsMessageClientRpc($"客户端 {serverRpcParams.Receive.SenderClientId} 的消息: {message}");
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="message">创建一个InputField (Legacy)和Button (Legacy) 来调用这个函数</param>
public void SendMessage(string message)
{
if (!string.IsNullOrEmpty(message))
{
if (IsServer)
{
Debug.Log($"[你(服务器):] {message}");
ServerToAllClientsMessageClientRpc($"[服务器] {message}");
}
else
{
Debug.Log($"[你(客户端):] {message}");
SendMessageToServerRpc(message);
}
}
}
//public static UIManager In_;
//public GameObject Mask;
//public InputField messageInput;
//public Button button;
//void Awake()
//{
// In_ = this;
// button.onClick.AddListener(() => {
// ConnectionManager.Singleton_.SendMessage(messageInput.text);
// messageInput.text = "";
// });
//}
}
总结
项目源码: 链接
Unity多人游戏开发简易教程
3064

被折叠的 条评论
为什么被折叠?



