Unity角色多人同步

1.位置同步和状态同步:需要同步的节点上挂载脚本:

        gameObject.AddComponent<SyncTransform>(); ; //同步gameObject.GetComponent<SyncTransform>().syncId = SyncUtilFunc.GetRoleSyncId(PlayerData.Instance.PlayerId); //同步gameObject.GetComponent<SyncTransform>().ownerState = OwnerState.Mine;gameObject.GetComponent<SyncTransform>().isRole = true;gameObject.AddComponent<SyncState>(); //同步gameObject.GetComponent<SyncState>().syncId = SyncUtilFunc.GetRoleSyncId(PlayerData.Instance.PlayerId); //同步gameObject.GetComponent<SyncState>().ownerState = OwnerState.Mine;

(1)用于多人同步的基类:SyncBase.cs

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public enum OwnerState {Null,//无人拥有Mine,//拥有者是我Ohter,//拥有者是其他玩家
}public class SyncBase : MonoBehaviour
{/// <summary>/// 只有拥有者才允许发数据/// </summary>public OwnerState ownerState;public string syncId;public SyncWrapData dataBase = new SyncWrapData();protected string dataName;public void SendNetWork<T>(T data, string stName) {if (dataName != stName) {Debug.LogError("error:传输数据出错请检查" + dataName + " :" + stName);return;}dataBase.syncId = syncId;dataBase.name = dataName;dataBase.obj = JsonUtility.ToJson(data);dataBase.playerId = PlayerData.Instance.PlayerId.ToString();NetManager.Instance.SendSyncData(dataBase);}public void AddNetWorkListner<T>(Action<T> callback,string stName) {if (NetManager.Instance.isConnected() == false) {return;}dataName = stName;NetManager.Instance.AddSyncHandler(nameof(T), (SyncWrapData syncData) => {if (syncData.syncId == syncId) { //只关心自己同步ID的事情if (stName == syncData.name) {//类型结构需要和传递进来的结构一致var dstData = JsonUtility.FromJson<T>(syncData.obj); //ID一样自然可以转callback(dstData);}}});}}

引入库:LitJson

引用类:

public class SyncWrapData {public string syncId;public string playerId; //拥有者的IDpublic bool isLocation;public string name;public string obj;
}

模型层:SyncModel.cs

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public static class SyncUtilFunc {public static string GetRoleSyncId(long id) {return $"{ConstTag.Player}{id}";}}public class SyncWrapData {public string syncId;public string playerId; //拥有者的IDpublic bool isLocation;public string name;public string obj;
}/// <summary>
/// 数据要乘以10转成分米
/// </summary>
public class SyncTransfomData {public int[] pos = new int[3]; public int[] roate = new int[3];public int[] scale = new int[3];  
}public static class SyncTransfomUtil
{public static SyncTransfomData FromTransform(Transform t,ref SyncTransfomData data) {data.pos[0] = (int)(t.position.x * 10f);data.pos[1] = (int)(t.position.y * 10f);data.pos[2] = (int)(t.position.z * 10f);data.roate[0] = (int)(t.eulerAngles.x * 10f);data.roate[1] = (int)(t.eulerAngles.y * 10f);data.roate[2] = (int)(t.eulerAngles.z * 10f);data.scale[0] = (int)(t.localScale.x * 10f);data.scale[1] = (int)(t.localScale.y * 10f);data.scale[2] = (int)(t.localScale.z * 10f);return data;}public static SyncTransfomData FromTankLocation(TankLocation location, ref SyncTransfomData data) {data.pos[0] = (int)(location.x*10f);data.pos[1] = (int)(location.y*10f);data.pos[2] = (int)(location.z*10f);data.roate[0] = (int)(location.dx * 10f);data.roate[1] = (int)(location.dy * 10f);data.roate[2] = (int)(location.dz * 10f);return data;}public static void ToTransform(Transform t, SyncTransfomData data) {t.position = new Vector3((float)data.pos[0]/10f, (float)data.pos[1] / 10f, (float)data.pos[2] / 10f);t.eulerAngles = new Vector3((float)data.roate[0]/10f, (float)data.roate[1] / 10f, (float)data.roate[2] / 10f);}public static TankLocation ToTankLocation(ref TankLocation location, SyncTransfomData data) {location.x = ((float)data.pos[0]) / 10f;location.y = ((float)data.pos[1]) / 10f;location.z = ((float)data.pos[2]) / 10f;location.dx = ((float)data.roate[0]) / 10f;location.dx = ((float)data.roate[1]) / 10f;location.dx = ((float)data.roate[2]) / 10f;return location;}/// <summary>/// 分米内相同/// </summary>/// <param name="v1"></param>/// <param name="v2"></param>/// <returns></returns>public static bool V3Equal(Vector3 v1 ,Vector3 v2) {return (Math.Abs(v1.x - v2.x) < 0.1f) && (Math.Abs(v1.y - v2.y) < 0.1f) && (Math.Abs(v1.z - v2.z) < 0.1f);}}public class SyncStateData {public string stateName;
}public class SyncOperationData {public string operation;
}public class SyncShareData{public string ownerId;public float[] pos = new float[3];public float[] roate = new float[3];public float[] scale = new float[3];
}public class SyncWorldData {public Dictionary<string, SyncShareData> dir_shareDatas = new Dictionary<string, SyncShareData>();public Dictionary<string, SyncStateData> dir_stateDatas = new Dictionary<string, SyncStateData>();public Dictionary<string, SyncTransfomData> dir_transformDatas = new Dictionary<string, SyncTransfomData>();
}public class SyncWorldModel:SingletonBase<SyncWorldModel> {SyncWorldData m_worldData = new();public void SetWorldData(SyncWorldData data) {m_worldData = data;}public SyncShareData GetShareData(string syncId) {if (m_worldData.dir_shareDatas.ContainsKey(syncId)) {return m_worldData.dir_shareDatas[syncId];}return null;}public SyncStateData GetStateData(string syncId) {if (m_worldData.dir_stateDatas.ContainsKey(syncId)) {return m_worldData.dir_stateDatas[syncId];}return null;}public SyncTransfomData GetTransfomData(string syncId) {if (m_worldData.dir_transformDatas.ContainsKey(syncId)) {return m_worldData.dir_transformDatas[syncId];}return null;}public void SetShareData(string syncId, SyncShareData data) {if (m_worldData.dir_shareDatas.ContainsKey(syncId)) {m_worldData.dir_shareDatas[syncId] = data;}else {m_worldData.dir_shareDatas.Add(syncId,data);}}public void SetStateData(string syncId, SyncStateData data) {if (m_worldData.dir_stateDatas.ContainsKey(syncId)) {m_worldData.dir_stateDatas[syncId] = data;}else {m_worldData.dir_stateDatas.Add(syncId, data);}}public void SetTransfomData(string syncId, SyncTransfomData data) {if (m_worldData.dir_transformDatas.ContainsKey(syncId)) {m_worldData.dir_transformDatas[syncId] = data;}else {m_worldData.dir_transformDatas.Add(syncId, data);}}public void SyncWorldData() {NetManager.Instance.SendSyncWorldData(m_worldData);}}

网络层脚本1:NetManager.cs


using Cysharp.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using LitJson;
using UnityEngine.Networking;
using UTNET;
using Unity.Burst.Intrinsics;
using System.Runtime.InteropServices;public class NetMessage<T> {public uint mergeid;delegate void Message(object sender, T msg);}public class NetManager : MonoBehaviour {private NetManager() { }public static NetManager Instance;public string token, version;public Client client;public string HaiMetaSpaceUrl = "https://test-haimeta-1314523153.cos-website.ap-shanghai.myqcloud.com/space";//192.144.138.221  /game.icewhale.netDictionary<string, List<Action<ExternalMessage>>> actions = new Dictionary<string, List<Action<ExternalMessage>>>();private Coroutine rec;[DllImport("__Internal")]private static extern string GetUA();private void Awake() {Instance = this;#if !UNITY_EDITOR && UNITY_WEBGLstring a = GetUA();if (a == "1"){//PC端Debug.Log("当前运行环境在PC端");PlayerData.Instance.isRunningPC = true;}if (a == "2"){//移动端Debug.Log("当前运行环境在移动端");PlayerData.Instance.isRunningPC = false;}
#endifEventManager.Instance.AddListener(EventName.OnSceneLoaded,OnSceneLoaded);}private void OnSceneLoaded(object sender, EventArgs e) {var arg = e as SceneLoadedArg;if (arg.state == AppConst.ViewMode) {CreateConeccetionBtn();}}async void Start() {DontDestroyOnLoad(this);Screen.sleepTimeout = SleepTimeout.NeverSleep;pushBtn();}public void webOpenHaiMetaUrl(string param = null) {//string url = this.HaiMetaSpaceUrl+param;//UnityEngine.Application.ExternalEval("window.location.href = \"" + url + "\";");}public void webOpenNewSpace(string spaceUrl,string spaceId){string url = spaceUrl + "?param={\"spaceId\":"+ spaceId + ",\"userName\":"+PlayerData.Instance.Name+",\"templateId\":"+ PlayerData.Instance.TemplateId + "}" + "&token=" + PlayerData.Instance.ltk + "&type=4"+ "&state=1";UnityEngine.Application.ExternalEval("window.location.href = \"" + url + "\";");}private void Update() {//if (client == null && newClient != null) {//    client = getNewClient();//}}public async void CreateConeccetionBtn(){
#if !UNITY_EDITORawait HttpHelper.Instance.GetServerConfig();
#endif//string host = string.Format("wss://game.icewhale.net/login");Debug.Log("CreateConeccetionBtn" +Host.gameServer);client = new Client(Host.gameServer);client.OnDisconnect = OnDisconnect;client.OnReconected = OnReconected;client.OnError = OnError;client.OnConnected = OnConnected;bool isConeccet = await client.ConnectAsync();if (isConeccet){Debug.Log("网络链接成功");}}//public void resetClient() {//    setNewClient(client);//}private void OnConnected() {Debug.Log("OnConnected");if (rec != null)StopCoroutine(rec);this.TriggerEvent(EventName.SocketOpen, null);}private void OnError(string msg) {Debug.LogError(string.Format("err msg:{0}", msg));}private void OnReconected() {Debug.Log("OnReconect");}private void OnDisconnect() {Debug.Log("OnDisconnect");//client = null;WarningPanel.ShowTimeTips("重连中....",10,null,null);rec = StartCoroutine(client.ReconectScoektAsync()); //断线重连逻辑}public void pushBtn() {LoadNetCofig();}void LoadNetCofig() {var configPath = Application.dataPath;#if UNITY_EDITORvar filepath = Path.Combine(Application.dataPath.Replace("Assets", ""), "config.txt");
#elsevar filepath = Path.Combine(Application.dataPath, "config.txt");
#endifDebug.Log("configPath" + filepath);filepath = filepath.Replace("\\", "/");LoadFileSetNetwork(filepath);}async void LoadFileSetNetwork(string filepath) {UnityWebRequest www = UnityWebRequest.Get(filepath);await www.SendWebRequest();if (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError) {Debug.LogError(www.error);}else {string json = www.downloadHandler.text;var data = LitJson.JsonMapper.ToObject(json);#if UNITY_EDITORvar serverMode = AppConst.serverMode;
#elsevar serverMode  = (ServerMode)int.Parse(HttpHelper.GetUrlParam("severAPI"));
#endifAppConst.serverMode = serverMode;switch (serverMode) {case ServerMode.preview:Host.ApiHost = (string)data["preview"];break;case ServerMode.dev:Host.ApiHost = (string)data["dev"];break;case ServerMode.overseas:Host.ApiHost = (string)data["overseas"];break;}switch (serverMode){case ServerMode.overseas:Host.AssetBundleIP = (string)data["OverseasAssetBundleIP"];break;case ServerMode.preview:Host.AssetBundleIP = (string)data["PreviewAssetBundleIP"];break;case ServerMode.dev:if ((string)data["AssetBundleIP"] != string.Empty){Host.AssetBundleIP = (string)data["AssetBundleIP"];}break;}#if !UNITY_EDITORvar paramUrl = HttpHelper.GetUrlParam("param");JsonData paramdata = JsonMapper.ToObject(UtilsFunc.UnicodeToString(paramUrl));try {string spaceId = paramdata["spaceId"].ToJson();PlayerData.Instance.SpaceId = spaceId;PlayerData.Instance.ltk = HttpHelper.GetUrlParam("token");}catch (Exception e) {Debug.LogError(e.Message + '\n' + e.StackTrace);}
#endifDebug.Log("LoadFileSetNetwork SpaceId" + PlayerData.Instance.SpaceId);//如果在编辑器下面,并且不填sapceId,那么则进入发布模式if (string.IsNullOrEmpty(PlayerData.Instance.SpaceId) == false) {await HttpHelper.Instance.GetSceneConfig();}#if !UNITY_EDITORif (paramdata.ContainsKey("templateId")) {if (paramdata["templateId"] != null) {string tempStr = (string)paramdata["templateId"];bool isExist = int.TryParse(tempStr,out var tempId);if (isExist) PlayerData.Instance.TemplateId = tempId;}}string stateUrl = HttpHelper.GetUrlParam("state");
#elsestring stateUrl = string.IsNullOrEmpty(PlayerData.Instance.SpaceId) == true ? AppConst.PublicMode : AppConst.ViewMode;
#endifvar arg = new SceneLoadActionArgs();arg.state = stateUrl;if (stateUrl == AppConst.PublicMode) {PlayerData.Instance.IsEditorMode = true;PlayerData.Instance.IsPublicMode = true;EventManager.Instance.TriggerEvent(EventName.OnPublishEnter, arg);HttpHelper.Instance.GetDefaultSpaceImg();return;}#if UNITY_EDITORHost.gameServer = (string)data["local"];
#endifEventManager.Instance.TriggerEvent(EventName.LoadSceneAction, arg);}}private async UniTaskVoid CreateConeccetion() {Debug.Log("开始...");bool isConeccet  = await client.ConnectAsync();if (isConeccet) {Debug.Log("网络链接成功");// this.Register<RegisterInfo>("test", (int code) =>// {//     Debug.Log("网络事件" +code);// });if (client != null) client.isConnect = true;//this.SendAPI();}elseDebug.Log("多次链接仍让未成功");}public void AddSyncHandler(string name, Action<SyncWrapData> call) {NetManager.Instance.Register<GameFrame>("gameFrame", (ExternalMessage pack) => {GameFrame info = PackageProtocol.DecodeInfo<GameFrame>(pack.data);//if (info.location.playerId == PlayerData.Instance.PlayerId) {//    return; //自己不用给自己同步//}var opc = info.opc;SyncWrapData dataBase = null;try {dataBase = JsonUtility.FromJson<SyncWrapData>(opc);if (dataBase.playerId != PlayerData.Instance.PlayerId.ToString()) { //只接受不同playerId的数据call.Invoke(dataBase);}}catch (Exception e) {//Debug.LogError(e);}});}public async void SendSyncData(SyncWrapData syncBase) {string jsonData = JsonUtility.ToJson(syncBase);if (NetManager.Instance.client == null) {Debug.LogError("NetManager.Instance.client == null");return;}var gameFrame = default (GameFrame);gameFrame.opc = jsonData;await NetManager.Instance.client.RequestAsync<GameFrame, GameFrame>("gameFrame", gameFrame);}public async void SendSyncWorldData(SyncWorldData worldData) {string jsonData = LitJson.JsonMapper.ToJson(worldData);if (NetManager.Instance.client == null) {Debug.LogError("NetManager.Instance.client == null");return;}//Debug.Log("SendSyncWorldData worldData:" + jsonData);var gameFrame = default(GameFrame);gameFrame.world = jsonData;await NetManager.Instance.client.RequestAsync<GameFrame, GameFrame>("gameFrame", gameFrame);}public async void SendFrameLocationData(SyncTransfomData syncPos,SyncWrapData wrap) {if (NetManager.Instance.client == null) {Debug.LogError("NetManager.Instance.client == null");return;}var gameFrame = new GameFrame();gameFrame.opc = JsonUtility.ToJson(wrap);gameFrame.location = new TankLocation();SyncTransfomUtil.ToTankLocation(ref gameFrame.location, syncPos);gameFrame.location.playerId = PlayerData.Instance.PlayerId;await NetManager.Instance.client.RequestAsync<GameFrame, GameFrame>("gameFrame", gameFrame);}public void Register<T>(string name, Action<ExternalMessage> ac) {//#if UNITY_WEBGL && !UNITY_EDITOR  //只在WebGl下生效if (PlayerData.Instance.IsEditorMode) return;//#endif//加入事件列表Proto pb = ProtoMaps.Instance.Name2Pb(name);List<Action<ExternalMessage>> list = null;if (actions.ContainsKey(name)) {list = actions[name];}else {list = new List<Action<ExternalMessage>>();actions.Add(name, list);}list.Add(ac);client.OnNetEx(name, (ExternalMessage pack) => {T info = PackageProtocol.DecodeInfo<T>(pack.data);//Debug.Log("网络事件" + JsonUtility.ToJson(info));//遍历事件列表依次回调foreach (Action<ExternalMessage> cb in list) {cb?.Invoke(pack);}});}public async void SendAPI() {//请求注册//RegisterInfo register = new RegisterInfo();//register.account = "abc";//register.phoneNum = "abc";//register.pwd = "abc";//RegisterInfo a = await client.RequestAsync<RegisterInfo, RegisterInfo>("test", register);//Debug.Log(a.phoneNum);client.OnNetEx("login", (ExternalMessage pack) => {Debug.LogError("回调返回");//T info = PackageProtocol.DecodeInfo<T>(pack.data);Debug.Log("网络事件" + JsonUtility.ToJson(info));遍历事件列表依次回调//foreach (Action<ExternalMessage> cb in list)//{//    cb?.Invoke(pack);//}});LoginVerify loginVerify = new LoginVerify();loginVerify.account = "18060974935";loginVerify.pwd = "123456ab";loginVerify.loginBizCode = 1;UserInfo ret = await NetManager.Instance.client.RequestAsync<LoginVerify, UserInfo>("login", loginVerify);Debug.Log(ret.nickName);TankEnterRoom myEnterRoom = new TankEnterRoom();myEnterRoom.roomId = 1002;myEnterRoom.team = 1;TankEnterRoom ret2 = await NetManager.Instance.client.RequestAsync<TankEnterRoom, TankEnterRoom>("enterRoom", myEnterRoom);Debug.Log(ret2.time);//int code = await client.RequestAsync<String, int>("gameObjUsed", "ok");}public bool isConnected() {if (client == null) {return false;}return client.isConnect;}private void OnDestroy() {if (client != null) {client.Cancel();}}
}

网络层脚本2:Client.cs

using System;
using UnityEngine;
using Cysharp.Threading.Tasks;
using System.Threading;
using System.Collections;
using UnityWebSocket;namespace UTNET
{public enum NetWorkState{CONNECTING,CONNECTED,DISCONNECTED,TIMEOUT,ERROR,KICK}public class Client{private static int RqID = 0;//public NetWorkState state;public Action OnReconected, OnDisconnect, OnConnected;public Action<string> OnError;public uint retry;public bool isConnect = false;Protocol protocol;WebSocket socket;UniTaskCompletionSource<bool> utcs;private bool isForce;private bool isHeardbeat = false;private string host;public Client(string host){Debug.Log("Client" + host);this.host = host;this.createSocket();}private void createSocket(){if (socket != null) {}Debug.Log("createSocket host=" + this.host);utcs = new UniTaskCompletionSource<bool>();if (socket != null) {socket.CloseAsync();socket.OnClose += ReCreateSocket;}else {socket = new WebSocket(this.host);socket.OnOpen += OnOpen;socket.OnMessage += OnMessage;socket.OnClose += OnClose;socket.OnError += OnErr;}}private void ReCreateSocket(object sender, CloseEventArgs e) {socket = new WebSocket(this.host);socket.OnOpen += OnOpen;socket.OnMessage += OnMessage;socket.OnClose += OnClose;socket.OnError += OnErr;}//断线重连逻辑public IEnumerator ReconectScoektAsync(){while (true){yield return new WaitForSeconds(2);Debug.Log(" client : reconectScoekt");if (PlayerData.Instance.isAnotherLogin) break;if (socket.ReadyState == WebSocketState.Connecting || socket.ReadyState == WebSocketState.Open) break;socket.ConnectAsync();}}//开始心跳逻辑public IEnumerator StartHeardBeat(){isHeardbeat = true;while (true){Debug.Log("开始发送心跳");if (isHeardbeat == false) continue;yield return new WaitForSeconds(2);//发送心跳this.sendHeardbeat();}}private async void sendHeardbeat(){int u1 = await this.RequestAsync<int, int>("heartbeat", 21);}public UniTask<bool> ConnectAsync(){socket.ConnectAsync();return utcs.Task;}private void OnOpen(object sender, OpenEventArgs e){if (protocol == null)protocol = new Protocol();protocol.SetSocket(socket);protocol.OnReconected = OnReconected;protocol.OnError = OnError;//bool isOK = await protocol.HandsharkAsync(this.token);Debug.Log("open:" + e);isConnect = true;utcs.TrySetResult(true);OnConnected?.Invoke();}private async void OnClose(object sender, CloseEventArgs e){if (socket.ReadyState == UnityWebSocket.WebSocketState.Connecting || socket.ReadyState == UnityWebSocket.WebSocketState.Open) return;await UniTask.SwitchToMainThread();isConnect = false;Cancel();if (!isForce){await UniTask.Delay(1000);//socket.Open();}OnDisconnect?.Invoke();}public void OnErr(object sender, ErrorEventArgs e){utcs.TrySetResult(false);isConnect = false;//Debug.LogError(e.Exception.Message);}private void OnMessage(object sender, MessageEventArgs e){protocol.OnReceive(e.RawData);isConnect = true;}public void OnNet(uint mid, uint sid, Action<ExternalMessage> cb){uint merge = PackageProtocol.getMergeCmd(mid, sid);protocol.SetOnNet(merge, cb);}public void OnNetEx(string name, Action<ExternalMessage> cb){Proto pb = ProtoMaps.Instance.protos[name] as Proto;uint merge = PackageProtocol.getMergeCmd(pb.mid, pb.sid);protocol.SetOnNet(merge, cb);}public void Notify<T>(string route, T info = default){//uint rqID = (uint)Interlocked.Increment(ref RqID);try{
#if SOCKET_DEBUGDebug.Log(string.Format("[Notify] -->> [{0}] {1}", route, JsonUtility.ToJson(info)));
#endifprotocol.Notify<T>(route, info);}catch (Exception e){Debug.Log(string.Format("[Notify Exception]{0}", e.Message));throw e;}}public async UniTask<S> RequestAsyncEx<T, S>(uint mid, uint sid, T info = default, string modelName = null){uint cmdMerge = PackageProtocol.getMergeCmd(mid, sid);uint rqID = (uint)Interlocked.Increment(ref RqID);try{
#if SOCKET_DEBUGDebug.Log(string.Format("[{0}][Request] -->> [{1}] {2}", rqID, route, JsonUtility.ToJson(info)));
#endifExternalMessage pack = await protocol.RequestAsync<T>(rqID, cmdMerge, info, modelName);S msg = PackageProtocol.DecodeInfo<S>(pack.data);#if SOCKET_DEBUGDebug.Log(string.Format("[{0}][Request] <<-- [{1}] {2} {3}", rqID, route, JsonUtility.ToJson(msg), JsonUtility.ToJson(msg.info)));
#endifreturn msg;}catch (Exception e){//Debug.Log(string.Format("[{0}][RequestAsync Exception]{1}", rqID, e.Message));throw e;}}public async UniTask<S> RequestAsync<T, S>(string name, T info = default, string modelName = null){// #if UNITY_WEBGL && !UNITY_EDITOR  //只在WebGl下生效if (PlayerData.Instance.IsEditorMode) {S msg = PackageProtocol.DecodeInfo<S>(null);return msg;}//#endif//Debug.Log($"sendMsg {name}");Proto pb = ProtoMaps.Instance.Name2Pb(name);uint cmdMerge = PackageProtocol.getMergeCmd(pb.mid, pb.sid);uint rqID = (uint)Interlocked.Increment(ref RqID);//Debug.LogError("调试消息id" + rqID);try{
#if SOCKET_DEBUGDebug.Log(string.Format("[{0}][Request] -->> [{1}] {2}", rqID, route, JsonUtility.ToJson(info)));
#endifExternalMessage pack = await protocol.RequestAsync<T>(rqID, cmdMerge, info, modelName);S msg = PackageProtocol.DecodeInfo<S>(pack.data);
#if SOCKET_DEBUGDebug.Log(string.Format("[{0}][Request] <<-- [{1}] {2} {3}", rqID, route, JsonUtility.ToJson(msg), JsonUtility.ToJson(msg.info)));
#endifreturn msg;}catch (Exception e){//Debug.Log(string.Format("[{0}][RequestAsync Exception]{1}", rqID, e.Message));//Debug.Log("房间网络连接断开.......");S msg = PackageProtocol.DecodeInfo<S>(null);return msg;throw e;}}public void Cancel(bool isForce = false){this.isForce = isForce;utcs.TrySetCanceled();if (socket.ReadyState != UnityWebSocket.WebSocketState.Closed){socket.CloseAsync();}if (protocol != null){protocol.StopHeartbeat();protocol.CanceledAllUTcs();}}public UnityWebSocket.WebSocketState getSocketState() {if (socket != null) {return socket.ReadyState;}return 0;}public void Close() {Debug.Log("UnityWebSocket......" + socket);Debug.Log("UnityWebSocket状态......"+ socket.ReadyState);if (socket!=null && socket.ReadyState == UnityWebSocket.WebSocketState.Open) {socket.CloseAsync();Debug.Log("强制玩家关闭连接......");}}void OnDestroy(){socket.CloseAsync(); }}
}

网络层脚步3:PackageProtocol.cs

using ProtoBuf;
using System;
using System.IO;
using System.Text;
using UnityEngine.XR;namespace UTNET
{public enum PackageType{HEARTBEAT = 1,REQUEST = 2,PUSH = 3,KICK = 4,RESPONSE = 5,HANDSHAKE = 6,ERROR = 7,NOTIFY = 8}public class PackageProtocol{//获取cmdpublic static uint getCmd(uint merge){return merge >> 16;}//获取subCmdpublic static uint getSubCmd(uint merge){return merge & 0xFFFF;}//获取mergeCmdpublic static uint getMergeCmd(uint cmd, uint subCmd){return (cmd << 16) + subCmd;}public static byte[] Encode(PackageType type){Package sr = new Package(){packageType = (uint)type};return Serialize(sr);}public static byte[] Encode<T>(PackageType type, uint packID, string route, T info, string modelName = null){Package sr = new Package(){packageType = (uint)type,packID = packID,route = route,buff = Serialize<T>(info),modelName = modelName};return Serialize(sr);}public static byte[] EncodeEx<T>(uint packID, uint cmdMerge, T info){//Package sr = new Package()//{//    packageType = (uint)type,//    packID = packID,//    route = route,//    buff = Encoding.Default.GetBytes(SerializeEx<T>(info)),//};//return SerializeEx(sr);ExternalMessage sr = new ExternalMessage(){cmdCode = 100,protocolSwitch = 1,cmdMerge = cmdMerge,responseStatus = 0,validMsg = "",data = Encoding.UTF8.GetBytes(SerializeEx<T>(info)),};return Serialize(sr);}public static byte[] Encode<T>(uint packID, uint cmdMerge, T info){ExternalMessage sr = new ExternalMessage(){cmdCode = 100,protocolSwitch = 1,cmdMerge = cmdMerge,responseStatus = 0,validMsg = "",data = Serialize<T>(info),msgId = (int)packID,};return Serialize(sr);}public static string SerializeEx<T>(T t){using (MemoryStream ms = new MemoryStream()){Serializer.Serialize<T>(ms, t);return Encoding.UTF8.GetString(ms.ToArray());}}public static byte[] Serialize<T>(T info){if (typeof(T) == typeof(int)){int value = (int)Convert.ChangeType(info, typeof(int));value = (value << 1);info = (T)Convert.ChangeType(info, typeof(T));}MemoryStream ms = new MemoryStream();Serializer.Serialize(ms, info);byte[] buff = ms.ToArray();ms.Close();return buff;//try//{//    //涉及格式转换,需要用到流,将二进制序列化到流中//    using (MemoryStream ms = new MemoryStream())//    {//        //使用ProtoBuf工具的序列化方法//        Serializer.Serialize<T>(ms, info);//        //定义二级制数组,保存序列化后的结果//        byte[] result = new byte[ms.Length];//        //将流的位置设为0,起始点//        ms.Position = 0;//        //将流中的内容读取到二进制数组中//        ms.Read(result, 0, result.Length);//        return result;//    }//}//catch (Exception ex)//{//    return null;//}}public static ExternalMessage Decode(byte[] buff){//protobuf反序列化MemoryStream mem = new MemoryStream(buff);ExternalMessage rs = Serializer.Deserialize<ExternalMessage>(mem);mem.Close();return rs;}public static T DecodeInfo<T>(byte[] buff){if (buff == null) return default;T rs;if (typeof(T) == typeof(int)){int value;using (var stream = new MemoryStream(buff)){value = Serializer.Deserialize<int>(stream);}//转zig zagvalue = (value >> 1) ^ -(value & 1);rs = (T)Convert.ChangeType(value, typeof(T));}else{MemoryStream mem = new MemoryStream(buff);rs = Serializer.Deserialize<T>(mem);mem.Close();}return rs;}//public static T MyMethod<T>(byte[] buff) where T : Object//{//    return null;//}}
}

网络层脚本4:ProtoMaps.cs

using System;
using System.Collections.Generic;namespace UTNET
{public interface IProto{}//R ��������, S ��������//public class Proto<R, S> : IProtopublic class Proto : IProto{public string name; //Э������public uint mid { get; set; } //��idpublic uint sid { get; set; } //��idpublic uint mergeid;public int typ; //Э������//public R r;//public S s;public Proto(string name, uint mid, uint sid, int typ){this.name = name;this.mid = mid;this.sid = sid;this.typ = typ;}}public class ProtoMaps{private static readonly ProtoMaps instance = new ProtoMaps();public static ProtoMaps Instance{get { return instance; }}//public Dictionary<string, IProto> protos = new Dictionary<string, IProto>();public Dictionary<string, Proto> protos = new Dictionary<string, Proto>();public Dictionary<uint, Proto> id2proto = new Dictionary<uint, Proto>();public void Add(string name, Proto pb){protos.Add(name, pb);}public void SortById(){foreach (var item in protos){var pb = item.Value;uint mergeid = PackageProtocol.getMergeCmd(pb.mid, pb.sid);pb.mergeid = mergeid;id2proto.Add(mergeid, pb);}}public ProtoMaps(){this.Add("login", new Proto("login", 1, 2, 1));this.Add("registerInfo", new Proto("registerInfo", 1, 1, 1));this.Add("enterRoom", new Proto("enterRoom", 1, 13, 3));this.Add("roleMove", new Proto("roleMove", 4, 3, 3));//this.Add("onNewRoleEnter", new Proto("onNewRoleEnter", 7, 1, 2));//服务器主动广播的角色退出this.Add("roleExitRoom", new Proto("roleExitRoom", 7, 2, 2));this.Add("talkMrg", new Proto("talkMrg", 1, 9, 3));//玩家点击的角色退出this.Add("playerExitRoom", new Proto("playerExitRoom", 1, 5, 3));this.Add("gameFrame", new Proto("gameFrame", 4, 2, 1));this.Add("gameObjUsed", new Proto("gameObjUsed", 4, 4, 3));this.Add("getOtherInfo", new Proto("getOtherInfo", 1, 23, 1));this.Add("heatBeat", new Proto("heatBeat", 1, 120, 1));SortById();}public Proto Name2Pb(string name){//Proto pb = ProtoMaps.Instance.protos[name];if (protos.ContainsKey(name)){return protos[name];}return null;}internal Proto GetByMergeId(uint cmdMerge){if (id2proto.ContainsKey(cmdMerge)){return id2proto[cmdMerge];}return null;}}}

网络层脚步5:ExternalMessage.cs

using ProtoBuf;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;namespace UTNET
{[ProtoContract]public class ExternalMessage{[ProtoMember(1)]// 请求命令类型: 0 心跳,1 业务public int cmdCode;// 协议开关,用于一些协议级别的开关控制,比如 安全加密校验等。 : 0 不校验[ProtoMember(2)]public int protocolSwitch;// 业务路由(高16为主, 低16为子)[ProtoMember(3)]public uint cmdMerge;// 响应码: 0:成功, 其他为有错误[ProtoMember(4,DataFormat = DataFormat.ZigZag)]public int responseStatus;// 验证信息(错误消息、异常消息),通常情况下 responseStatus == -1001 时, 会有值[ProtoMember(5)]public string validMsg;// 业务请求数据[ProtoMember(6)]public byte[] data;// 消息标记号;由前端请求时设置,服务器响应时会携带上;[ProtoMember(7, DataFormat = DataFormat.ZigZag)]public int msgId;}[ProtoContract]public class Package{[ProtoMember(1)]public uint packageType;[ProtoMember(2)]public string route = null;[ProtoMember(3)]public uint packID = 0;[ProtoMember(4)]public byte[] buff = null;[ProtoMember(5)]public string modelName = null;}//[ProtoContract]//public class Message<T>//{//    [ProtoMember(1)]//    public uint err;//    [ProtoMember(2)]//    public string errMsg = default;//    [ProtoMember(3)]//    public T data = default;//    public Message() { }//    public Message(uint err, string errMsg, T info)//    {//        this.err = err;//        this.errMsg = errMsg;//        this.data = info;//    }//}[ProtoContract]public class HandShake{[ProtoMember(1)]public string token;}[ProtoContract]public class Heartbeat{[ProtoMember(1)]public uint heartbeat;}
}

网络层脚本6:TestProto.cs


using ProtoBuf;
using ProtoBuf.Meta;
using System.Collections.Generic;[ProtoContract]
public class RegisterInfo
{[ProtoMember(1)]public string phoneNum;[ProtoMember(2)]public string account;[ProtoMember(3)]public string pwd;
}[ProtoContract]
public class LoginVerify 
{[ProtoMember(1)]public string account;[ProtoMember(2)]public string pwd;[ProtoMember(3, DataFormat = DataFormat.ZigZag)]public int loginBizCode;[ProtoMember(4, DataFormat = DataFormat.ZigZag)]public int spaceId;
}[ProtoContract]
public class LobbyTalkMsg
{[ProtoMember(1, DataFormat = DataFormat.ZigZag)]public long id;[ProtoMember(2, DataFormat = DataFormat.ZigZag)]public long usrId;[ProtoMember(3)]public string nickName;[ProtoMember(4, DataFormat = DataFormat.ZigZag)]public int to_usrId;[ProtoMember(5)]public string msg;[ProtoMember(6, DataFormat = DataFormat.ZigZag)]public int msg_type;
}[ProtoContract]
public class RequestSuccess
{[ProtoMember(1, DataFormat = DataFormat.ZigZag)]public int success;}[ProtoContract]
public struct GameFrame {[ProtoMember(1, DataFormat = DataFormat.ZigZag)]public long frameId;[ProtoMember(2)]public string opc;[ProtoMember(3)]public string world;[ProtoMember(4)]public TankLocation location;}[ProtoContract]
public class UserInfo {[ProtoMember(1, DataFormat = DataFormat.ZigZag)]public long id;[ProtoMember(2)]public  string nickName;[ProtoMember(3)]public string account;[ProtoMember(4)]public string gender;[ProtoMember(5)]public string headImgUrl;[ProtoMember(6, DataFormat = DataFormat.ZigZag)]public long avatarId;[ProtoMember(7)]public string avatar;[ProtoMember(8)]public string hold;[ProtoMember(9)]public string mtk;[ProtoMember(10)]public string ltk;[ProtoMember(11, DataFormat = DataFormat.ZigZag)]public long roomId;[ProtoMember(12, DataFormat = DataFormat.ZigZag)]public long team;[ProtoMember(13)]public TankLocation tankLocation;[ProtoMember(14, DataFormat = DataFormat.ZigZag)]public int speed;
}[ProtoContract]
public class TankLocation {[ProtoMember(1, DataFormat = DataFormat.ZigZag)]public  float x;[ProtoMember(2, DataFormat = DataFormat.ZigZag)]public float y;[ProtoMember(3, DataFormat = DataFormat.ZigZag)]public float z;[ProtoMember(4, DataFormat = DataFormat.ZigZag)]public float dx;[ProtoMember(5, DataFormat = DataFormat.ZigZag)]public float dy;[ProtoMember(6, DataFormat = DataFormat.ZigZag)]public float dz;[ProtoMember(7, DataFormat = DataFormat.ZigZag)]public long playerId;[ProtoMember(8, DataFormat = DataFormat.ZigZag)]public long time;}[ProtoContract]
public class TankPlayer {[ProtoMember(1, DataFormat = DataFormat.ZigZag)]public long id;[ProtoMember(2, DataFormat = DataFormat.ZigZag)]public int speed;[ProtoMember(3)]public TankLocation tankLocation;[ProtoMember(4)]public string nickname;[ProtoMember(5)]public string hold;// 其他属性: key: 子弹 id 1 玩具弹, 2 雪弹; value : 数量[ProtoMember(6)]public Dictionary<int, int> tankBulletMap;
}[ProtoContract]
public class TankEnterRoom {[ProtoMember(1, DataFormat = DataFormat.ZigZag)]public long roomId;[ProtoMember(2, DataFormat = DataFormat.ZigZag)]public long time;[ProtoMember(3, DataFormat = DataFormat.ZigZag)]public long team;[ProtoMember(4, DataFormat = DataFormat.ZigZag)]public long playerId;[ProtoMember(5)]public string sharetoken;[ProtoMember(6)]public string usertoken;[ProtoMember(7)]public string world;[ProtoMember(8)]public List<UserInfo> tankPlayerList;}[ProtoContract]
public class UserIds {[ProtoMember(1, DataFormat = DataFormat.ZigZag)]public long[] userIds;
}[ProtoContract]
public class UserInfos
{[ProtoMember(1, DataFormat = DataFormat.ZigZag)]public UserInfo[] users;
}[ProtoContract]
public class HeartBeat
{[ProtoMember(1, DataFormat = DataFormat.ZigZag)]public int beat;
}

(2)用于多人同步的具体实现类:SyncTransform.cs

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UTNET;[DisallowMultipleComponent]
public class SyncTransform : SyncBase
{public  SyncTransfomData  data = new SyncTransfomData();public bool isRole;//判断是否是角色的位置同步bool isFirst = true; //第一次同步不用插值private void Awake() {SyncTransfomUtil.FromTransform(transform,ref data);EventManager.Instance.AddListener(EventName.OnEnterRoom, OnEnterRoom);}private void OnEnterRoom(object sender, EventArgs e) {AddNetWorkListner<SyncTransfomData>(OnSyncNet, nameof(SyncTransfomData));OnSyncWorldData();}// Start is called before the first frame updatevoid Start(){if (syncId == string.Empty) {Debug.LogError("syncId null");return;}AddNetWorkListner<SyncTransfomData>(OnSyncNet, nameof(SyncTransfomData));//OnSyncWorldData();}void OnSyncWorldData() {var syncdata = SyncWorldModel.Instance.GetTransfomData(syncId);if (syncdata != null) {OnSyncNet(syncdata);SyncTransfomUtil.ToTransform(transform, syncdata);}}public void InitLocation(SyncTransfomData syncdata) {data = syncdata;SyncTransfomUtil.ToTransform(transform, data);}public void SetPosAndSync(SyncTransfomData syncdata) {data = syncdata;SyncTransfomUtil.ToTransform(transform, data);SendNetWork<SyncTransfomData>(data, nameof(SyncTransfomData));SyncWorldModel.Instance.SetTransfomData(syncId, data);SyncWorldModel.Instance.SyncWorldData();}private void OnSyncNet(SyncTransfomData syData) {data = syData;}//private void OnMouseDrag() {//    var dstRay = Camera.main.ScreenPointToRay(Input.mousePosition);//    transform.forward = -Camera.main.transform.forward;//    transform.position = Camera.main.transform.position + Vector3.Normalize(dstRay.direction) * 5f;//    transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, 0);//}void SendPlayerLocation(SyncTransfomData data, string stName) {if (dataName != stName) {Debug.LogError("error:传输数据出错请检查");return;}dataBase.syncId = syncId;dataBase.name = dataName;dataBase.obj = JsonUtility.ToJson(data);dataBase.isLocation = true;dataBase.playerId = PlayerData.Instance.PlayerId.ToString();NetManager.Instance.SendFrameLocationData(data, dataBase);}//bool TransformEqual(int[] arryInt) {//    transform.position//}// Update is called once per framevoid Update(){var posV3 = new Vector3((float)data.pos[0] / 10f, (float)data.pos[1] / 10f, (float)data.pos[2] / 10f);var rotateV3 = new Vector3((float)data.roate[0] / 10f, (float)data.roate[1] / 10f, (float)data.roate[2] / 10f);if(ownerState == OwnerState.Ohter) {if (SyncTransfomUtil.V3Equal(transform.position, posV3) == false){if (isFirst){transform.position = posV3;}else if (Vector3.Distance(transform.position, posV3) < 0.5f){transform.position = posV3;}else{transform.position = Vector3.Lerp(transform.position, posV3, 5f * Time.deltaTime);}isFirst = false;}if (SyncTransfomUtil.V3Equal(transform.eulerAngles, rotateV3) == false) {transform.eulerAngles = rotateV3;}}else if (ownerState == OwnerState.Mine){var scaleV3 = new Vector3((float)data.scale[0] / 10f, (float)data.scale[1] / 10f, (float)data.scale[2] / 10f);if (SyncTransfomUtil.V3Equal(posV3, transform.position) &&SyncTransfomUtil.V3Equal(rotateV3, transform.eulerAngles) &&SyncTransfomUtil.V3Equal(scaleV3, transform.localScale)) {return;}SyncTransfomUtil.FromTransform(transform, ref data);if (isRole == false) { //如果不是角色就正常同步base.SendNetWork<SyncTransfomData>(data, nameof(SyncTransfomData));SyncWorldModel.Instance.SetTransfomData(syncId, data);SyncWorldModel.Instance.SyncWorldData();}else {SendPlayerLocation(data, nameof(SyncTransfomData));}}}}

(3)用于状态同步的具体实现类:SyncState.cs

using System;
using UnityEngine;
using UnityEngine.Events;
[DisallowMultipleComponent]
public class SyncState : SyncBase {public SyncStateData data = new SyncStateData();public UnityEvent<SyncStateData> onNetSyncEven = new UnityEvent<SyncStateData>();public UnityEvent<SyncStateData> onLoginSyncEven = new UnityEvent<SyncStateData>();bool isInitOk;private void Awake() {EventManager.Instance.AddListener(EventName.OnEnterRoom,OnEnterRoom);}// Start is called before the first frame updatevoid Start() {if (syncId == string.Empty) {Debug.LogError("syncId null");return;}AddNetWorkListner<SyncStateData>(OnSyncNet, nameof(SyncStateData));}private void OnEnterRoom(object sender, EventArgs e) {AddNetWorkListner<SyncStateData>(OnSyncNet, nameof(SyncStateData));OnSyncWorldData();//如果是创建出来的物体,不是在场景中的,那么就需要在start的时候去处理}void OnSyncWorldData() {if (isInitOk == true) {return;}var syncdata = SyncWorldModel.Instance.GetStateData(syncId);if (syncdata != null) {Debug.Log($"OnSyncWorldData {syncId} {syncdata.stateName}");data = syncdata;onLoginSyncEven?.Invoke(syncdata);isInitOk = true;}}private void OnSyncNet(SyncStateData syData) {onNetSyncEven?.Invoke(syData);}public void SendState(string stateName) {if (ownerState ==  OwnerState.Ohter) {return;}data.stateName = stateName;SendNetWork<SyncStateData>(data, nameof(SyncStateData));SyncWorldModel.Instance.SetStateData(syncId,data);SyncWorldModel.Instance.SyncWorldData();}//float timeadd = 0f;//private void Update() {//    timeadd += Time.deltaTime;//    if (timeadd >3) {//        timeadd = 0f;//        //data.stateName = "Jump";//        //onNetSyncEven?.Invoke(data);//        GetComponentInChildren<Animator>().SetTrigger("Jump");//    }//}}

(4)状态同步的做法:本人

(5)其他人:

private void OnNetSync(SyncStateData data)
{var splits = data.stateName.Split(';');var protoName = splits[0];var userId = long.Parse(splits[1]);if (protoName == "openVioce"){if (userId == this.roleId){this.isShowRoleUIVoice(true);}}if (protoName == "closeVioce"){if (userId == this.roleId){this.isShowRoleUIVoice(false);}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/776408.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt定时器类QTimer

参考原文链接&#xff1a;https://blog.csdn.net/weixin_43780415/article/details/131389737 Qt定时器类QTimer是一个用于重复执行或延迟执行函数的类。它可以在一定时间间隔内发送一个信号&#xff0c;也可以在指定的时间后发送一个信号。QTimer是一个基于事件的定时器&#…

鸿蒙OS开发实例:【工具类封装-页面路由】

import common from ohos.app.ability.common; import router from ohos.router 封装app内的页面之间跳转、app与app之间的跳转工具类 【使用要求】 DevEco Studio 3.1.1 Release api 9 【使用示例】 import MyRouterUtil from ../common/utils/MyRouterUtil MyRouterUtil…

giteed的使用

1. 将工作区的内容添加到暂存区 你的工作区要有内容&#xff08;.git 不算&#xff09; 注意&#xff1a;空文件可以添加&#xff0c;但是空文件夹不管 如果没有形成历史版本之前&#xff0c;暂存区的同名文件会被覆盖 //打开命令行&#xff0c;切换到 .git所在的目录&…

JVM常见垃圾收集算法

JVM常见垃圾收集算法 标记-清除算法复制算法标记-整理&#xff08;标记压缩&#xff09;算法分代收集算法新生代和老年代分代收集算法工作机制 面试题&#xff1a;为什么分代收集算法把堆分成年轻代和老年代&#xff1f; 标记-清除算法 最基础的算法&#xff0c;分标记和清除两…

红队笔记8-CTF5打靶流程-CMS漏洞-多用户信息泄露(vulnhub)

目录 开头: 1.主机发现和端口扫描&#xff1a; 2.80端口-NanoCMS哈希密码信息泄露-后台getshell 3.提权-用户过多信息泄露 4.总结&#xff1a; 开头: 学习的视频是哔哩哔哩红队笔记&#xff1a; 「红队笔记」靶机精讲&#xff1a;LAMPSecurityCTF5 - 标准攻击链&#xff…

ByteTrack多目标跟踪——yolox_model代码详解

文章目录 yolox_modelYOLOPAFPNYOLOXHeadmodel损失计算初步筛选SimOTA 求解 附&#xff1a;网络结构Cls headCls_convsCls_preds Reg headReg_convsReg_preds Obj headObj_preds yolox_model yolox_model主要包括以下几个文件:yolox.py、yolo_pafpn.py以及yolo_head.py train时…

[AIGC] MySQL存储引擎详解

MySQL 是一种颇受欢迎的开源关系型数据库系统&#xff0c;它的强大功能、灵活性和开放性赢得了用户们的广泛赞誉。在 MySQL 中&#xff0c;有一项特别重要的技术就是存储引擎。在本文中&#xff0c;我们将详细介绍什么是存储引擎&#xff0c;以及MySQL中常见的一些存储引擎。 文…

申请GeoTrust数字证书

GeoTrust介绍&#xff1a; 大家应该都不陌生&#xff0c;作为最老资格的一批国际大牌证书&#xff0c;GeoTrust的品牌效益和使用群体非常庞大。在数字证书领域也是当之无愧的龙头地位&#xff0c;作为Symantec和Digicert的子品牌&#xff0c;证书安全性能方面毋庸置疑&#xf…

IP SSL证书注册流程

使用IP地址申请SSL证书&#xff0c;需要用公网IP地址申请&#xff0c;申请之前确保直接的IP地址可以开放80或者443端口两者选择1个就好&#xff0c;端口不需要一直开放&#xff0c;只要认证的几分钟内开放就可以了&#xff0c;然后IP地址根目录可以上传txt文件。 IP SSL证书认…

Codeforces Round 800 (Div. 1)C. Keshi in Search of AmShZ 反向dijkstra,并附带权值

Problem - C - Codeforces 目录 题意&#xff1a; 思路&#xff1a; 答疑&#xff1a; 1.为什么反向做呢&#xff1f; 2.为什么是到达点的剩余度数呢&#xff1f; 3.相同路是否可以去重&#xff0c;用个set&#xff1f; 4.如果有多条路相同呢&#xff1f; 参考代码&am…

【SecretFlow——SPU基础】

1.SPU基础 SPU设备在SecretFlow中负责执行MPC计算。 2.代码解读 2.1 创建设备 import secretflow as sf # 如果存在secretflow&#xff0c;先关闭已经存在的环境 sf.shutdown() # 初始化四个参与方 sf.init([alice, bob, carol, dave], addresslocal) # 寻找未占用的端口来…

【YOLOV5 入门】——detect.py简单解析模型检测基于torch.hub的检测方法

声明&#xff1a;笔记是毕设时根据B站博主视频学习时自己编写&#xff0c;请勿随意转载&#xff01; 一、打开detect.py&#xff08;文件解析&#xff09; 打开上节桌面创建的yolov5-7.0文件夹里的detect.py文件&#xff08;up主使用的是VScode&#xff0c;我这里使用pycharm…

NLP深入学习:结合源码详解 BERT 模型(三)

文章目录 1. 前言2. 预训练2.1 modeling.BertModel2.1.1 embedding_lookup2.1.2 embedding_postprocessor2.1.3 transformer_model 2.2 get_masked_lm_output2.3 get_next_sentence_output2.4 训练 3. 参考 1. 前言 前情提要&#xff1a; 《NLP深入学习&#xff1a;结合源码详…

PyQt5开发——QCheckBox 复选框用法与代码示例

1. 复选框 QCheckBox 是 Qt 框架中的一个控件&#xff0c;用于在界面中表示一个可以被选中或取消选中的复选框。它通常用于允许用户在多个选项之间进行选择。在 Python 中使用 PyQt 或 PySide 开发 GUI 应用程序时&#xff0c;可以使用 QCheckBox 控件来实现复选框。 2.基本用…

[ Linux ] git工具的基本使用(仓库的构建,提交)

1.安装git yum install -y git 2.打开Gitee&#xff0c;创建你的远程仓库&#xff0c;根据提示初始化本地仓库&#xff08;这里以我的仓库为例&#xff09; 新建好仓库之后跟着网页的提示初始化便可以了 3.add、commit、push三板斧 git add . //add仓库新增&#xff08;变…

企业数字化转型:聊聊数据思维!

笔者曾在《深入聊一聊企业数字化转型这个事儿》 一文中给出了数字化转型的定义&#xff0c;即&#xff1a;通过应用数字化技术来重塑企业的信息化环境和业务过程。本质上来讲&#xff0c;企业数字化转型&#xff0c;不仅是技术方面的升级&#xff0c;更是企业文化、思维方式的转…

【计算机考研】408到底有多难?

你真以为大家是学不会408吗&#xff1f; 不是&#xff01;单纯是因为时间不够&#xff01;&#xff01;&#xff01; 再准确一些就是不会分配时间 408的知识其实并不难&#xff0c;要说想上130那确实有难度&#xff0c;但是100在时间充裕的情况下还是可以做到的 我本人是双…

非wpf应用程序项目【类库、用户控件库】中使用HandyControl

文章速览 前言参考文章实现方法1、添加HandyControl包;2、添加资源字典3、修改资源字典内容坚持记录实属不易,希望友善多金的码友能够随手点一个赞。 共同创建氛围更加良好的开发者社区! 谢谢~ 前言 wpf应用程序中,在入口项目中存在App.xaml文件,在这个文件中加上对各个…

Linux之进程控制进程终止进程等待进程的程序替换替换函数实现简易shell

文章目录 一、进程创建1.1 fork的使用 二、进程终止2.1 终止是在做什么&#xff1f;2.2 终止的3种情况&&退出码的理解2.3 进程常见退出方法 三、进程等待3.1 为什么要进行进程等待&#xff1f;3.2 取子进程退出信息status3.3 宏WIFEXITED和WEXITSTATUS&#xff08;获取…

全球首位AI程序员Devin诞生,以此谈谈AI对程序员的影响

一、简介 全球首位 AI 程序员 Devin 是由初创公司 Cognition AI 创造的。这家公司成立仅四个月&#xff0c;却已经引起了广泛关注。 Devin作为人工智能的代表&#xff0c;将展示出人工智能在编程领域的潜力和能力&#xff0c;激发程序员探索和应用人工智能技术的兴趣。这将可…