【UNet】初识Unity Network,制作一个简单的网络聊天室

UNet官方文档:https://docs.unity3d.com/Manual/UNet.html

1.创建Offline场景

创建一个新场景,命名为Offline,在场景中创建一个空GameObject,添加NetworkManager、NetworkManagerHUD两个组件

Network Manager:

网络管理器功能包括:

    • Game state management(游戏状态管理,NetworkManager中有大量Hook,例如OnServerConnect服务端启动时自动调用,OnClientConnect客户端连接时自动调用)
    • Spawning management(同步产生游戏对象,例如有玩家进入游戏,就需要Server端通知所有已连接Clients同步创建出这个玩家)
    • Scene management(场景管理,开始多人游戏时进入联网场景(Play Scene),断开连接时切换到离线场景(Offline Scene))
    • Debugging information(调试信息)
    • Matchmaking(这是Unity Cloud提供的服务,用于游戏房间的创建、匹配、加入、销毁等)
    • Customization(NetworkManager虽然封装了好了功能,但通常需要自己写NetworkManager的扩展类以满足自己游戏的需求,例如Unity还有一个NetworkLobbyManager,也是继承NetworkManager,但是扩展出一个游戏大厅的功能)

Network Manager HUD:

这是用来配合NetworkManager的组件,只是一个功能展示UI,控制NetworkManager的基本功能。如StartHost()启动为主机,StartClient()启动为客户端等,一般只有测试的时候才用这个组件

2.创建Online场景

1.搭建UI

新建场景,命名为Game,创建Scroll View作为聊天信息显示区域,创建InputField作为消息输入框,创建一个发送Button。

2.制作消息框

1.创建一个Panel,添加VerticalLayoutGroup、ContentSizeFitter组件,并做一下设置,用于根据文字多少消息框自适应大小

2.在Panel下创建一个Text,用于显示用户发送的文字消息

3.保存成Prefab,用于在代码中动态创建消息框

3.制作玩家Prefab

1.新建空GameObject,添加NetworkIdentify组件,并勾选Local Player Authority,只允许本地客户端控制。NetworkIdentify是网络物体必须要有的组件,用于网络身份识别

2.新建脚本命名为Speaker,用于将客户端输入的消息发送给服务端,然后由服务端发给所有客户端以实现消息同步.

代码如下:

using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
public class Speaker : NetworkBehaviour
{
    public GameObject itemPrefab;
    private Transform content;
    private InputField inputBox;
    private Button sendButton;
    //[SyncVar(hook = "OnValueChanged")]
    [SyncVar]
    private int onlineNum = 0;
    void Start()
    {
        content = GameObject.Find("Canvas/Scroll View/Viewport/Content").transform;
        inputBox = GameObject.Find("Canvas/InputField").GetComponent<InputField>();
        sendButton = GameObject.Find("Canvas/SendButton").GetComponent<Button>();
        sendButton.onClick.AddListener(SendButtonCallback);
    }
    /// <summary>
    /// 显示在线人数
    /// </summary>
    private void OnGUI()
    {
        if (!isLocalPlayer)
            return;
        GUI.Label(new Rect(new Vector2(10, 10), new Vector2(150, 50)),
            string.Format("在线人数:{0}", onlineNum));
    }
    /// <summary>
    /// 更新Serve端在线人数
    /// </summary>
    private void Update()
    {
        if(isServer)
            onlineNum = NetworkManager.singleton.numPlayers;
    }
    /// <summary>
    /// 发送按钮响应事件
    /// 将用户输入消息发送给服务端
    /// </summary>
    void SendButtonCallback()
    {
        if (!isLocalPlayer)
            return;
        if (inputBox.text.Length > 0)
        {
            string str = string.Format("{0}:{1}{2}", Network.player.ipAddress, System.Environment.NewLine, inputBox.text);
            CmdSend(str);
            inputBox.text = string.Empty;
        }
    }
    /// <summary>
    /// 使用Command修饰的函数表示在客户端调用,在服务端执行
    /// </summary>
    /// <param name="str"></param>
    [Command]
    void CmdSend(string str)
    {
        RpcShowMessage(str);
    }
    /// <summary>
    /// ClientRpc修饰的函数 表示由服务端调用,在所有客户端执行
    /// </summary>
    /// <param name="str"></param>
    [ClientRpc]
    void RpcShowMessage(string str)
    {
        GameObject item = Instantiate(itemPrefab, content);
        item.GetComponentInChildren<Text>().text = str;
    }
}


代码非常简单,没有写发包收包却实现了客户端和服务端的消息同步,这就是UNet的强大之处,UNet的口号是让网络游戏开发就像开发单机游戏一样,最重要的是UNet Server端与客户端运行的是同样的代码,也就是说Server端有Unity所有的功能(物理引擎、网格导航等),这些信息直接在Server端计算出来同步给Clients,而用其它网络引擎就需要在Server端自己写碰撞和导航系统。不过UNet也有很多不足,例如Server和Client执行的是同样的代码,一旦有客户端被反编译那么Server端的逻辑也就暴露了。

