1.Protobufd的概念
Protocol Buffers(简称 Protobuf)是一种用于序列化结构化数据的语言无关、平台无关、可扩展的机制。它由 Google
开发,用于解决数据交换和持久化的问题。Protobuf 定义了一种简单的语言来描述数据结构,然后使用这个描述生成用于序列化数据的代码。
2.Protobuf的使用
- 先去https://github.com/protocolbuffers/protobuf/tree/main/csharp将Probuf源码下载下来
将它下载到项目文件夹里
打开它的源码
打开之后重新生成解决方案
他会提示有6个成功
如果你没有下载相关库,vs会有提示信息,下载安装就好了
如若控制台还报错缺少库,可以直接去官网下载相关的库
将这三个文件拷贝到Unity的Plugin文件夹里面
最后在Unity项目中创建一个ProtoHelper脚本实现序列化和反序列化的接口
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Google.Protobuf;
using System;
public class ProtoHelper : MonoBehaviour
{
//protobuf序列化接口
public static byte[] ToBytes(object message)
{
return ((Google.Protobuf.IMessage)message).ToByteArray();
}
//反序列化接口
public static T ToObject<T>(byte[] bytes) where T : Google.Protobuf.IMessage
{
var message = Activator.CreateInstance<T>();
message.MergeFrom(bytes);
return message;
}
}
然后将这个类拷贝到你的服务端代码
创建一个protocol-buffers文件夹
结构如下
vs安装Google.protobuf.Tools,打开如下面板,在搜索框输入Google.protobuf.Tools,找到之后直接安装
安装成功控制台会提示,文件夹里面会有这个包
将这个Window64位的protoc.exe拷贝到之前创建的protocol-buffers下的tool
这里有两个批处理文件,这个批处理脚本的作用是编译 Protocol Buffers (.proto) 文件为 C# 代码,使用了 protoc.exe 这个工具。内容如下:
@echo off
set "PROTOC_EXE=%cd%\tool\protoc.exe"
set "WORK_DIR=%cd%\ProtoFile"
set "CS_OUT_PATH=%cd%\cs"
::if not exist %CS_OUT_PATH% md %CS_OUT_PATH%
for /f "delims=" %%i in ('dir /b protoFile "ProtoFile/*.proto"') do (
echo gen protoFile/%%i...
"%PROTOC_EXE%" --proto_path="%WORK_DIR%" --csharp_out="%CS_OUT_PATH%" "%WORK_DIR%\%%i")
echo finish...
pause
解释:
@echo off:
这行指令告诉批处理关闭命令回显,这意味着在运行脚本时不会在屏幕上显示命令本身,只会显示执行结果。
set “PROTOC_EXE=%cd%\tool\protoc.exe”
这行设置了一个变量 PROTOC_EXE,它指向了 protoc.exe 的路径,该路径位于当前目录下的 tool 文件夹中。
set “WORK_DIR=%cd%\ProtoFile” 和 set “CS_OUT_PATH=%cd%\cs”
这两行分别设置了两个变量,WORK_DIR 指向了 ProtoFile 文件夹的路径CS_OUT_PATH 指向了 cs 文件夹的路径。
::if not exist %CS_OUT_PATH% md %CS_OUT_PATH%
这一行是一个注释,原本可能是用于检查输出路径是否存在,如果不存在则创建该路径。但由于前面已经注释掉了,所以这行代码不会执行。
for /f “delims=” %%i in (‘dir /b protoFile “ProtoFile/*.proto”’) do (…)
这行开始一个 for 循环,它遍历 ProtoFile 文件夹下所有的 .proto 文件。
echo gen protoFile/%%i…
这行会打印当前正在处理的 .proto 文件的路径。
%PROTOC_EXE% --proto_path=“%WORK_DIR%” --csharp_out=“%CS_OUT_PATH%” “%WORK_DIR%%%i”
这行是真正调用 protoc.exe 的命令,它将当前 .proto 文件编译为 C# 代码,然后输出到指定路径。参数–proto_path 指定了 .proto 文件的搜索路径,–csharp_out 指定了生成的 C# 代码的输出路径。
echo finish…
这行会在编译完成后打印 “finish…”。
pause: 这行会让脚本暂停,等待用户按下任意键继续,这样可以让用户看到脚本的输出结果。
打开D:\Unity 2023.1.20f1c1\NetProject\protocol-buffers\ProtoFile\PlayerMessage文件
点击批处理_GenAllC#.bat将cs文件生成出来
PlayerMessage.cs里面就会有相关字段的定义
将PlayerMessage.cs文件拷贝到unity中进行检验
在GameManger中加入一段代码
if (Input.GetKeyDown(KeyCode.S))
{
LoginS2C loginS2C = new LoginS2C();
loginS2C.Account = "xxx";
loginS2C.Password = "yyy";
loginS2C.Test.Add(1);
loginS2C.Test.Add(2);
loginS2C.Test.Add(3);
loginS2C.Test.Add(4);
byte[] data = ProtoHelper.ToBytes(loginS2C);
Debug.Log(data.Length);
LoginS2C loginS2C_test = ProtoHelper.ToObject<LoginS2C>(data);
var act = loginS2C_test.Account;
var pwd = loginS2C_test.Password;
Debug.Log(act+" "+pwd);
}
GameObject就变成这样了
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
public class GameManager : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
Client.Instance.Start();
var loginPrefab = Resources.Load<GameObject>("LoginView");//加载Resources/LoginView目录下的一个预制体
var loginView = GameObject.Instantiate<GameObject>(loginPrefab);//实例化这个预制体
loginView.AddComponent<LoginView>();//将LoginView脚本挂载在这个实例化的对象上
}
// Update is called once per frame
void Update()
{
if(Input.GetKeyDown(KeyCode.A)) {
Client.Instance.Send(Encoding.UTF8.GetBytes("login..."));
}
if (Input.GetKeyDown(KeyCode.S))
{
LoginS2C loginS2C = new LoginS2C();
loginS2C.Account = "xxx";
loginS2C.Password = "yyy";
loginS2C.Test.Add(1);
loginS2C.Test.Add(2);
loginS2C.Test.Add(3);
loginS2C.Test.Add(4);
byte[] data = ProtoHelper.ToBytes(loginS2C);
Debug.Log(data.Length);
LoginS2C loginS2C_test = ProtoHelper.ToObject<LoginS2C>(data);
var act = loginS2C_test.Account;
var pwd = loginS2C_test.Password;
Debug.Log(act+" "+pwd);
}
}
}
当运行unity按下S键之后,会出现这样的结果