代码中有几个UNet重要的关键字:

自动同步:

[SyncVar]或[SyncVar(hook="FuncName")]: 用于自动同步Server端变量,当Server端SyncVar修饰的变量发生改变时就会自动同步给所有Clients, 第二种写法是当此变量改变时会自动调用FuncName函数,这种同步方法通常用于变化比较频繁的变量。

手动同步:

[Command]:函数名必须以Cmd开头,Command修饰的函数表示在客户端调用,在服务端执行。如代码中在客户端调用CmdSend(string message), 把当前客户端用户输入消息作为参数传进去,该函数在客户端调用时传入了消息,然后此函数会在服务端执行,这样服务端就得到了客户端传递进来的消息。

[ClientRpc]:函数名必须以Rpc开头,ClientRpc修饰的函数 表示由服务端调用,在所有客户端执行。服务端执行了CmdSend()方法,而此方法又调用了RpcShowMessage(string str),把从客户端传来的消息又直接传入了RpcShowMessage(), 而[ClientRpc]修饰的函数是服务端调用客户端执行,服务端又将从客户端收到的消息转发给了所有客户端,这样就实现了消息的同步。

4.设置NetworkManager

如图,把Offline和Game场景以及Speaker预制体拖入

5.运行,测试

想要使用Unity Cloud的在线游戏房间匹配系统需要在Unity开启MultiPlayer服务

Build项目,然后运行两个,一个启动为Host,一个启动为Client,启动后NetworkManager会自动切换至Online场景:

测试图:

  • 6
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
好的,以下是基于PyTorch实现的简单U-Net网络。 ```python import torch.nn as nn import torch.nn.functional as F class DoubleConv(nn.Module): # 定义一个DoubleConv类,表示U-Net网络的基本结构 def __init__(self, in_channels, out_channels): super(DoubleConv, self).__init__() # 定义两个卷积层 self.conv = nn.Sequential( nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1), nn.BatchNorm2d(out_channels), nn.ReLU(inplace=True), nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1), nn.BatchNorm2d(out_channels), nn.ReLU(inplace=True) ) def forward(self, x): x = self.conv(x) return x class UNet(nn.Module): # 定义一个UNet类,表示U-Net网络的整体结构 def __init__(self, in_channels=3, out_channels=1, features=[64, 128, 256, 512]): super(UNet, self).__init__() self.ups = nn.ModuleList() self.downs = nn.ModuleList() self.pool = nn.MaxPool2d(kernel_size=2, stride=2) # 定义下采样和上采样过程中的卷积层 for feature in features: self.downs.append(DoubleConv(in_channels, feature)) in_channels = feature self.ups.append(nn.ConvTranspose2d(feature*2, feature, kernel_size=2, stride=2)) self.ups.append(DoubleConv(feature*2, feature)) # 定义最后的卷积层 self.bottleneck = DoubleConv(features[-1], features[-1]*2) self.final_conv = nn.Conv2d(features[0], out_channels, kernel_size=1) def forward(self, x): skip_connections = [] for down in self.downs: x = down(x) skip_connections.append(x) x = self.pool(x) x = self.bottleneck(x) skip_connections = skip_connections[::-1] for idx in range(0, len(self.ups), 2): x = self.ups[idx](x) skip_connection = skip_connections[idx//2] if x.shape != skip_connection.shape: x = F.interpolate(x, size=skip_connection.shape[2:], mode='bilinear', align_corners=True) concat_skip = torch.cat((skip_connection, x), dim=1) x = self.ups[idx+1](concat_skip) return self.final_conv(x) ``` 该网络包含两个类,`DoubleConv` 和 `UNet`。`DoubleConv` 类表示 U-Net 网络中的基本结构,包含两个卷积层,一个 batchnorm 层和一个 ReLU 激活层。`UNet` 类表示 U-Net 网络的整体结构,包含下采样过程中的卷积层和上采样过程中的反卷积层,以及最后的卷积层。 在 `UNet` 类的 `__init__` 方法中,我们定义了下采样和上采样过程中的卷积层和反卷积层,以及最后的卷积层。其中 `features` 参数是一个列表,表示每一层的特征数,我们将使用该参数来定义网络结构。 在 `UNet` 类的 `forward` 方法中,我们首先定义了一个列表 `skip_connections`,用于存储下采样过程中的 feature maps,然后我们依次执行下采样和上采样过程,并将下采样过程中的 feature maps 存储到 `skip_connections` 中。在上采样过程中,我们将当前 feature map 和对应的下采样过程中的 feature map 进行拼接,然后执行一次卷积操作,最后使用一个 1x1 的卷积层生成最终的输出。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值