报错修改

This commit is contained in:
2025-07-02 10:05:26 +08:00
parent c2bf9196b8
commit ec8b2f7e25
405 changed files with 16738 additions and 1883 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 45d07e1bb40ffcf4c8d9dc0cc5c31331
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 73f0ed03caf557d42ab55a0979f6c452
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a15f3bb70d00bcd4b8c01eb4ace17c3b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,52 @@
using System;
using UnityEngine;
namespace Stary.Evo
{
public class AppConfig
{
/// <summary>
/// package name
/// </summary>
public static string PackageDomainName { get; set; }
public static string IpConfig { get; set; }
public static string UserName { get; set; }
public static string PassWord { get; set; }
public static string ProductName { get; set; }
public static string Platform { get; set; }
public static string MainDomainVersion { get; set; }
/// <summary>
/// 主场景main实例物体
/// </summary>
private static GameObject _MainBaseModel;
/// <summary>
/// 赋值默认的实例
/// </summary>
public static void SetDefaultMainInstance(GameObject mainbase)
{
_MainBaseModel = mainbase;
}
/// <summary>
/// 赋值默认的实例
/// </summary>
public static GameObject GetDefaultMainInstance()
{
return _MainBaseModel;
}
}
public class GlobalConfig
{
public const string RikidHandLeft = "LeftHandRender/RKHandVisual/Hand_L/left_wrist/left_palm";
public const string RikidHandRight = "RightHandRender/RKHandVisual/Hand_R/right_wrist/right_palm";
public const string RikidHandRightIndexTip = "RightHandRender/RKHandVisual/Hand_R/right_wrist/right_index_metacarpal/right_index_proximal/right_index_intermediate/right_index_distal/right_index_tip";
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 43c9d32ba5934c65bf80d76e35074a1a
timeCreated: 1741162242

View File

@@ -0,0 +1,83 @@
using System;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using Main;
using Stary.Evo.AudioCore;
using UnityEngine;
using UnityEngine.Events;
using YooAsset;
namespace Stary.Evo
{
/// <summary>
/// 热更基类,应该继承的基类
/// </summary>
public class DomainBase : MonoBehaviour
{
public string DomainName { protected get; set; }
public Transform TransformInfo;
protected bool isExit { private get; set; }
/// <summary>
/// 触发Domain时调用该方法
/// </summary>
/// <param name="param"></param>
public virtual void OnEnter(string param)
{
TransformInfo = transform.Find("TransformInfo");
}
/// <summary>
/// Domain被关闭时会调该方法
/// </summary>
/// <param name="param"></param>
public virtual void OnExit()
{
isExit = true;
AudioCoreManager.StopMusic();
}
public virtual async Task OnEnterAsync(string param)
{
isExit = true;
}
public virtual Task OnExitAsync()
{
return Task.CompletedTask;
}
private async void OnDestroy()
{
await ForceUnloadAllAssets();
}
// 强制卸载所有资源包,该方法请在合适的时机调用。
// 注意Package在销毁的时候也会自动调用该方法。
private async UniTask ForceUnloadAllAssets()
{
var package = YooAssets.TryGetPackage(DomainName);
if (package != null)
{
var operation = package.UnloadAllAssetsAsync();
await operation;
await package.DestroyAsync();
YooAssets.RemovePackage(DomainName);
Resources.UnloadUnusedAssets();
GC.Collect();
GC.WaitForPendingFinalizers();
Debug.Log($"UnityEvo:{DomainName} 资源包已卸载...");
}
else
{
Debug.LogWarning($"UnityEvo:{DomainName} 资源包不存在,请检查是否已经卸载还是卸载异常...");
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8923efe3eb184e7f9283f71e2dc4ea10
timeCreated: 1742540500

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 555a223b39dcb90489791ddba86221e6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,136 @@
using System.Collections.Generic;
using UnityEngine;
using YooAsset;
namespace Main
{
/// <summary>
/// 类对象池
/// </summary>
public interface IClassPool
{
/// <summary>
/// 将类对象存进池里
/// </summary>
/// <param name="obj"></param>
void Creation(GameObject obj);
/// <summary>
/// 从未激活池里面取类对象,并放入激活池
/// </summary>
/// <returns></returns>
GameObject Spawn(Transform parent);
/// <summary>
/// 从激活池中释放类对象到未激活池中
/// </summary>
GameObject RecycleSpawn(GameObject obj);
/// <summary>
/// 回收类对象
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
/// <summary>
/// 清空类对象池
/// </summary>
void RecycleAll();
int GetPolLength();
}
public class ClassObjectPool:IClassPool
{
private AssetHandle poolObject;
/// <summary>
/// 回收对象的父物体
/// </summary>
private Transform recycle;
protected List<GameObject> activepool = new List<GameObject>();
protected Stack<GameObject> inactivepool = new Stack<GameObject>();
//没有回收的个数
protected int noRecycleCount;
public ClassObjectPool(AssetHandle poolObject,Transform recycle)
{
this.poolObject = poolObject;
this.recycle = recycle;
}
/// <summary>
/// 将类对象存进池里
/// </summary>
/// <param name="obj"></param>
public void Creation(GameObject obj)
{
inactivepool.Push(obj);
noRecycleCount++;
}
/// <summary>
/// 从未激活池里面取类对象,并放入激活池
/// </summary>
/// <param name="createIfPoolEmpty">如果为空是否new出来</param>
public GameObject Spawn(Transform parent)
{
GameObject obj = null;
if(noRecycleCount>0)
{
obj = inactivepool.Pop();
obj.transform.SetParent(parent);
noRecycleCount--;
if(obj==null)
Debug.LogErrorFormat("对象池中不存在此对象【{0}】请排查代码", obj);
}
else
{
obj= poolObject.InstantiateSync(parent);
}
obj.transform.localPosition = Vector3.zero;
obj.transform.localRotation = Quaternion.identity;
obj.transform.localScale = Vector3.one;
obj.SetActive(true);
activepool.Add(obj);
return obj;
}
/// <summary>
/// 从激活池中释放类对象到未激活池中
/// </summary>
public GameObject RecycleSpawn(GameObject obj)
{
if(obj!=null&&activepool.Contains(obj))
{
activepool.Remove(obj);
inactivepool.Push(obj);
obj.transform.parent = recycle;
obj.gameObject.SetActive(false);
noRecycleCount++;
}
return null;
}
/// <summary>
/// 清空类对象池
/// </summary>
public void RecycleAll()
{
noRecycleCount=0;
foreach (var pool in inactivepool)
{
GameObject.Destroy(pool);
}
inactivepool.Clear();
foreach (var pool in activepool)
{
GameObject.Destroy(pool);
}
activepool.Clear();
}
public int GetPolLength()
{
return inactivepool.Count;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a4fc6b70fe91726439cc0d19d7e92edc
timeCreated: 1626164240

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0346f4dcafef4fe7b87b5db6881b56e1
timeCreated: 1741164620

View File

@@ -0,0 +1,19 @@
using Main;
namespace Stary.Evo
{
public class FsmLoadSystem : FsmSystem , IFsmSystem
{
private OpenDomainType OpenDomainType { get; set; }
public ProgressBarPanel ProgressBarPanel { get; set; }
public void SetOpenDomainType(OpenDomainType type)
{
this.OpenDomainType = type;
}
public OpenDomainType GetOpenDomainType()
{
return this.OpenDomainType;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1f012ae5832b467a802b8d775b1dae1e
timeCreated: 1744710396

View File

@@ -0,0 +1,259 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Cysharp.Threading.Tasks;
using HybridCLR;
using UnityEngine;
using YooAsset;
namespace Stary.Evo
{
public class HotFixState : AbstractFSMIState
{
public string[] PatchedAOTAssemblyList = new string[]
{
"System.Core.dll",
"UnityEngine.CoreModule.dll",
"mscorlib.dll",
"DOTween.dll",
"InformationSave.RunTime.dll",
"UIFarme.RunTime.dll",
"UniTask.dll",
"YooAsset.dll",
"com.stary.evo.runtime.dll"
};
public HotFixState(IFsmSystem system) : base(system)
{
}
public override async UniTask OnEnterAsync<T>(T param)
{
DomainConfig domainConfig = param as DomainConfig;
Debug.Log("UnityEvo:热更脚本...");
var package = YooAssets.GetPackage(AppConfig.PackageDomainName);
// Editor环境下HotUpdate.dll.bytes已经被自动加载不需要加载重复加载反而会出问题。
// 添加类型存在性检查
string fullClassName = $"{domainConfig.@namespace}.{domainConfig.className}";
#if EDITOR_SIMULATEMODE
// Editor下无需加载直接查找获得HotUpdate程序集
Assembly hotUpdateAss = System.AppDomain.CurrentDomain.GetAssemblies()
.First(a => a.GetName().Name == $"HotUpdate_{AppConfig.PackageDomainName}");
#else
string hotUpdateAssemblyName = $"HotUpdate_{AppConfig.PackageDomainName}";
Type assemblyType = IsAssetLoaded(fullClassName);
if (assemblyType!=null)
{
Debug.Log($"UnityEvo:热更程序集:{hotUpdateAssemblyName} 已经加载过了");
FsmSystem.SetCurState(nameof(LoadResState), domainConfig, assemblyType);
return;
}
//判断DLL是否下载成功
foreach (var aot in PatchedAOTAssemblyList)
{
var aotName = $"Android_{aot}";
var handle = package.LoadAssetAsync<TextAsset>(aotName);
await handle;
if (handle.Status == EOperationStatus.Succeed)
{
var assetObj = handle.AssetObject as TextAsset;
_sAssetDatas.Add(assetObj);
Debug.Log($"UnityEvo:dll:{aotName} ,下载成功");
}
else
{
Debug.Log($"UnityEvo:dll:{aotName} ,下载失败");
}
}
//先补元数据
LoadMetadataForAOTAssemblies();
// 加载热更dll
var hotfixDll = package.LoadAssetAsync<TextAsset>($"Android_HotUpdate_{AppConfig.PackageDomainName}.dll");
await hotfixDll;
var hotfixPdb = package.LoadAssetAsync<TextAsset>($"Android_HotUpdate_{AppConfig.PackageDomainName}.pdb");
await hotfixPdb;
Assembly hotUpdateAss = null;
if (hotfixDll.Status == EOperationStatus.Succeed)
{
var hotfixDllAsset = hotfixDll.AssetObject as TextAsset;
if (hotfixPdb.Status == EOperationStatus.Succeed)
{
Debug.Log($"UnityEvo:dll:加载成功!!");
var hotfixPdbAsset = hotfixPdb.AssetObject as TextAsset;
hotUpdateAss = Assembly.Load(hotfixDllAsset.bytes, hotfixPdbAsset.bytes);
Debug.Log($"UnityEvo:dll: Assembly.Load加载成功");
}
else
{
if (hotfixDllAsset != null)
hotUpdateAss = Assembly.Load(hotfixDllAsset.bytes);
}
}
else
{
Debug.LogError($"UnityEvo:Android_HotUpdate_{AppConfig.PackageDomainName}.dll加载失败");
}
#endif
Debug.Log($"UnityEvo:dll: Assembly.Load加载后是否为空:{hotUpdateAss == null}");
if (hotUpdateAss != null)
{
Type type = hotUpdateAss.GetType(fullClassName);
if (type == null)
{
Debug.LogError($"UnityEvo:热更类不存在!程序集: {hotUpdateAss.FullName}\n" +
$"期望类名: {fullClassName}\n" +
$"可用类型列表:{string.Join("\n", hotUpdateAss.GetTypes().Select(t => t.FullName))}");
return;
}
// 添加方法存在性检查
Debug.Log($"UnityEvo:dll:开始MethodInfo检查");
MethodInfo onEnterMethod = type.GetMethod("OnEnter");
MethodInfo onEnterAsyncMethod = type.GetMethod("OnEnterAsync");
if (onEnterMethod == null || onEnterAsyncMethod == null)
{
Debug.LogError($"UnityEvo:热更类进入方法缺失!\n" +
$"存在方法:{string.Join(", ", type.GetMethods().Select(m => m.Name))}");
return;
}
Debug.Log($"UnityEvo:dll:OnEnter检查成功");
MethodInfo onExitMethod = type.GetMethod("OnExit");
MethodInfo onExitAsyncMethod = type.GetMethod("OnExitAsync");
if (onExitMethod == null || onExitAsyncMethod == null)
{
Debug.LogError($"UnityEvo:热更类退出方法缺失!\n" +
$"存在方法:{string.Join(", ", type.GetMethods().Select(m => m.Name))}");
return;
}
Debug.Log($"UnityEvo:dll:OnExit检查成功");
// AppConfig.SetDefaultHotfixType(type);
FsmSystem.SetCurState(nameof(LoadResState), domainConfig, type);
// // 创建热更类实例
// DomainBase hotfixInstance = AppConfig.HOTFIXBASE.AddComponent(type) as DomainBase;
//
// if (hotfixInstance == null)
// {
// Debug.LogError($"热更类{fullClassName}实例创建失败必须继承MonoBehaviour");
// return;
// }
//
//
// // 原有调用逻辑修改为使用实例
// onEnterMethod.Invoke(hotfixInstance, new object[] { "" }); // 第一个参数改为实例对象
// onEnterAsyncMethod.Invoke(hotfixInstance, new object[] { "" });
}
else
{
Debug.LogError($"UnityEvo:热更类不存在!程序集: hotUpdateAss 不存在\n" +
$"期望类名: {fullClassName}\n" +
$"可用类型列表:{string.Join("\n", hotUpdateAss.GetTypes().Select(t => t.FullName))}");
}
}
public override UniTask OnEnterAsync()
{
return UniTask.CompletedTask;
}
public override UniTask OnEnterAsync<T1, T2>(T1 param1, T2 param2)
{
return UniTask.CompletedTask;
}
#region
// //补充元数据dll的列表
// //通过RuntimeApi.LoadMetadataForAOTAssembly()函数来补充AOT泛型的原始元数据
// private List<string> AOTMetaAssemblyFiles { get; } =new()
// {
// "Android_mscorlib.dll", "Android_System.dll", "Android_System.Core.dll",
// };
private readonly List<TextAsset> _sAssetDatas = new List<TextAsset>();
// public byte[] ReadBytesFromStreamingAssets(string dllName)
// {
// if (_sAssetDatas.ContainsKey(dllName))
// {
// return _sAssetDatas[dllName].bytes;
// }
//
// return Array.Empty<byte>();
// }
/// <summary>
/// 为aot assembly加载原始metadata 这个代码放aot或者热更新都行。
/// 一旦加载后如果AOT泛型函数对应native实现不存在则自动替换为解释模式执行
/// </summary>
private void LoadMetadataForAOTAssemblies()
{
HomologousImageMode mode = HomologousImageMode.SuperSet;
// foreach (var aotDllName in AOTGenericReferences.PatchedAOTAssemblyList)
foreach (var aotDll in _sAssetDatas)
{
// var aotName = $"Android_{aotDllName}";
//
// byte[] dllBytes = ReadBytesFromStreamingAssets(aotName);
if (aotDll != null)
{
byte[] dllBytes = aotDll.bytes;
// 添加元数据加载校验
if (dllBytes == null || dllBytes.Length == 0)
{
Debug.LogError($"AOT元数据加载失败{aotDll.name}");
continue;
}
LoadImageErrorCode err = HybridCLR.RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, mode);
Debug.Log($"UnityEvo:【补元】{aotDll.name} 加载结果: {err} 字节数: {dllBytes.Length}");
}
}
}
#endregion
private Type IsAssetLoaded(string assemblyName)
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
Type assemblyType = assembly.GetType(assemblyName);
if (assemblyType!=null)
{
Debug.Log("type:" + assemblyType);
return assemblyType;
}
}
return null;
}
public override void OnUpdate()
{
base.OnUpdate();
}
public override UniTask OnExitAsync()
{
_sAssetDatas.Clear();
return UniTask.CompletedTask;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 693e2ecffc1b43caa7c7818e3aba708c
timeCreated: 1741165298

View File

@@ -0,0 +1,88 @@
using Cysharp.Threading.Tasks;
using Main;
using UnityEngine;
using YooAsset;
namespace Stary.Evo
{
public class LoadResMainState : AbstractFSMIState
{
private string _doMain = "Main";
public LoadResMainState(IFsmSystem system) : base(system)
{
}
public override async UniTask OnEnterAsync()
{
// Debug.Log("加载资源...");
// var package = YooAssets.GetPackage(_doMain);
//
// DomainConfig config = null;
// //加载热更配置文件
// var loadHotfixSettingsOp = package.LoadAssetAsync<DomainConfig>("Config_DomainConfig");
// await loadHotfixSettingsOp;
// if (loadHotfixSettingsOp.Status == EOperationStatus.Succeed)
// {
// //更新成功
// Debug.Log($"UnityEvo:加载主配置文件 loadHotfixSettings : 【成功】");
// config = loadHotfixSettingsOp.AssetObject as DomainConfig;
// }
// else
// {
// Debug.LogError($"UnityEvo:加载主配置文件 loadHotfixSettings : 【失败】");
// }
//
// // 加载热更资源
// var loadOperation = package.LoadAssetAsync<GameObject>(config.mainPrefab);
//
// await loadOperation;
// if (loadOperation.Status == EOperationStatus.Succeed)
// {
// GameObject loadhotfix = loadOperation.InstantiateSync();
// AppConfig.SetDefaultMainInstance(loadhotfix);
// if (loadhotfix != null)
// {
// DomainBase hotfixInstance = loadhotfix.GetComponent<MainDomain>();
// if (hotfixInstance == null)
// {
// hotfixInstance = loadhotfix.AddComponent<MainDomain>();
// }
//
// if (hotfixInstance == null)
// {
// Debug.LogError($"热更类{loadhotfix.name}实例创建失败必须继承MonoBehaviour");
// return;
// }
//
//
// // 原有调用逻辑修改为使用实例
// hotfixInstance.OnEnter("");
// hotfixInstance.OnEnterAsync("");
// }
// }
}
public override UniTask OnEnterAsync<T>(T param)
{
return UniTask.CompletedTask;
}
public override UniTask OnEnterAsync<T1, T2>(T1 param1, T2 param2)
{
return UniTask.CompletedTask;
}
public override void OnUpdate()
{
base.OnUpdate();
}
public override UniTask OnExitAsync()
{
return UniTask.CompletedTask;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c0fed0de80c04e7fb0f513ce21fed7b9
timeCreated: 1744361114

View File

@@ -0,0 +1,144 @@
using System;
using Cysharp.Threading.Tasks;
using Immersal.AR;
using Stary.Evo;
using Stary.Evo.InformationSave;
using UnityEngine;
using UnityEngine.SceneManagement;
using YooAsset;
namespace Stary.Evo
{
public class LoadResState : AbstractFSMIState
{
private DomainConfig.LoadResType loadResType;
public GameObject mainPrefab;
private DomainConfig domainConfig;
public LoadResState(IFsmSystem system) : base(system)
{
}
public override UniTask OnEnterAsync()
{
return UniTask.CompletedTask;
}
public override UniTask OnEnterAsync<T>(T param)
{
return UniTask.CompletedTask;
}
public override async UniTask OnEnterAsync<T1, T2>(T1 param1, T2 param2)
{
Debug.Log("加载资源...");
domainConfig = param1 as DomainConfig;
loadResType = domainConfig.loadResType;
Type type = param2 as Type;
var package = YooAssets.GetPackage(domainConfig.domain);
if (loadResType == DomainConfig.LoadResType.Prefab)
{
// 加载热更资源
var loadOperation = package.LoadAssetAsync<GameObject>(domainConfig.mainPrefab);
await loadOperation;
if (loadOperation.Status == EOperationStatus.Succeed)
{
ARSpace arSpace = GameObject.FindObjectOfType<ARSpace>();
if (arSpace != null)
{
Debug.Log("UnityEvo:找到ARSpace开始加载点云运行环境...");
mainPrefab = loadOperation.InstantiateSync(arSpace.transform);
}
else
{
Debug.Log("UnityEvo:未找到ARSpace开始加载普通运行环境通过语音唤醒...");
mainPrefab = loadOperation.InstantiateSync();
}
if(domainConfig.domain =="Main")
AppConfig.SetDefaultMainInstance(mainPrefab);
}
}
else if (loadResType == DomainConfig.LoadResType.Scene)
{
var sceneMode = domainConfig.loadSceneMode;
var physicsMode = LocalPhysicsMode.None;
SceneHandle handle = package.LoadSceneAsync(domainConfig.mainScene, sceneMode, physicsMode);
await handle;
mainPrefab = GameObject.Find(domainConfig.mainPrefab);
}
LocalTransformInfo info = mainPrefab.GetComponentInChildren<LocalTransformInfo>();
FsmLoadSystem fsmLoadSystem = FsmSystem as FsmLoadSystem;
if (info._list.Count >= 2)
{
if (fsmLoadSystem.GetOpenDomainType() == OpenDomainType.DEFAULT)
{
info.Switch(0);
}
else if (fsmLoadSystem.GetOpenDomainType() == OpenDomainType.VIOICE)
{
info.Switch(1);
}
}
else
{
Debug.LogError($"UnityEvo:{mainPrefab.name}的TransformInfo长度小于2无法继续运行请排查");
}
if (mainPrefab != null)
{
DomainBase hotfixInstance = mainPrefab.GetComponent(type) as DomainBase;
if (hotfixInstance == null)
{
hotfixInstance = mainPrefab.AddComponent(type) as DomainBase;
}
hotfixInstance.DomainName = domainConfig.domain;
if (hotfixInstance == null)
{
Debug.LogError($"热更类{type.Name}实例创建失败必须继承MonoBehaviour");
return;
}
// 原有调用逻辑修改为使用实例
hotfixInstance.OnEnter("");
hotfixInstance.OnEnterAsync("");
}
}
public override void OnUpdate()
{
base.OnUpdate();
}
public override async UniTask OnExitAsync()
{
Debug.Log("UnityEvo:Domain退出...");
if (domainConfig.domain != "Main")
{
DomainBase domainBase = mainPrefab.GetComponent<DomainBase>();
if (domainBase == null)
{
Debug.LogError($"UnityEvo:{mainPrefab.name}的DomainBase为空无法退出请排查");
}
else
{
domainBase.OnExit();
await domainBase.OnExitAsync();
}
if (loadResType == DomainConfig.LoadResType.Scene)
{
await SceneManager.UnloadSceneAsync("server");
}
if (domainBase != null)
GameObject.Destroy(domainBase.gameObject);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f689e40cd4654793a8f1d3ce69ba3532
timeCreated: 1741165763

View File

@@ -0,0 +1,277 @@
using System;
using System.IO;
using Cysharp.Threading.Tasks;
using Main;
using Newtonsoft.Json;
using Stary.Evo.UIFarme;
using UnityEditor;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Networking;
using YooAsset;
using Object = UnityEngine.Object;
namespace Stary.Evo
{
public class ResStartState : AbstractFSMIState
{
public ResStartState(IFsmSystem system) : base(system)
{
}
public override async UniTask OnEnterAsync()
{
Debug.Log("UnityEvo:启动开始资源初始化...");
//初始化读取资源配置表
HotfixMainResDomain hotfixMainResDomain = Resources.Load<HotfixMainResDomain>("HotfixMainResDomain");
if (hotfixMainResDomain != null)
{
AppConfig.IpConfig = hotfixMainResDomain.hotfixMainResDomainEntity.ipconfig;
AppConfig.UserName = hotfixMainResDomain.hotfixMainResDomainEntity.username;
AppConfig.PassWord = hotfixMainResDomain.hotfixMainResDomainEntity.password;
AppConfig.ProductName =Application.identifier;
AppConfig.MainDomainVersion = hotfixMainResDomain.hotfixMainResDomainEntity.mainDomainVersion;
}
Debug.Log($"UnityEvo:读取资源配置表成功...{AppConfig.IpConfig}{AppConfig.UserName}{AppConfig.PassWord}");
// 初始化资源系统
YooAssets.Initialize();
//自定义网络请求器
// 设置自定义请求委托
//YooAssets.SetDownloadSystemUnityWebRequest(NasWebRequester);
//初始化资源加载模块
// 增加包存在性检查
var package = YooAssets.TryGetPackage(AppConfig.PackageDomainName);
if (package == null)
{
Debug.LogWarning($"UnityEvo:资源包 {AppConfig.PackageDomainName} 不存在,正在尝试创建...");
package = YooAssets.CreatePackage(AppConfig.PackageDomainName);
}
YooAssets.SetDefaultPackage(package);
// 初始化资源包
#if EDITOR_SIMULATEMODE
await EDITOR_SIMULATEMODE(package);
FsmSystem.SetCurState(nameof(ResUpdateServerState));
#elif OFFLINE_PLAYMODE
await OFFLINE_PLAYMODE(package);
FsmSystem.SetCurState(nameof(ResUpdateLocalState));
#elif HOST_PLAYMODE
// if (package.PackageName.Equals("Main"))
// {
// await OFFLINE_PLAYMODE(package);
// }
// else
// {
//登录
string url = AppConfig.IpConfig + "/Authentication/login";
bool isLogin = await WebRequestSystem.Login(url, AppConfig.UserName, AppConfig.PassWord);
if (isLogin)
await HOST_PLAYMODE(package);
else
await Chche_PLAYMODE(package);
// }
FsmSystem.SetCurState(nameof(ResUpdateLocalState));
#elif WEB_PLAYMODE
// IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
// var webServerFileSystemParams = FileSystemParameters.CreateDefaultWebServerFileSystemParameters();
// var webRemoteFileSystemParams =
// FileSystemParameters.CreateDefaultWebRemoteFileSystemParameters(remoteServices); //支持跨域下载
//
// var initParameters = new WebPlayModeParameters();
// initParameters.WebServerFileSystemParameters = webServerFileSystemParams;
// initParameters.WebRemoteFileSystemParameters = webRemoteFileSystemParams;
//
// var initOperation = package.InitializeAsync(initParameters);
// await initOperation;
//
// if (initOperation.Status == EOperationStatus.Succeed)
// Debug.Log("UnityEvo:资源包初始化成功!");
// else
// Debug.LogError($"UnityEvo:资源包初始化失败:{initOperation.Error}");
Debug.LogError($"UnityEvo:暂不支持");
#endif
}
public override UniTask OnEnterAsync<T>(T param)
{
return UniTask.CompletedTask;
}
public override UniTask OnEnterAsync<T1, T2>(T1 param1, T2 param2)
{
return UniTask.CompletedTask;
}
public override void OnUpdate()
{
base.OnUpdate();
}
public override UniTask OnExitAsync()
{
return UniTask.CompletedTask;
}
private async UniTask EDITOR_SIMULATEMODE(ResourcePackage package)
{
var initParams=YooAssetFileSystem.EditorSimulateInitializeParameter();
var initialization = package.InitializeAsync(initParams);
await initialization;
if (initialization.Status == EOperationStatus.Succeed)
{
Assert.AreEqual(EOperationStatus.Succeed, initialization.Status);
Debug.Log("UnityEvo:资源包初始化成功!");
}
else
{
Debug.LogError($"UnityEvo:资源包初始化失败:{initialization.Error}");
}
}
private async UniTask Chche_PLAYMODE(ResourcePackage package)
{
Debug.Log("UnityEvo:网络连接不通畅,切换缓存加载!");
var initParams=YooAssetFileSystem.hostInitializeParameter();
var initOperation = package.InitializeAsync(initParams);
await initOperation;
var operation = package.RequestPackageVersionAsync();
await operation;
if (operation.Status == EOperationStatus.Succeed)
{
PlayerPrefs.SetString($"{AppConfig.PackageDomainName}_GAME_VERSION", operation.PackageVersion);
Debug.Log("UnityEvo:从本地缓存中加载资源包,初始化获取版本号成功!");
}else
{
Debug.LogError($"UnityEvo:从本地缓存中加载资源包,初始化获取版本号失败!");
}
if (initOperation.Status == EOperationStatus.Succeed)
Debug.Log("UnityEvo:从本地缓存中资源包,初始化成功!");
else
Debug.LogError($"UnityEvo:从本地缓存中资源包,初始化失败:{initOperation.Error}");
}
private async UniTask OFFLINE_PLAYMODE(ResourcePackage package)
{
var initParams=YooAssetFileSystem.OfflineInitializeParameter();
var initOperation = package.InitializeAsync(initParams);
await initOperation;
var operation = package.RequestPackageVersionAsync();
await operation;
if (operation.Status == EOperationStatus.Succeed)
{
PlayerPrefs.SetString($"{AppConfig.PackageDomainName}_GAME_VERSION", operation.PackageVersion);
Debug.Log("UnityEvo:从本地加载资源包,初始化获取版本号成功!");
}else
{
Debug.LogError($"UnityEvo:从本地加载资源包,初始化获取版本号失败!");
}
if (initOperation.Status == EOperationStatus.Succeed)
Debug.Log("UnityEvo:从本地加载资源包,初始化成功!");
else
Debug.LogError($"UnityEvo:从本地加载资源包,初始化失败:{initOperation.Error}");
}
public async UniTask HOST_PLAYMODE(ResourcePackage package)
{
// 新增平台判断代码
#if UNITY_EDITOR
BuildTarget buildTarget = UnityEditor.EditorUserBuildSettings.activeBuildTarget;
AppConfig.Platform = buildTarget.ToString();
#else
AppConfig.Platform = Application.platform.ToString();
#endif
Debug.Log($"目标平台标识: {AppConfig.Platform}");
// 请求资源版本
string url = $"{AppConfig.IpConfig}/ResDomain/GetResDomainByDomain";
var resDmainRequst = new ResDmainRequst()
{
ProductName = AppConfig.ProductName,
DomainName = AppConfig.PackageDomainName,
Platform = AppConfig.Platform,
};
//获取服务器版本
var resDmainMessageEntity = await WebRequestSystem.Post(url, JsonConvert.SerializeObject(resDmainRequst));
if (resDmainMessageEntity.code == 200)
{
ResDmainResponse resDmainResponse =
JsonConvert.DeserializeObject<ResDmainResponse>(resDmainMessageEntity.data.ToString());
//获取当前版本
var oldVersion = PlayerPrefs.GetString($"{AppConfig.PackageDomainName}_GAME_VERSION");
//版本不一致,开始下载
if (resDmainResponse.PackageVersion != oldVersion)
{
await Download(resDmainResponse.DocumentFileId);
PlayerPrefs.SetString($"{AppConfig.PackageDomainName}_GAME_VERSION",
resDmainResponse.PackageVersion);
}
else //版本一致,加载缓存资源
{
Debug.Log($"UnityEvo:资源版本一致,自动跳过更新...");
}
}
else
{
Debug.LogError($"UnityEvo:获取资源版本失败: 【{resDmainMessageEntity.message}】");
}
var initParams=YooAssetFileSystem.hostInitializeParameter();
// initParameters.CacheFileSystemParameters = cacheFileSystemParams;
var initOperation = package.InitializeAsync(initParams);
await initOperation;
if (initOperation.Status == EOperationStatus.Succeed)
Debug.Log("UnityEvo:从远程加载资源包,初始化成功!");
else
Debug.LogError($"UnityEvo:从远程加载资源包,初始化失败:{initOperation.Error}");
}
public async UniTask Download(string fileId)
{
// 在任意MonoBehaviour或DomainBase派生类中
string loadPath = $"{Application.persistentDataPath}/DownloadedContent/{AppConfig.PackageDomainName}";
if (Directory.Exists(loadPath))
{
Directory.Delete(loadPath, true);
}
FsmLoadSystem loadSystem =FsmSystem as FsmLoadSystem;
if (loadSystem.ProgressBarPanel == null)
{
loadSystem.ProgressBarPanel =Object.Instantiate(Resources.Load<GameObject>("ProgressBarPanel"),
Camera.main.transform).GetOrAddComponent<ProgressBarPanel>();
}
await ZipTool.DownloadAndUnzipAsync(fileId, loadPath, DownLoadProgress, UnzipProgress);
}
private void DownLoadProgress(float progress)
{
Debug.Log($"下载进度:{progress:P0}");
FsmLoadSystem loadSystem =FsmSystem as FsmLoadSystem;
loadSystem.ProgressBarPanel.SetProgressBarValue("下载中", progress);
}
private void UnzipProgress(float progress)
{
Debug.Log($"解压进度:{progress:P0}");
FsmLoadSystem loadSystem =FsmSystem as FsmLoadSystem;
loadSystem.ProgressBarPanel.SetProgressBarValue("解压中", progress);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2462c737fc9c4b53b35557f8a6aac453
timeCreated: 1741165298

View File

@@ -0,0 +1,144 @@
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.Assertions;
using YooAsset;
namespace Stary.Evo
{
public class ResUpdateLocalState : AbstractFSMIState
{
public ResUpdateLocalState(IFsmSystem system) : base(system)
{
}
public override async UniTask OnEnterAsync()
{
//初始化读取资源配置表
var package = YooAssets.GetPackage(AppConfig.PackageDomainName);
//更新失败
Debug.Log($"UnityEvo:切换为加载本地缓存资源...");
string packageVersion = "";
if (package.PackageName.Equals("Main"))
{
var operation = package.RequestPackageVersionAsync();
await operation;
if (operation.Status == EOperationStatus.Succeed)
{
//更新成功
packageVersion = operation.PackageVersion;
Debug.Log($"Unity:【Main】Request package Version : {packageVersion}");
}
else
{
//更新失败
Debug.LogError("Unity:【Main】" + operation.Error);
}
}
else
{
// 获取上次成功记录的版本
packageVersion = PlayerPrefs.GetString($"{AppConfig.PackageDomainName}_GAME_VERSION", string.Empty);
if (string.IsNullOrEmpty(packageVersion))
{
Debug.Log($"UnityEvo:没有找到本地版本记录,需要更新资源!");
return;
}
}
Debug.Log($"UnityEvo:获取资源版本 Version : 【{packageVersion}】");
Debug.Log($"UnityEvo:开始加载本地资源...");
// Assert.AreEqual(EOperationStatus.Succeed, requetVersionOp.Status);
// 加载本地缓存的资源清单文件
var updateManifestOp = package.UpdatePackageManifestAsync(packageVersion);
await updateManifestOp;
if (updateManifestOp.Status == EOperationStatus.Succeed)
{
//更新成功
Debug.Log($"UnityEvo:更新本地资源清单 updateManifest : 【成功】");
}
else
{
//更新失败
Debug.LogError($"UnityEvo:加载本地资源清单文件失败,需要更新资源!: 【{updateManifestOp.Error}】");
return;
}
Assert.AreEqual(EOperationStatus.Succeed, updateManifestOp.Status);
//4.下载补丁包
await Download();
//加载热更配置文件
var loadHotfixSettingsOp = package.LoadAssetAsync<DomainConfig>("Config_DomainConfig");
await loadHotfixSettingsOp;
DomainConfig domainConfig = null;
if (loadHotfixSettingsOp.Status == EOperationStatus.Succeed)
{
//更新成功
Debug.Log($"UnityEvo:加载热更配置文件 DomainConfig : 【成功】");
domainConfig = loadHotfixSettingsOp.AssetObject as DomainConfig;
}
else
{
Debug.LogError($"UnityEvo:加载热更配置文件 DomainConfig : 【失败】");
}
if (domainConfig == null)
{
Debug.LogError($"UnityEvo:【{package.PackageName}】加载DomainConfig为空无法继续执行后续流程请检查");
}
FsmSystem.SetCurState(nameof(HotFixState), domainConfig);
}
public override UniTask OnEnterAsync<T>(T param)
{
return UniTask.CompletedTask;
}
public override UniTask OnEnterAsync<T1, T2>(T1 param1, T2 param2)
{
return UniTask.CompletedTask;
}
public override void OnUpdate()
{
base.OnUpdate();
}
public override UniTask OnExitAsync()
{
return UniTask.CompletedTask;
}
#region
public async UniTask Download()
{
int downloadingMaxNum = 1;
int failedTryAgain = 1;
var package = YooAssets.GetPackage(AppConfig.PackageDomainName);
var downloader = package.CreateResourceDownloader(downloadingMaxNum, failedTryAgain);
// 在正常开始游戏之前,还需要验证本地清单内容的完整性。
if (downloader.TotalDownloadCount > 0)
{
Debug.Log("UnityEvo:资源内容本地并不完整,需要更新资源!");
return;
}
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: eca158039896455dba3ded1eb703a5da
timeCreated: 1742291100

View File

@@ -0,0 +1,193 @@
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.Assertions;
using YooAsset;
namespace Stary.Evo
{
public class ResUpdateServerState : AbstractFSMIState
{
public ResUpdateServerState(IFsmSystem system) : base(system)
{
}
public override async UniTask OnEnterAsync()
{
Debug.Log("UnityEvo:开始资源更新...");
// //初始化读取资源配置表
var package = YooAssets.GetPackage(AppConfig.PackageDomainName);
// 请求资源版本
var requetVersionOp = package.RequestPackageVersionAsync();
await requetVersionOp;
string packageVersion = "";
if (requetVersionOp.Status == EOperationStatus.Succeed)
{
//更新成功
packageVersion = requetVersionOp.PackageVersion;
PlayerPrefs.SetString($"{AppConfig.PackageDomainName}_GAME_VERSION", packageVersion);
Debug.Log($"UnityEvo:获取资源版本 Version : 【{packageVersion}】");
Debug.Log($"UnityEvo:开始加载服务器资源...");
}
else
{
Debug.LogError($"UnityEvo:获取资源版本失败: 【{requetVersionOp.Error}】");
FsmSystem.SetCurState(nameof(ResUpdateLocalState));
return;
}
// Assert.AreEqual(EOperationStatus.Succeed, requetVersionOp.Status);
// 更新资源清单
var updateManifestOp = package.UpdatePackageManifestAsync(packageVersion);
await updateManifestOp;
if (updateManifestOp.Status == EOperationStatus.Succeed)
{
//更新成功
Debug.Log($"UnityEvo:更新资源清单 updateManifest : 【成功】");
}
else
{
//更新失败
Debug.LogError($"UnityEvo:更新资源清单失败: 【{updateManifestOp.Error}】");
}
Assert.AreEqual(EOperationStatus.Succeed, updateManifestOp.Status);
//4.下载补丁包
await Download();
//加载热更配置文件
var loadHotfixSettingsOp = package.LoadAssetAsync<DomainConfig>("Config_DomainConfig");
await loadHotfixSettingsOp;
DomainConfig domainConfig = null;
if (loadHotfixSettingsOp.Status == EOperationStatus.Succeed)
{
//更新成功
Debug.Log($"UnityEvo:加载热更配置文件 loadHotfixSettings : 【成功】");
domainConfig = loadHotfixSettingsOp.AssetObject as DomainConfig;
}
else
{
Debug.LogError($"UnityEvo:加载热更配置文件 loadHotfixSettings : 【失败】");
}
if (domainConfig == null)
{
Debug.LogError($"UnityEvo:【{package.PackageName}】加载DomainConfig为空无法继续执行后续流程请检查");
}
FsmSystem.SetCurState(nameof(HotFixState), domainConfig);
}
public override UniTask OnEnterAsync<T>(T param)
{
return UniTask.CompletedTask;
}
public override UniTask OnEnterAsync<T1, T2>(T1 param1, T2 param2)
{
return UniTask.CompletedTask;
}
public override void OnUpdate()
{
base.OnUpdate();
}
public override UniTask OnExitAsync()
{
return UniTask.CompletedTask;
}
#region
public async UniTask Download()
{
int downloadingMaxNum = 10;
int failedTryAgain = 3;
var package = YooAssets.GetPackage(AppConfig.PackageDomainName);
var downloader = package.CreateResourceDownloader(downloadingMaxNum, failedTryAgain);
//没有需要下载的资源
if (downloader.TotalDownloadCount == 0)
{
Debug.Log("UnityEvo:没有需要下载的资源,跳过更新");
return;
}
//需要下载的文件总数和总大小
int totalDownloadCount = downloader.TotalDownloadCount;
long totalDownloadBytes = downloader.TotalDownloadBytes;
Debug.Log($"UnityEvo:需要下载的资源的个数【{totalDownloadCount}】,需要下载的资源的总大小{totalDownloadBytes / 1024} MB");
//===================================适应新版本YooAsset插件的修改===================================
//注册回调方法
downloader.DownloadErrorCallback = OnDownloadErrorFunction;
downloader.DownloadUpdateCallback = OnDownloadProgressUpdateFunction;
downloader.DownloadFinishCallback = OnDownloadOverFunction;
downloader.DownloadFileBeginCallback = OnStartDownloadFileFunction;
//===================================适应新版本YooAsset插件的修改===================================
//开启下载
downloader.BeginDownload();
await downloader;
//检测下载结果
if (downloader.Status == EOperationStatus.Succeed)
{
//下载成功
Debug.Log("UnityEvo:资源更新完成");
}
else
{
//下载失败
Debug.Log("UnityEvo:资源更新失败");
}
}
//===================================适应新版本YooAsset插件的修改===================================
/// <summary>
/// 开始下载
/// </summary>
private void OnStartDownloadFileFunction(DownloadFileData downloadFileData)
{
Debug.Log(
$"UnityEvo:开始下载:文件名:{downloadFileData.FileName},文件大小:{downloadFileData.FileSize / 1024f / 1024f} MB");
}
/// <summary>
/// 下载完成
/// </summary>
private void OnDownloadOverFunction(DownloaderFinishData downloaderFinishData)
{
Debug.Log("UnityEvo:下载" + (downloaderFinishData.Succeed ? "成功" : "失败"));
}
/// <summary>
/// 更新中
/// </summary>
private void OnDownloadProgressUpdateFunction(DownloadUpdateData downloadUpdateData)
{
Debug.Log(
$"UnityEvo:文件总数:{downloadUpdateData.TotalDownloadCount},已下载文件数:{downloadUpdateData.CurrentDownloadCount},下载总大小:{downloadUpdateData.TotalDownloadBytes / 1024f / 1024f} MB已下载大小{downloadUpdateData.CurrentDownloadBytes / 1024f / 1024f} MB");
}
/// <summary>
/// 下载出错
/// </summary>
/// <param name="errorData"></param>
private void OnDownloadErrorFunction(DownloadErrorData errorData)
{
Debug.Log($"UnityEvo:下载出错:包名:{errorData.PackageName} 文件名:{errorData.FileName},错误信息:{errorData.ErrorInfo}");
}
//===================================适应新版本YooAsset插件的修改===================================
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bf15fa0d3ca74b0991fd60bef616d007
timeCreated: 1741248693

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using Sirenix.OdinInspector;
using Stary.Evo;
using UnityEngine;
namespace Stary.Evo
{
public class HybridClREntrance : MonoBehaviour
{
private FsmLoadSystem _fsmSystem;
public string domain;
private void Awake()
{
_fsmSystem = new FsmLoadSystem();
// 初始化日志
_fsmSystem.AddState(new ResStartState(_fsmSystem));
_fsmSystem.AddState(new ResUpdateServerState(_fsmSystem));
_fsmSystem.AddState(new ResUpdateLocalState(_fsmSystem));
_fsmSystem.AddState(new HotFixState(_fsmSystem));
_fsmSystem.AddState(new LoadResState(_fsmSystem));
//_fsmSystem.AddState(new LoadResMainState(_fsmSystem));
}
private void Start()
{
#if !UNITY_EDITOR
domain = "Main";
#endif
_fsmSystem.SetOpenDomainType(OpenDomainType.DEFAULT);
AppConfig.PackageDomainName = domain;
_fsmSystem.SetCurState(nameof(ResStartState));
}
public void OpenDomain()
{
AppConfig.PackageDomainName = domain;
_fsmSystem.SetCurState(nameof(ResStartState));
}
public void OpenDomain(string domain, OpenDomainType openDomainType)
{
_fsmSystem.SetOpenDomainType(openDomainType);
AppConfig.PackageDomainName = domain;
_fsmSystem.SetCurState(nameof(ResStartState));
}
public void CloseDomain()
{
_fsmSystem.SetCurState(nameof(DefaultState));
}
private void Update()
{
_fsmSystem.CurState.OnUpdate();
}
}
/// <summary>
/// 打开的项目类型
/// DEFAULT:默认打开方式
/// VIOICE:语音打开方式
/// </summary>
public enum OpenDomainType
{
DEFAULT,
VIOICE
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 467bc10b56c272043a89b12cbf23b3ef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 40e68a50876f42e3ad8ecec2e4d36aa3
timeCreated: 1745896633

View File

@@ -0,0 +1,208 @@
using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using Stary.Evo;
using UnityEngine;
using YooAsset;
namespace Main
{
public interface IAudioTableData : IData
{
UniTask LoadData(string audiotabledata_asset, string uitabledata_asset);
Stary.Evo.TableTextConversion.AudioTableData.MessageInfo GetAudioInfo(string auid);
UniTask<AudioClip> GetAudioClip(string auid);
AudioTableData.AudioToUITableData GetAudioToUIInfo(string auid);
UniTask<AudioTableData.AudioToUIData> GetAudioClipToUISprite(string auid);
Stary.Evo.TableTextConversion.UITableData.MessageInfo GetUIInfo(string uiid);
UniTask<Sprite> GetUISprite(string uiid);
}
public class AudioTableData : AbstractData, IAudioTableData
{
private Stary.Evo.TableTextConversion.AudioTableData audioTableData;
private Stary.Evo.TableTextConversion.UITableData uiTableDatas;
private CancellationTokenSource _cancellationTokenSource;
protected override async void OnInit()
{
}
public async UniTask LoadData(string audiotabledata_asset, string uitabledata_asset)
{
_cancellationTokenSource = new CancellationTokenSource();
var audioHandle =
YooAssets.LoadAssetAsync<Stary.Evo.TableTextConversion.AudioTableData>(audiotabledata_asset);
await audioHandle.WithCancellation(_cancellationTokenSource.Token);
audioTableData = audioHandle.GetAssetObject<Stary.Evo.TableTextConversion.AudioTableData>();
var UIHandle =
YooAssets.LoadAssetAsync<Stary.Evo.TableTextConversion.UITableData>(uitabledata_asset);
await UIHandle.WithCancellation(_cancellationTokenSource.Token);
uiTableDatas = UIHandle.GetAssetObject<Stary.Evo.TableTextConversion.UITableData>();
}
/// <summary>
/// 获取音频数据
/// </summary>
/// <param name="auid"></param>
/// <returns></returns>
public Stary.Evo.TableTextConversion.AudioTableData.MessageInfo GetAudioInfo(string auid)
{
var info = audioTableData.infos.Find(x => x.auid == auid);
if (info != null && !info.filename.Contains("Audios"))
{
info.filename = "Audios_" + info.filename;
}
return info;
}
/// <summary>
/// 获取音频
/// </summary>
/// <param name="auid"></param>
/// <returns></returns>
public async UniTask<AudioClip> GetAudioClip(string auid)
{
var info=GetAudioInfo(auid);
var handle = YooAssets.LoadAssetAsync<AudioClip>(info.filename);
await handle.WithCancellation(_cancellationTokenSource.Token);
if (handle.Status == EOperationStatus.Succeed)
{
return handle.GetAssetObject<AudioClip>();
}
else
{
Debug.LogError(
$"加载音频失败,错误的id为:{auid},错误的音频名称为:{info.filename},错误的错误信息为:{handle.LastError}");
return null;
}
}
/// <summary>
/// 获取音频数据获取UI数据
/// </summary>
/// <param name="auid"></param>
/// <returns></returns>
public AudioToUITableData GetAudioToUIInfo(string auid)
{
var info=GetAudioInfo(auid);
Stary.Evo.TableTextConversion.UITableData.MessageInfo messageInfo = GetUIInfo(info.uirelated);
if (messageInfo != null)
{
return new AudioToUITableData()
{
audioFileName = info.filename,
UIMessageInfo = messageInfo
};
}
else
{
Debug.Log($"没有找到对应的uiid,错误的id为:{info.uirelated}");
return default;
}
}
/// <summary>
/// 获取音频数据获取UI数据
/// </summary>
/// <param name="auid"></param>
/// <returns></returns>
public async UniTask<AudioToUIData> GetAudioClipToUISprite(string auid)
{
var info=GetAudioToUIInfo(auid);
AudioClip audioClip = await GetAudioClip(auid);
var audioTableData = new AudioToUIData();
if (audioClip != null)
{
audioTableData.audioClip = audioClip;
}
else
{
Debug.LogError($"没有找到对应的auid,错误的id为:{auid}");
}
if (info.UIMessageInfo != null)
{
Sprite sprite = await GetUISprite(info.UIMessageInfo.uiid);
if ( sprite != null)
{
audioTableData.sprite = sprite;
}
else
{
Debug.LogError($"没有找到对应的uiid,错误的id为:{info.UIMessageInfo.uiid}");
}
}
return audioTableData;
}
/// <summary>
/// 获取UI数据
/// </summary>
/// <param name="uiid"></param>
/// <returns></returns>
public Stary.Evo.TableTextConversion.UITableData.MessageInfo GetUIInfo(string uiid)
{
var info = uiTableDatas.infos.Find(x => x.uiid == uiid);
if (info != null && !info.filename.Contains("Sprites_"))
{
info.filename = "Sprites_" + info.filename;
}
return info;
}
/// <summary>
/// 获取UI
/// </summary>
/// <param name="auid"></param>
/// <returns></returns>
public async UniTask<Sprite> GetUISprite(string uiid)
{
var info = GetUIInfo(uiid);
var handle = YooAssets.LoadAssetAsync<Sprite>(info.filename);
await handle.WithCancellation(_cancellationTokenSource.Token);
if (handle.Status == EOperationStatus.Succeed)
{
return handle.GetAssetObject<Sprite>();
}
else
{
Debug.LogError(
$"加载ui失败,错误的id为:{uiid},错误的ui名称为:{info.filename},错误的错误信息为:{handle.LastError}");
return null;
}
}
public override void Dispose()
{
try
{
if (_cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested)
{
_cancellationTokenSource.Cancel();
_cancellationTokenSource.Dispose();
}
}
catch (OperationCanceledException e)
{
Debug.Log("异步取消操作");
}
}
public struct AudioToUITableData
{
public string audioFileName;
public Stary.Evo.TableTextConversion.UITableData.MessageInfo UIMessageInfo;
}
public struct AudioToUIData
{
public AudioClip audioClip;
public Sprite sprite;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 336c695a4299453bb325ceeaaa9bb4ed
timeCreated: 1745736803

View File

@@ -0,0 +1,224 @@
using System;
using System.Collections.Generic;
using System.Threading;
using Cysharp.Threading.Tasks;
using DG.Tweening;
using Stary.Evo;
using Stary.Evo.RKTools;
using UnityEngine;
namespace Main
{
public interface IClickSystem : ISystem
{
void AddClickIntervalEvent(Action callback);
void StartTime();
void EndClick();
void BindClickEvent(List<ClickElementChildren> clickElementChildrens, Action<GameObject> callback);
void ClickBreatheTween(List<ClickElementChildren> tweenList, Transform tweenTarget);
bool IsElementChildrenClick();
}
public class ClickSystem : AbstractSystem, IClickSystem
{
// public Sequence loopTargetTween;
/// <summary>
/// 点击计时器
/// </summary>
public float time;
private CancellationTokenSource tokenSource;
private List<ClickElementChildren> clickElementChildrens;
private Action ClickIntervalEvent;
protected override void OnInit()
{
}
public void StartTime()
{
time = 0;
Update();
}
public void BindClickEvent(List<ClickElementChildren> clickElementChildrens, Action<GameObject> callback)
{
StartTime();
this.clickElementChildrens = clickElementChildrens;
//绑定点击事件
foreach (var child in clickElementChildrens)
{
child.transform.gameObject.ObjectAddTouchEvent(callback);
}
}
public void ClickBreatheTween(List<ClickElementChildren> tweenList, Transform tweenTarget)
{
Debug.Log("UnityEvo: 点击呼吸");
time = 0;
foreach (var tween in tweenList)
{
tween.transform.gameObject.ObjectResumeTouchEvent();
// if (tween.transform == tweenTarget)
// {
// tween.isClick = true;
// }
if (tween.transform == tweenTarget)
{
tween.isClick = true;
tween.transform.GetComponent<Animator>().CrossFade("dianji_idle", 0.2f);
this.SendEvent<ModeType, ClickElementChildren>(ModeType.PLayAudioOrVideo,tween);
tweenTarget.gameObject.ObjectPauseTouchEvent();
// if (loopTargetTween != null)
// {
// loopTargetTween.Kill();
// loopTargetTween = null;
// }
//
// loopTargetTween = DOTween.Sequence();
// loopTargetTween.Append(tweenTarget.DOScale(tweenTarget.lossyScale * 1.3f, 1f));
//
// loopTargetTween.SetLoops(6, LoopType.Yoyo);
// loopTargetTween.OnKill(() =>
// {
// if (tweenTarget != null)
// tweenTarget.DOScale(Vector3.one, 0.3f);
// });
// loopTargetTween.Play();
continue;
}
// tween.transform.DOKill();
// tween.transform.DOScale(Vector3.one * 0.9f, 0.5f);
tween.transform.GetComponent<Animator>().CrossFade("dianji_nood", 1f);
}
}
public void AddClickIntervalEvent(Action callback)
{
this.ClickIntervalEvent = callback;
}
public bool IsElementChildrenClick()
{
//是否全部已经点击
for (int i = 0; i < clickElementChildrens.Count; i++)
{
if (clickElementChildrens[i].isClick == false)
{
return false;
}
}
return true;
}
public void EndClick()
{
// if (loopTargetTween != null)
// {
// loopTargetTween.Kill();
// loopTargetTween = null;
// }
time = 0;
if (tokenSource != null)
{
tokenSource?.Cancel();
tokenSource?.Dispose();
tokenSource = null;
}
foreach (var children in clickElementChildrens)
{
children.transform.gameObject.ObjectRemoveTouchEvent();
children.transform.GetComponent<Animator>().CrossFade("dianji_disappear", 0.2f);
}
}
private async void Update()
{
//TODO暂时禁用
// if (tokenSource != null)
// {
// tokenSource?.Cancel();
// }
//
// tokenSource = new CancellationTokenSource();
// try
// {
// while (time <= 11 && !tokenSource.IsCancellationRequested)
// {
// time += Time.deltaTime;
//
// if (time >= 10)
// {
// Debug.Log("UnityEvo:执行未点击间隔事件");
// time = 0;
// ClickIntervalEvent?.Invoke();
// }
//
// await UniTask.Yield(tokenSource.Token);
// }
// }
// catch (OperationCanceledException e)
// {
// Debug.Log("UnityEvo: 取消任务:" + e);
// }
}
public override void Dispose()
{
if (tokenSource != null)
{
tokenSource?.Cancel();
tokenSource?.Dispose();
tokenSource = null;
}
// if (loopTargetTween != null)
// {
// loopTargetTween.Kill();
// loopTargetTween = null;
// }
}
}
public class ClickElementChildren
{
public bool isClick = false;
public string[] auid;
public string vid;
public Transform transform;
public Transform targetTransform;
public ClickElementChildren(Transform child)
{
this. transform = child;
}
public ClickElementChildren(Transform child, string[] auid, string vid)
{
this.auid = auid;
this.vid = vid;
this. transform = child;
}
public ClickElementChildren(Transform child,Transform targetTransform , string[] auid, string vid)
{
this.auid = auid;
this.vid = vid;
this. transform = child;
this.targetTransform= targetTransform;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 206bc7f4b09d456898aed456017e6529
timeCreated: 1745915168

View File

@@ -0,0 +1,103 @@
using System;
using Cysharp.Threading.Tasks;
using Stary.Evo;
using Stary.Evo.AudioCore;
using Stary.Evo.InformationSave;
using Stary.Evo.UIFarme;
using UnityEngine;
using UnityEngine.UI;
using YooAsset;
namespace Main
{
public class IntroPanel : BasePanel
{
static readonly string path = "IntroPanel";
private Image intro;
private LocalTransformInfo transformInfo;
public IntroPanel()
{
}
public override void Initialize(GameObject panelGo)
{
base.Initialize(panelGo);
intro = activePanel.transform.Find("Intro").GetComponent<Image>();
transformInfo = activePanel.transform.GetComponent<LocalTransformInfo>();
intro.gameObject.SetActive(false);
}
public override void OnEnter()
{
base.OnEnter();
this.RegisterEvent<IntroType, string, string>(IntroType.IntroSprite, OnClickPreviousIntro);
this.RegisterEvent<IntroType, RuntimeAnimatorController, string>(IntroType.IntroAnimation, OnAddAnimation);
this.RegisterEvent<IntroType, string, string>(IntroType.IntroAudio, OnClickPreviousIntroAudio);
}
public override void OnExit(float delay = 0)
{
base.OnExit(delay);
intro.sprite = null;
intro.gameObject.SetActive(false);
this.UnRegisterEvent<IntroType, string, string>(IntroType.IntroSprite, OnClickPreviousIntro);
this.UnRegisterEvent<IntroType, string, string>(IntroType.IntroAudio, OnClickPreviousIntroAudio);
this.UnRegisterEvent<IntroType, RuntimeAnimatorController, string>(IntroType.IntroAnimation, OnAddAnimation);
var animator = intro.transform.GetComponent<Animator>();
if (animator != null)
{
GameObject.Destroy(animator);
}
}
public async void OnAddAnimation(RuntimeAnimatorController animatorController, string desc)
{
var animator = intro.transform.GetOrAddComponent<Animator>();
animator.runtimeAnimatorController = animatorController;
intro.SetNativeSize();
intro.gameObject.SetActive(true);
transformInfo.Set(desc);
}
public async void OnClickPreviousIntro(string spriteName, string desc)
{
var handle= YooAssets.LoadAssetAsync<Sprite>(spriteName);
await handle.Task;
intro.sprite = handle.GetAssetObject<Sprite>();
intro.SetNativeSize();
intro.gameObject.SetActive(true);
transformInfo.Set(desc);
}
public async void OnClickPreviousIntroAudio(string auid, string desc)
{
var info = await this.GetData<IAudioTableData>().GetAudioClipToUISprite(auid);
AudioCoreManager.PlayVoice(new AudioData()
{
clip = info.audioClip,
});
SetIntroSprite(info.sprite, desc);
}
private void SetIntroSprite(Sprite sprite, string desc)
{
intro.sprite = sprite;
intro.SetNativeSize();
intro.gameObject.SetActive(true);
transformInfo.Set(desc);
}
public enum IntroType
{
IntroSprite,
IntroAudio,
IntroAnimation
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2fced65e1b5fa8041a7f8474992e2a5a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,42 @@
using System;
using Cysharp.Threading.Tasks;
using Stary.Evo;
using Stary.Evo.AudioCore;
using Stary.Evo.InformationSave;
using Stary.Evo.UIFarme;
using UnityEngine;
using UnityEngine.UI;
using YooAsset;
namespace Main
{
public class ProgressBarPanel : MonoBehaviour
{
private Image _fill;
private Text _fillText;
private Text _fillMessage;
private void Awake()
{
_fill = this.transform.Find("Fill").GetComponent<Image>();
_fillText = _fill.transform.Find("FillText").GetComponent<Text>();
_fillMessage = _fill.transform.Find("FillMessage").GetComponent<Text>();
_fill.fillAmount = 0;
_fillText.text = "0%";
}
public async void SetProgressBarValue(string message, float value)
{
this.gameObject.SetActive(true);
_fill.fillAmount = value;
_fillMessage.text = message;
_fillText.text = $"{value:P0}";
if (value >= 1)
{
this.gameObject.SetActive(false);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c1795353bee11314b950ef806ab8458f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,45 @@
using Cysharp.Threading.Tasks;
using Stary.Evo;
using YooAsset;
namespace X_04_04
{
public interface IUITableData : IData
{
UniTask LoadData(string audiotabledata_asset);
Stary.Evo.TableTextConversion.UITableData.MessageInfo GetUIInfo(string uiid);
}
public class UITableData : AbstractData, IUITableData
{
private Stary.Evo.TableTextConversion.UITableData uiTableDatas;
protected override async void OnInit()
{
}
public async UniTask LoadData(string audiotabledata_asset)
{
var handle = YooAssets.LoadAssetAsync<Stary.Evo.TableTextConversion.UITableData>(audiotabledata_asset);
await handle.Task;
uiTableDatas = handle.GetAssetObject<Stary.Evo.TableTextConversion.UITableData>();
}
public Stary.Evo.TableTextConversion.UITableData.MessageInfo GetUIInfo(string uiid)
{
var info = uiTableDatas.infos.Find(x => x.uiid == uiid);
if (info != null && !info.filename.Contains("Sprites_"))
{
info.filename="Sprites_"+info.filename;
}
return info;
}
public override void Dispose()
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 31eb4845a899e544dab03b7aba620e01
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using Stary.Evo;
using UnityEngine;
using YooAsset;
namespace Main
{
public interface IVideoTableData : IData
{
UniTask LoadData(string videotabledata_asset);
Stary.Evo.TableTextConversion.VideoTableData.MessageInfo PlayVideoName(string vidid);
}
public class VideoTableData : AbstractData, IVideoTableData
{
private Stary.Evo.TableTextConversion.VideoTableData videoTableDatas;
private CancellationTokenSource _cancellationTokenSource;
protected override async void OnInit()
{
}
public async UniTask LoadData(string videotabledata_asset)
{
_cancellationTokenSource = new CancellationTokenSource();
var handle = YooAssets.LoadAssetAsync<Stary.Evo.TableTextConversion.VideoTableData>(videotabledata_asset);
await handle.WithCancellation(this._cancellationTokenSource.Token);
videoTableDatas = handle.GetAssetObject<Stary.Evo.TableTextConversion.VideoTableData>();
}
public Stary.Evo.TableTextConversion.VideoTableData.MessageInfo PlayVideoName(string vidid)
{
var info = videoTableDatas.infos.Find(x => x.vidid == vidid);
if (info != null && !info.filename.Contains("Video"))
{
info.filename = "Video_" + info.filename;
}
return info;
}
public override void Dispose()
{
try
{
if (_cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested)
{
_cancellationTokenSource.Cancel();
_cancellationTokenSource.Dispose();
}
}
catch (OperationCanceledException e)
{
Debug.Log("异步取消操作");
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2c1c8c62f563482ea249c39b08cab4fa
timeCreated: 1745311080

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b3f64633ec28465bb032f798d31269cf
timeCreated: 1745723917

View File

@@ -0,0 +1,6 @@
public enum ModeType
{
VideoStart,
VideoEnd,
PLayAudioOrVideo
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 83e20a216f634924a360a92115ffe0cd
timeCreated: 1745723981

View File

@@ -0,0 +1,190 @@
using System;
using Cysharp.Threading.Tasks;
using DG.Tweening;
using Stary.Evo;
using Stary.Evo.AudioCore;
using Stary.Evo.UIFarme;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Video;
using YooAsset;
public class VideoPanel : BasePanel
{
private RawImage intro;
private GameObject prospect;
private GameObject bg;
private Animator _animator;
private VideoPlayer videoPlayer;
private AudioSource audioSource;
private RenderTexture renderTexture;
public VideoPanel()
{
}
public override void Initialize(GameObject panelGo)
{
base.Initialize(panelGo);
intro = activePanel.transform.Find("Intro").GetComponent<RawImage>();
prospect = activePanel.transform.Find("prospect").gameObject;
bg = activePanel.transform.Find("bg").gameObject;
videoPlayer = activePanel.GetComponentInChildren<VideoPlayer>();
audioSource = activePanel.GetComponentInChildren<AudioSource>();
_animator = activePanel.GetComponentInChildren<Animator>();
}
public override void OnEnter()
{
base.OnEnter();
this.RegisterEvent<ModeType, VideoInfo>(ModeType.VideoStart, OnStartMove);
this.RegisterEvent<ModeType>(ModeType.VideoEnd, OnStopMove);
}
public override void OnExit(float delay = 0)
{
base.OnExit(delay);
this.UnRegisterEvent<ModeType, VideoInfo>(ModeType.VideoStart, OnStartMove);
this.UnRegisterEvent<ModeType>(ModeType.VideoEnd, OnStopMove);
}
private void OnStartMove(VideoInfo info)
{
if (info.filename.Equals(""))
{
Debug.LogError("UnityEvo视频文件名为空,必须传文件名才能执行");
return;
}
if (info.position != null)
{
activePanel.transform.position = info.position.SetVector3Ctor();
}
if (info.rotation != null)
{
activePanel.transform.rotation = Quaternion.Euler(info.rotation.SetVector3Ctor());
}
if (info.scale != null)
{
activePanel.transform.DOScale(info.scale.SetVector3Ctor(), 0.5f);
}
OnStartMove(info.filename, info.callback, info.isFixedSize);
}
private async void OnStartMove(string fileName, Action callback, bool isFixedSize)
{
if (isFixedSize)
{
_animator.gameObject.SetActive(false);
bg.SetActive(false);
prospect.SetActive(false);
}
else
{
_animator.gameObject.SetActive(true);
bg.SetActive(true);
prospect.SetActive(true);
}
//新增renderTexture创建
if (videoPlayer.isPlaying)
{
videoPlayer.Stop();
//释放renderTexture
if (renderTexture != null)
{
renderTexture.Release();
renderTexture = null;
}
}
var videHandle = YooAssets.LoadAssetAsync<VideoClip>(fileName);
await videHandle.Task;
Debug.Log("UnityEvo开始播放视频:" + fileName);
VideoClip videoClip = videHandle.GetAssetObject<VideoClip>();
renderTexture = new RenderTexture((int)videoClip.width, (int)videoClip.height, 24);
videoPlayer.targetTexture = renderTexture;
intro.texture = renderTexture;
intro.SetNativeSize();
var musicHandle = YooAssets.LoadAssetAsync<AudioClip>(fileName + "_mp3");
await musicHandle.Task;
videoPlayer.source = VideoSource.VideoClip;
videoPlayer.clip = videoClip;
Debug.Log("UnityEvo开始播放视频的音频:" + fileName + "_mp3");
AudioClip audioClip = musicHandle.GetAssetObject<AudioClip>();
audioSource.clip = audioClip;
//等待视频加载完成
videoPlayer.Prepare();
videoPlayer.prepareCompleted += (source) =>
{
_animator.Play("vid_maskAni", 0, 0);
//预加载
if (videoPlayer.isPrepared)
{
videoPlayer.Play();
audioSource.Play();
}
};
videoPlayer.loopPointReached += (source) =>
{
OnStopMove();
callback?.Invoke();
};
videoPlayer.errorReceived += (source, error) => { Debug.LogError($"视频播放失败:{error}"); };
}
private void OnStopMove()
{
Debug.Log("UnityEvo视频播放完成");
AudioCoreManager.SetMusicVolume(new AudioData()
{
fadeDuration = 2f,
volume = 1f,
});
_animator.Play("vid_maskDefault", 0, 0);
if (renderTexture != null)
{
renderTexture.Release();
renderTexture = null;
}
if (audioSource.isPlaying)
{
audioSource.Stop();
}
PanelSystem.PopQueue<VideoPanel>();
}
public struct VideoInfo
{
public string filename;
public Vector3Ctor position;
public Vector3Ctor rotation;
public Vector3Ctor scale;
public Action callback;
public bool isFixedSize;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bb1f78aabccc4287a8df3dfdc5158c7b
timeCreated: 1745723921

View File

@@ -0,0 +1,41 @@
using System;
using System.Threading;
using Stary.Evo;
using Stary.Evo.AudioCore;
using Stary.Evo.UIFarme;
using UnityEngine;
public interface IVideoSystem : ISystem
{
void PlayVideo(VideoPanel.VideoInfo info);
void StopVideo();
}
public class VideoSystem : AbstractSystem, IVideoSystem
{
private CancellationTokenSource _cancellationTokenSource;
protected override void OnInit()
{
_cancellationTokenSource = new CancellationTokenSource();
}
public override void Dispose()
{
}
public async void PlayVideo(VideoPanel.VideoInfo info)
{
AudioCoreManager.SetMusicVolume(new AudioData()
{
fadeDuration = 2f,
volume = 0f,
});
await this.GetSystem<IPanelSystem>().PushQueue<VideoPanel>(AppConfig.GetDefaultMainInstance().transform,"Main");
this.GetSystem<IPanelSystem>().SendPanelEvent(ModeType.VideoStart,info);
}
public void StopVideo()
{
this.GetSystem<IPanelSystem>().SendPanelEvent(ModeType.VideoEnd);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e8c49084e4704cb99a6caae60466e889
timeCreated: 1745724754

View File

@@ -0,0 +1,141 @@
namespace R
{
public class Res
{
public class Main
{
public static class anim
{
public const string vid_maskani_anim = "Anim_vid_maskAni";
public const string vid_maskcontroller_controller = "Anim_vid_maskController";
public const string vid_maskdefault_anim = "Anim_vid_maskDefault";
}
public static class audios
{
public const string au_effect_element_pop_mp3 = "Audios_au_effect_element_pop";
public const string au_x_07_ending1_wav = "Audios_au_X_07_ending1";
public const string au_x_07_ending2_wav = "Audios_au_X_07_ending2";
public const string au_x_07_ending3_wav = "Audios_au_X_07_ending3";
public const string au_x_07_ending4_wav = "Audios_au_X_07_ending4";
}
public static class config
{
public const string domainconfig_asset = "Config_DomainConfig";
}
public static class dll
{
public static class android
{
public const string com_stary_evo_runtime_dll_bytes = "Android_com.stary.evo.runtime.dll";
public const string dotween_dll_bytes = "Android_DOTween.dll";
public const string informationsave_runtime_dll_bytes = "Android_InformationSave.RunTime.dll";
public const string mscorlib_dll_bytes = "Android_mscorlib.dll";
public const string system_core_dll_bytes = "Android_System.Core.dll";
public const string uifarme_runtime_dll_bytes = "Android_UIFarme.RunTime.dll";
public const string unitask_dll_bytes = "Android_UniTask.dll";
public const string unityengine_coremodule_dll_bytes = "Android_UnityEngine.CoreModule.dll";
public const string yooasset_dll_bytes = "Android_YooAsset.dll";
}
}
public static class prefabs
{
public const string guideball_point_prefab = "Prefabs_GuideBall_Point";
public const string guideball_zone_prefab = "Prefabs_GuideBall_Zone";
public const string kkcontroller_prefab = "Prefabs_KKController";
public const string main_prefab = "Prefabs_Main";
public const string progressbarpanel_prefab = "Prefabs_ProgressBarPanel";
public const string videopanel_prefab = "Prefabs_VideoPanel";
public const string watermark_prefab = "Prefabs_Watermark";
}
public static class scenes
{
}
public static class spriteatlas
{
public const string video_spriteatlas = "";
public const string vid_video_mask_spriteatlas = "";
}
public static class sprites
{
public const string ui_video_kepu_png = "Sprites_ui_video_kepu";
public const string ui_x_07_ending1_png = "Sprites_ui_X_07_ending1";
public const string ui_x_07_ending2_png = "Sprites_ui_X_07_ending2";
public const string ui_x_07_ending3_png = "Sprites_ui_X_07_ending3";
public const string ui_zone1_shengmingjiankangqu_png = "Sprites_ui_zone1_shengmingjiankangqu";
public const string ui_zone2_jiankangyingxiangyinsuqu_png = "Sprites_ui_zone2_jiankangyingxiangyinsuqu";
public const string ui_zone3_shengmingfanghuqu_png = "Sprites_ui_zone3_shengmingfanghuqu";
public const string ui_zone4_jiankangsuyangqu_png = "Sprites_ui_zone4_jiankangsuyangqu";
public const string ui_zone5_jiankangshenghuofangshiqu_png = "Sprites_ui_zone5_jiankangshenghuofangshiqu";
public static class video
{
}
public static class vid_video_mask
{
public const string vid_mask_001_png = "vid_video_mask_vid_mask_001";
public const string vid_mask_002_png = "vid_video_mask_vid_mask_002";
public const string vid_mask_003_png = "vid_video_mask_vid_mask_003";
public const string vid_mask_004_png = "vid_video_mask_vid_mask_004";
public const string vid_mask_005_png = "vid_video_mask_vid_mask_005";
public const string vid_mask_006_png = "vid_video_mask_vid_mask_006";
public const string vid_mask_007_png = "vid_video_mask_vid_mask_007";
public const string vid_mask_008_png = "vid_video_mask_vid_mask_008";
public const string vid_mask_009_png = "vid_video_mask_vid_mask_009";
public const string vid_mask_010_png = "vid_video_mask_vid_mask_010";
public const string vid_mask_011_png = "vid_video_mask_vid_mask_011";
public const string vid_mask_012_png = "vid_video_mask_vid_mask_012";
public const string vid_mask_013_png = "vid_video_mask_vid_mask_013";
public const string vid_mask_014_png = "vid_video_mask_vid_mask_014";
public const string vid_mask_015_png = "vid_video_mask_vid_mask_015";
public const string vid_mask_016_png = "vid_video_mask_vid_mask_016";
public const string vid_mask_017_png = "vid_video_mask_vid_mask_017";
public const string vid_mask_018_png = "vid_video_mask_vid_mask_018";
public const string vid_mask_019_png = "vid_video_mask_vid_mask_019";
public const string vid_mask_020_png = "vid_video_mask_vid_mask_020";
public const string vid_mask_021_png = "vid_video_mask_vid_mask_021";
public const string vid_mask_022_png = "vid_video_mask_vid_mask_022";
public const string vid_mask_023_png = "vid_video_mask_vid_mask_023";
public const string vid_mask_024_png = "vid_video_mask_vid_mask_024";
public const string vid_mask_025_png = "vid_video_mask_vid_mask_025";
public const string vid_mask_026_png = "vid_video_mask_vid_mask_026";
public const string vid_mask_027_png = "vid_video_mask_vid_mask_027";
public const string vid_mask_028_png = "vid_video_mask_vid_mask_028";
public const string vid_mask_029_png = "vid_video_mask_vid_mask_029";
public const string vid_mask_030_png = "vid_video_mask_vid_mask_030";
public const string vid_mask_031_png = "vid_video_mask_vid_mask_031";
public const string vid_mask_032_png = "vid_video_mask_vid_mask_032";
public const string vid_mask_033_png = "vid_video_mask_vid_mask_033";
public const string vid_mask_034_png = "vid_video_mask_vid_mask_034";
public const string vid_mask_035_png = "vid_video_mask_vid_mask_035";
public const string vid_mask_036_png = "vid_video_mask_vid_mask_036";
public const string vid_mask_037_png = "vid_video_mask_vid_mask_037";
public const string vid_mask_038_png = "vid_video_mask_vid_mask_038";
public const string vid_mask_039_png = "vid_video_mask_vid_mask_039";
public const string vid_mask_040_png = "vid_video_mask_vid_mask_040";
public const string vid_mask_041_png = "vid_video_mask_vid_mask_041";
public const string vid_mask_042_png = "vid_video_mask_vid_mask_042";
public const string vid_mask_043_png = "vid_video_mask_vid_mask_043";
public const string vid_mask_044_png = "vid_video_mask_vid_mask_044";
public const string vid_mask_045_png = "vid_video_mask_vid_mask_045";
public const string vid_mask_046_png = "vid_video_mask_vid_mask_046";
public const string vid_mask_047_png = "vid_video_mask_vid_mask_047";
public const string vid_mask_048_png = "vid_video_mask_vid_mask_048";
public const string vid_mask_049_png = "vid_video_mask_vid_mask_049";
public const string vid_mask_050_png = "vid_video_mask_vid_mask_050";
public const string vid_mask_051_png = "vid_video_mask_vid_mask_051";
public const string vid_mask_052_png = "vid_video_mask_vid_mask_052";
public const string vid_mask_053_png = "vid_video_mask_vid_mask_053";
public const string vid_mask_054_png = "vid_video_mask_vid_mask_054";
public const string vid_mask_055_png = "vid_video_mask_vid_mask_055";
public const string vid_mask_056_png = "vid_video_mask_vid_mask_056";
public const string vid_mask_057_png = "vid_video_mask_vid_mask_057";
public const string vid_mask_058_png = "vid_video_mask_vid_mask_058";
public const string vid_mask_059_png = "vid_video_mask_vid_mask_059";
public const string vid_mask_060_png = "vid_video_mask_vid_mask_060";
}
}
public static class video
{
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8c2846e6784c72e4186e83c127058adb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dca552217fdddaa4098bbd152e410548
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,88 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public static class BezierCurve
{
// 新增路径生成方法
public static Vector3[] GenerateBezierPath(Vector3[] controlPoints, int segments = 20)
{
Vector3[] path = new Vector3[segments + 1];
for (int i = 0; i <= segments; i++)
{
float t = i / (float)segments;
switch (controlPoints.Length)
{
case 2:
path[i] = LineBezier(controlPoints[0], controlPoints[1], t);
break;
case 3:
path[i] = QuadraticBezier(controlPoints, t);
break;
case 4:
path[i] = CubicBezier(controlPoints, t);
break;
default: // 处理n阶情况
path[i] = BezierInterpolation(controlPoints, t);
break;
}
}
return path;
}
// 一阶贝塞尔(线性插值)
public static Vector3 LineBezier(Vector3 p0, Vector3 p1, float t)
{
return Vector3.Lerp(p0, p1, t);
}
// 二阶贝塞尔曲线
public static Vector3 QuadraticBezier(Vector3[] points, float t)
{
if (points.Length != 3) Debug.LogError("需要3个控制点");
float u = 1 - t;
return
u * u * points[0] +
2 * u * t * points[1] +
t * t * points[2];
}
// 三阶贝塞尔曲线
public static Vector3 CubicBezier(Vector3[] points, float t)
{
if (points.Length != 4) Debug.LogError("需要4个控制点");
float u = 1 - t;
return
u * u * u * points[0] +
3 * u * u * t * points[1] +
3 * u * t * t * points[2] +
t * t * t * points[3];
}
// n阶贝塞尔曲线通用实现
public static Vector3 BezierInterpolation(Vector3[] points, float t)
{
if (points.Length < 2)
{
Debug.LogError("至少需要2个控制点");
return Vector3.zero;
}
Vector3[] temp = new Vector3[points.Length];
points.CopyTo(temp, 0);
// 德卡斯特里奥算法递推
for (int k = 1; k < points.Length; k++)
{
for (int i = 0; i < points.Length - k; i++)
{
temp[i] = Vector3.Lerp(temp[i], temp[i + 1], t);
}
}
return temp[0];
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d0f6bf90b6c476e4eb75f7a6dae5c45d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,66 @@
using System;
using UnityEngine;
namespace Stary.Evo
{
public static class VectorExtension {
public static Vector3Ctor GetVector3Ctor(this Vector3 vector)
{
return new Vector3Ctor(vector);
}
public static Vector3 SetVector3Ctor(this Vector3Ctor vector3Ctor)
{
return new Vector3(vector3Ctor.x, vector3Ctor.y, vector3Ctor.z);
}
public static Vector2Ctor GetVector2Ctor( this Vector2 vector)
{
return new Vector2Ctor(vector);
}
public static Vector2 SetVector2Data(this Vector2Ctor vector3Ctor)
{
return new Vector2(vector3Ctor.x, vector3Ctor.y);
}
}
[Serializable]
public class Vector3Ctor
{
public float x;
public float y;
public float z;
public Vector3Ctor()
{
}
public Vector3Ctor(Vector3 vector)
{
x = vector.x;
y = vector.y;
z = vector.z;
}
public Vector3Ctor(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
}
[Serializable]
public class Vector2Ctor
{
public float x;
public float y;
public Vector2Ctor(Vector2 vector)
{
x = vector.x;
y = vector.y;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1077acf7c628ea24586d2c243ab718b4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,90 @@
using System;
using Rokid.UXR.Module;
using Stary.Evo;
using UnityEngine;
using UnityEngine.Android;
using ModuleManager = Rokid.UXR.Module.ModuleManager;
public class VoiceSwitchingScene : MonoBehaviour
{
private String[] _sceneNames = new string[]
{
"打开第一个场景",
"打开第二个场景",
"打开第三个场景",
"打开第四个场景",
"打开第五个场景",
"打开第六个场景",
"打开第七个场景",
"打开第八个场景",
"打开第九个场景",
"打开第十个场景",
"打开第十一个场景",
"打开第十二个场景",
"打开第十三个场景",
"打开第十四个场景",
"打开第十五个场景",
"打开第十六个场景",
"打开第十七个场景",
"打开第十八个场景",
"打开第十九个场景",
"打开第二十个场景",
};
private String[] _sceneNamesSpell = new string[]
{
"da kai di yi ge chang jing",
"da kai di er ge chang jing",
"da kai di san ge chang jing",
"da kai di si ge chang jing",
"da kai di wu ge chang jing",
"da kai di liu ge chang jing",
"da kai di qi ge chang jing",
"da kai di ba ge chang jing",
"da kai di jiu ge chang jing",
"da kai di shi ge chang jing",
"da kai di shi yi ge chang jing",
"da kai di shi er ge chang jing",
"da kai di shi san ge chang jing",
"da kai di shi si ge chang jing",
"da kai di shi wu ge chang jing",
"da kai di shi liu ge chang jing",
"da kai di shi qi ge chang jing",
"da kai di shi ba ge chang jing",
"da kai di shi jiu ge chang jing",
"da kai di shi er shi ge chang jing",
};
private HybridClREntrance hybridClREntrance;
private MainDomainAll mainDomainAll;
// Start is called before the first frame update
void Start()
{
hybridClREntrance = GetComponent<HybridClREntrance>();
if (!Permission.HasUserAuthorizedPermission("android.permission.RECORD_AUDIO"))
{
Permission.RequestUserPermission("android.permission.RECORD_AUDIO");
}
ModuleManager.Instance.RegistModule("com.rokid.voicecommand.VoiceCommandHelper", false);
OfflineVoiceModule.Instance.ChangeVoiceCommandLanguage(LANGUAGE.CHINESE);
mainDomainAll = Resources.Load<MainDomainAll>("MainDomainAll");
for (int i = 0; i < mainDomainAll.domainAll.Length; i++)
{
if(mainDomainAll.domainAll[i].isVideo)
OfflineVoiceModule.Instance.AddInstruct(LANGUAGE.CHINESE, _sceneNames[i], _sceneNamesSpell[i], this.gameObject.name, "OnReceive");
}
OfflineVoiceModule.Instance.Commit();
}
void OnReceive(string msg)
{
int index = Array.IndexOf(_sceneNames, msg);
if (index != -1)
{
hybridClREntrance.OpenDomain(mainDomainAll.domainAll[index].domainName , OpenDomainType.VIOICE);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 97f6e15196934254c94bd3de1f798b77
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,7 @@
namespace Stary.Evo
{
public class WebRequestTools
{
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 48091c7012fc4cd88a54c096febdcdd0
timeCreated: 1748318581

View File

@@ -0,0 +1,35 @@
using UnityEngine;
using YooAsset;
namespace Stary.Evo
{
public class YooAssetFileSystem
{
public static InitializeParameters OfflineInitializeParameter()
{
var buildinFileSystemParams = FileSystemParameters.CreateDefaultBuildinFileSystemParameters();
var initParameters = new OfflinePlayModeParameters();
initParameters.BuildinFileSystemParameters = buildinFileSystemParams;
return initParameters;
}
public static InitializeParameters hostInitializeParameter()
{
var initParameters = new OfflinePlayModeParameters();
var buildinFileSystemParams = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(null,
$"{Application.persistentDataPath}/DownloadedContent/{AppConfig.PackageDomainName}");
buildinFileSystemParams.AddParameter(FileSystemParametersDefine.APPEND_FILE_EXTENSION, true);
buildinFileSystemParams.AddParameter(FileSystemParametersDefine.COPY_BUILDIN_PACKAGE_MANIFEST, true);
initParameters.BuildinFileSystemParameters = buildinFileSystemParams;
return initParameters;
}
public static InitializeParameters EditorSimulateInitializeParameter()
{
var buildResult = EditorSimulateModeHelper.SimulateBuild(AppConfig.PackageDomainName);
var packageRoot = buildResult.PackageRootDirectory;
var editorFileSystemParameters = FileSystemParameters.CreateDefaultEditorFileSystemParameters(packageRoot);
var initParams = new EditorSimulateModeParameters();
initParams.EditorFileSystemParameters = editorFileSystemParameters;
return initParams;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c6f288f9aa65416baf25a50a4056fcae
timeCreated: 1749695458

View File

@@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
namespace Stary.Evo
{
public class ZipTool
{
// 新增:带进度回调的下载解压方法
public static async UniTask DownloadAndUnzipAsync(string fildId, string extractPath,
Action<float> downloadProgress = null, Action<float> unzipProgress = null)
{
string tempPath = Path.Combine(Application.persistentDataPath, "temp.zip");
try
{
string url = $"{AppConfig.IpConfig}/FileLoad/Download/{fildId}";
await WebRequestSystem.GetFile(url, tempPath,downloadProgress);
// 解压下载的文件
await UnzipAsync(tempPath, extractPath, unzipProgress);
File.Delete(tempPath);
}
finally
{
// 清理临时文件
if (File.Exists(tempPath))
{
File.Delete(tempPath);
}
}
}
// 修改:添加进度回调参数
public static async UniTask UnzipAsync(string zipPath, string extractPath,
Action<float> progressCallback = null)
{
try
{
using (ZipArchive archive = ZipFile.OpenRead(zipPath))
{
float totalEntries = archive.Entries.Count;
float processed = 0;
foreach (ZipArchiveEntry entry in archive.Entries)
{
string filePath = Path.Combine(extractPath, entry.FullName);
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
if (!string.IsNullOrEmpty(entry.Name))
{
using (Stream inputStream = entry.Open())
using (FileStream outputStream = File.Create(filePath))
{
await inputStream.CopyToAsync(outputStream);
}
}
processed++;
float progress = processed / totalEntries;
progressCallback?.Invoke(progress);
}
}
Debug.Log($"解压完成: {extractPath}");
}
catch (Exception ex)
{
Debug.LogError($"解压失败: {ex.Message}");
throw;
}
}
/// <summary>
/// 自定义网络请求器需要登录
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
private static UnityWebRequest WebRequester(string url)
{
var request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbGET);
var authorization = GetAuthorization(AppConfig.UserName, AppConfig.PassWord);
request.SetRequestHeader("AUTHORIZATION", authorization);
return request;
}
private static string GetAuthorization(string userName, string password)
{
string auth = userName + ":" + password;
var bytes = System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(auth);
return "Basic " + System.Convert.ToBase64String(bytes);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2f5ebd73225744188b38cf307e565ff2
timeCreated: 1747988495

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 90f5ed74142c4ae695e3e0c175e59b06
timeCreated: 1750651156

View File

@@ -0,0 +1,14 @@
*.sln
*.csproj
SocketClient/.vscode/
SocketClient/Packages/
SocketClient/Logs/
SocketClient/Library/
SocketServer/Packages/
SocketServer/obj/
SocketServer/Logs/
SocketServer/Library/
SocketServer/.vscode/
SocketClient/obj/
SocketClient/Temp/
SocketServer/Temp/

View File

@@ -0,0 +1,71 @@
# UnitySocket
该项目是基于Unity平台 纯C#编写的Socket网络模块,包括客户端与服务端的实现
## 环境依赖
- 项目运行平台: Unity2022.3.22f1
该项目仅包含网络底层的几个C#脚本与一个测试脚本可以随意选择Unity版本测试。
## 特点
1. 回调事件完善,包含服务器主动踢出、客户端主动断开、心跳包等底层功能逻辑。
2. 对象形式非单例模式一个项目可new多个连接
3. 代码量少纯C#编写未使用插件、DLL及工具类便于二次封装利于移植
## 报文组成
- 通用
报文总长度(4byte) + 报文类型(2byte) + 数据(N byte)
- 心跳包
报文总长度(4byte) + 报文类型-心跳包(2byte)
- 客户端断开
报文总长度(4byte) + 报文类型-客户端断开(2byte)
- 服务端踢出
报文总长度(4byte) + 报文类型-服务端踢出(2byte)
## 工作流
- 建立连接
1. 服务端通过 new SocketServer(string ip,int port) 建立连接
2. 客户端通过 new SocketClient(string ip,int port) 建立实例通过Connect函数尝试连接。
3. 客户端连接成功后,开始定时发送心跳包报文;服务端接收后记录时间,定时检测判断是否超时。
4. 如存在以下情况,则断开连接:
- 服务端主动踢出连接即时响应回调DisConnect
- 客户端主动断开连接即时响应回调DisConnect
- 客户端关闭连接即时响应回调DisConnect
- 服务端检测到客户端太久没有发送心跳包此处需要响应时间根据心跳包超时间隔、心跳包发送间隔及服务端心跳包超时检测间隔来决定发现超时则回调DisConnect
- 通信逻辑过程中报错即时响应自动重连默认重连上限为三次达到重连上限并未成功则回调DisConnect
- 接收数据
1. 接收线程收到数据存放入DataBuff数据缓存区。
2. DataBuff中不断通过SocketDataPack尝试解包解包成功后将报文发送到接收线程。
3. 接收线程收到报文后触发OnReceive回调
4. 业务层通过OnReceive回调接收到报文后反序列化报文体得到数据
- 发送数据
1. 业务层序列化数据得到报文体字节集通过Send函数发送数据。
2. Send函数中通过SocketDataPack装包后以字节集的形式发送出去。
## 代码说明
- DataBufferSocket传输数据缓存区此处主要处理Socket传输时粘包、分包的情况
- SocketEventSocket报文类型枚举此处只枚举了网络底层需要发送的报文类型业务逻辑层所使用的报文类型建议封装至报文体序列化类中
- SocketDataPackSocket报文类处理具体的拆包、装包逻辑
- SocketServerSocket服务端
- SocketClientSocket客户端
## 回调事件
- SocketServer 服务端
- public event Action<Socket> OnConnect; //客户端建立连接回调
- public event Action<Socket> OnDisconnect; // 客户端断开连接回调
- public event Action<Socket, SocketDataPack> OnReceive; // 接收报文回调
- SocketClient 客户端
- public event Action OnConnectSuccess; // 连接成功回调
- public event Action OnConnectError; // 连接失败回调
- public event Action OnDisconnect; // 断开回调
- public event Action<SocketDataPack> OnReceive; // 接收报文回调
- public event Action<SocketException> OnError; // 异常捕获回调
- public event Action<int> OnReConnectSuccess; // 重连成功回调
- public event Action<int> OnReConnectError; // 单次重连失败回调
- public event Action<int> OnReconnecting; // 单次重连中回调

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 3f0c65b3aa2616440978bf5d9f804e7c
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7b43e2f4bdef74642999b4d5530a9b3d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2e04a0e82198abe4fb1796dc44bc9e9d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e3cdc884284e12f48a350017446975ca
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,57 @@
using System;
/// <summary>
/// Socket传输过程的缓冲区尝试拆包获得数据
/// </summary>
public class DataBuffer
{
// 缓存区长度
private const int MIN_BUFF_LEN = 1024;
private byte[] _buff;
private int _buffLength = 0;
public DataBuffer(int minBuffLen = MIN_BUFF_LEN)
{
if (minBuffLen <= 0)
{
minBuffLen = MIN_BUFF_LEN;
}
_buff = new byte[minBuffLen];
}
/// <summary>
/// 添加缓存数据
/// </summary>
public void AddBuffer(byte[] data, int len)
{
byte[] buff = new byte[len];
Array.Copy(data, buff, len);
if (len > _buff.Length - _buffLength) //超过当前缓存
{
byte[] temp = new byte[_buffLength + len];
Array.Copy(_buff, 0, temp, 0, _buffLength);
Array.Copy(buff, 0, temp, _buffLength, len);
_buff = temp;
}
else
{
Array.Copy(data, 0, _buff, _buffLength, len);
}
_buffLength += len;//修改当前数据标记
}
public bool TryUnpack(out SocketDataPack dataPack)
{
dataPack = SocketDataPack.Unpack(_buff);
if (dataPack == null)
{
return false;
}
// 清理旧缓存
_buffLength -= dataPack.BuffLength;
byte[] temp = new byte[_buffLength < MIN_BUFF_LEN ? MIN_BUFF_LEN : _buffLength];
Array.Copy(_buff, dataPack.BuffLength, temp, 0, _buffLength);
_buff = temp;
return true;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e879d0a89aac39f4a8320fd3857d0012
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,332 @@
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Timers;
/// <summary>
/// Socket客户端
/// </summary>
public class SocketClient
{
/// <summary>
/// 主线程
/// </summary>
private readonly SynchronizationContext _mainThread;
private readonly string _ip;
private readonly int _port;
private const int TIMEOUT_CONNECT = 3000; // 连接超时时间 毫秒
private const int TIMEOUT_SEND = 3000; // 发送超时时间 毫秒
private const int TIMEOUT_RECEIVE = 3000; //接收超时时间 毫秒
private const int HEAD_OFFSET = 2000; //心跳包发送间隔 毫秒
private const int RECONN_MAX_SUM = 3; //最大重连次数
private Socket _client;
private Thread _receiveThread;
private System.Timers.Timer _connTimeoutTimer;
private System.Timers.Timer _headTimer;
private readonly DataBuffer _dataBuffer = new DataBuffer();
public event Action OnConnectSuccess; // 连接成功回调
public event Action OnConnectError; // 连接失败回调
public event Action OnDisconnect; // 断开回调
public event Action<SocketDataPack> OnReceive; // 接收报文回调
public event Action<SocketDataPack> OnSend; // 发送报文回调
public event Action<SocketException> OnError; // 异常捕获回调
public event Action<int> OnReConnectSuccess; // 重连成功回调
public event Action<int> OnReConnectError; // 单次重连失败回调
public event Action<int> OnReconnecting; // 单次重连中回调
private bool _isConnect = false;
private bool _isReconnect = false;
public SocketClient(string ip, int port)
{
_mainThread = SynchronizationContext.Current;
_ip = ip;
_port = port;
}
public void Connect(Action success = null, Action error = null)
{
OnDisconnect += () => { UnityEngine.Debug.Log("UnityEvo:断开连接"); };
OnReceive += (dataPack) => { UnityEngine.Debug.LogFormat("UnityEvo:接收数据>>>{0}", (SocketEvent)dataPack.Type); };
OnSend += (dataPack) => { UnityEngine.Debug.LogFormat("UnityEvo:发送数据>>>{0}", (SocketEvent)dataPack.Type); };
OnError += (ex) => { UnityEngine.Debug.LogFormat("UnityEvo:出现异常>>>{0}", ex); };
OnReConnectSuccess += (num) => { UnityEngine.Debug.LogFormat("UnityEvo:第{0}次重连成功", num); };
OnReConnectError += (num) => { UnityEngine.Debug.LogFormat("UnityEvo:第{0}次重连失败", num); };
OnReconnecting += (num) => { UnityEngine.Debug.LogFormat("UnityEvo:正在进行第{0}次重连", num); };
void OnTrigger(bool flag)
{
if (flag)
{
PostMainThreadAction(success);
PostMainThreadAction(OnConnectSuccess);
}
else
{
PostMainThreadAction(error);
PostMainThreadAction(OnConnectError);
}
if (_connTimeoutTimer != null)
{
_connTimeoutTimer.Stop();
_connTimeoutTimer = null;
}
}
try
{
_client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建套接字
_client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, TIMEOUT_SEND);
_client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, TIMEOUT_RECEIVE);
IPAddress ipAddress = IPAddress.Parse(_ip);//解析IP地址
IPEndPoint ipEndpoint = new IPEndPoint(ipAddress, _port);
IAsyncResult result = _client.BeginConnect(ipEndpoint, new AsyncCallback((iar) =>
{
try
{
Socket client = (Socket)iar.AsyncState;
client.EndConnect(iar);
_isConnect = true;
// 开始发送心跳包
_headTimer = new System.Timers.Timer(HEAD_OFFSET);
_headTimer.AutoReset = true;
_headTimer.Elapsed += delegate (object sender, ElapsedEventArgs args)
{
Send((UInt16)SocketEvent.ClientHead);
};
_headTimer.Start();
// 开始接收数据
_receiveThread = new Thread(new ThreadStart(ReceiveEvent));
_receiveThread.IsBackground = true;
_receiveThread.Start();
OnTrigger(true);
}
catch (SocketException ex)
{
OnTrigger(false);
}
}), _client);//异步连接
_connTimeoutTimer = new System.Timers.Timer(TIMEOUT_CONNECT);
_connTimeoutTimer.AutoReset = false;
_connTimeoutTimer.Elapsed += delegate (object sender, ElapsedEventArgs args)
{
OnTrigger(false);
};
_connTimeoutTimer.Start();
}
catch (SocketException ex)
{
OnTrigger(false);
// throw;
}
}
/// <summary>
/// 断线重连
/// </summary>
/// <param name="num"></param>
/// <param name="index"></param>
public void ReConnect(int num = RECONN_MAX_SUM, int index = 0)
{
_isReconnect = true;
num--;
index++;
if (num < 0)
{
onDisconnect();
_isReconnect = false;
return;
}
PostMainThreadAction<int>(OnReconnecting, index);
Connect(() =>
{
PostMainThreadAction<int>(OnReConnectSuccess, index);
_isReconnect = false;
}, () =>
{
PostMainThreadAction<int>(OnReConnectError, index);
ReConnect(num, index);
});
}
public void Send(UInt16 e, byte[] buff = null, Action<SocketDataPack> onTrigger = null)
{
buff = buff ?? new byte[] { };
var dataPack = new SocketDataPack(e, buff);
var data = dataPack.Buff;
try
{
_client.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback((asyncSend) =>
{
Socket c = (Socket)asyncSend.AsyncState;
c.EndSend(asyncSend);
PostMainThreadAction<SocketDataPack>(onTrigger, dataPack);
PostMainThreadAction<SocketDataPack>(OnSend, dataPack);
}), _client);
}
catch (SocketException ex)
{
onError(ex);
}
}
/// <summary>
/// 线程内接收数据的函数
/// </summary>
private void ReceiveEvent()
{
while (true)
{
try
{
if (!_isConnect) break;
if (_client.Available <= 0) continue;
byte[] rbytes = new byte[8 * 1024];
int len = _client.Receive(rbytes);
if (len > 0)
{
_dataBuffer.AddBuffer(rbytes, len); // 将收到的数据添加到缓存器中
if (_dataBuffer.TryUnpack(out var dataPack)) // 尝试解包
{
if (dataPack.Type == (UInt16)SocketEvent.ServerKickout)
{
// 服务端踢出
onDisconnect();
}
else if (dataPack.Type == (UInt16)SocketEvent.ServerMessage)
{
// 收到消息
PostMainThreadAction<SocketDataPack>(OnReceive, dataPack);
}
}
}
}
catch (SocketException ex)
{
onError(ex);
// throw;
}
}
}
/// <summary>
/// 业务逻辑 - 客户端主动断开
/// </summary>
public void DisConnect()
{
Send((UInt16)SocketEvent.ClientDisconn);
onDisconnect();
}
/// <summary>
/// 缓存数据清理
/// </summary>
public void Close()
{
if (!_isConnect) return;
_isConnect = false;
if (_headTimer != null)
{
_headTimer.Stop();
_headTimer = null;
}
// if (_receiveThread != null)
// {
// _receiveThread.Abort();
// _receiveThread = null;
// }
if (_connTimeoutTimer != null)
{
_connTimeoutTimer.Stop();
_connTimeoutTimer = null;
}
if (_client != null)
{
_client.Close();
_client = null;
}
}
/// <summary>
/// 错误回调
/// </summary>
/// <param name="e"></param>
private void onError(SocketException ex)
{
Close();
PostMainThreadAction<SocketException>(OnError, ex);
if (!_isReconnect)
{
ReConnect();
}
}
/// <summary>
/// 断开回调
/// </summary>
private void onDisconnect()
{
Close();
PostMainThreadAction(OnDisconnect);
}
/// <summary>
/// 通知主线程回调
/// </summary>
private void PostMainThreadAction(Action action)
{
_mainThread.Post(new SendOrPostCallback((o) =>
{
Action e = (Action)o.GetType().GetProperty("action")?.GetValue(o);
if (e != null) e();
}), new { action = action });
}
private void PostMainThreadAction<T>(Action<T> action, T arg1)
{
_mainThread.Post(new SendOrPostCallback((o) =>
{
Action<T> e = (Action<T>)o.GetType().GetProperty("action")?.GetValue(o);
T t1 = (T)o.GetType().GetProperty("arg1")?.GetValue(o);
if (e != null) e(t1);
}), new { action = action, arg1 = arg1 });
}
public void PostMainThreadAction<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2)
{
_mainThread.Post(new SendOrPostCallback((o) =>
{
Action<T1, T2> e = (Action<T1, T2>)o.GetType().GetProperty("action")?.GetValue(o);
T1 t1 = (T1)o.GetType().GetProperty("arg1")?.GetValue(o);
T2 t2 = (T2)o.GetType().GetProperty("arg2")?.GetValue(o);
if (e != null) e(t1, t2);
}), new { action = action, arg1 = arg1, arg2 = arg2 });
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8e91098b3165d6b478e1a1ab72aeb94c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,105 @@
using System;
using System.IO;
/// <summary>
/// Socket通信过程中的数据包 处理具体拆包装包逻辑
/// </summary>
public class SocketDataPack
{
// 消息:数据总长度(4byte) + 数据类型(2byte) + 数据(N byte)
public const int HEAD_DATA_LEN = 4;
public const int HEAD_TYPE_LEN = 2;
public static int HeadLen
{
get { return HEAD_DATA_LEN + HEAD_TYPE_LEN; }
}
/// <summary>
/// 数据包类型
/// </summary>
public UInt16 Type;
/// <summary>
/// 数据包数据
/// </summary>
public byte[] Data;
public byte[] Buff;
public int BuffLength
{
get { return Buff.Length; }
}
public int DataLength
{
get { return Data.Length; }
}
public SocketDataPack()
{
}
public SocketDataPack(UInt16 type, byte[] data)
{
Type = type;
Data = data;
Buff = GetBuff(Type, Data);
}
public static byte[] GetBuff(UInt16 type, byte[] data)
{
byte[] buff = new byte[data.Length + HeadLen];
byte[] temp;
temp = BitConverter.GetBytes(buff.Length);
Array.Copy(temp, 0, buff, 0, HEAD_DATA_LEN);
temp = BitConverter.GetBytes(type);
Array.Copy(temp, 0, buff, HEAD_DATA_LEN, HEAD_TYPE_LEN);
Array.Copy(data, 0, buff, HeadLen, data.Length);
return buff;
}
public static SocketDataPack Unpack(byte[] buff)
{
try
{
if (buff.Length < HeadLen)
{
// 头部没取完则返回
return null;
}
byte[] temp;
// 取数据长度
temp = new byte[HEAD_DATA_LEN];
Array.Copy(buff, 0, temp, 0, HEAD_DATA_LEN);
int buffLength = BitConverter.ToInt32(temp, 0);
if (buffLength <= 0) return null;
if (buffLength > buff.Length)
{
// 数据没取完
return null;
}
int dataLength = buffLength - HeadLen;
// 取数据类型
temp = new byte[HEAD_TYPE_LEN];
Array.Copy(buff, HEAD_DATA_LEN, temp, 0, HEAD_TYPE_LEN);
UInt16 dataType = BitConverter.ToUInt16(temp, 0);
// 取数据
byte[] data = new byte[dataLength];
Array.Copy(buff, HeadLen, data, 0, dataLength);
var dataPack = new SocketDataPack(dataType, data);
// UnityEngine.Debug.LogFormat("buffLen:{0} type:{1} dataLength:{2}", buffLength, dataType, data.Length);
return dataPack;
}
catch
{
// 存在不完整数据解包 则返回null
return null;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5e0c4c6bf95d3324a975f6ce0cdee4c4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,14 @@
/// <summary>
/// 网络事件协议ID枚举
/// </summary>
public enum SocketEvent
{
ClientHead = 0x0001, //心跳包
ClientDisconn = 0x0002, //客户端主动断开
ServerKickout = 0x0003, //服务端踢出
ServerMessage = 0x0004, //服务端发送消息
ClientMessage = 0x0005, //客户端发送消息
ScTest = 0x1001, //测试用
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b1c8d60cbbc2c7547827955a371fda00
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,349 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Timers;
using UnityEngine;
public class SocketInfo
{
public string IP;
public Thread ReceiveThread;
public long HeadTime;
}
/// <summary>
/// Socket服务端
/// </summary>
public class SocketServer
{
/// <summary>
/// 主线程
/// </summary>
private readonly SynchronizationContext _mainThread;
private const int HEAD_TIMEOUT = 5000; // 心跳超时 毫秒
private const int HEAD_CHECKTIME = 5000; // 心跳包超时检测 毫秒
public readonly Dictionary<Socket, SocketInfo> ClientInfoDic = new Dictionary<Socket, SocketInfo>();
private readonly Socket _server;
private System.Timers.Timer _headCheckTimer;
private readonly DataBuffer _dataBuffer = new DataBuffer();
public event Action<Socket> OnConnect; //客户端建立连接回调
public event Action<Socket> OnDisconnect; // 客户端断开连接回调
public event Action<Socket, SocketDataPack> OnReceive; // 接收报文回调
public event Action<Socket, SocketDataPack> OnSend; // 发送报文回调
// 目前捕获异常将触发OnDisconnect回调 暂不单独处理
// public event Action<SocketException> OnError; // 异常捕获回调
private bool _isValid = true;
public SocketServer(string ip, int port)
{
OnConnect += (client) =>
{
UnityEngine.Debug.LogFormat("UnityEvo:连接成功 >> IP:{0}", client.LocalEndPoint.ToString());
};
OnDisconnect += (client) =>
{
if(ClientInfoDic.TryGetValue(client, out var value))
Debug.LogFormat("UnityEvo:连接断开 >> IP:{0}", value.IP);
};
_mainThread = SynchronizationContext.Current;
_server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ipAddress = IPAddress.Parse(ip); //解析IP地址
_server.Bind(new IPEndPoint(ipAddress, port)); //绑定IP地址端口
_server.Listen(10); //设定最多10个排队连接请求
// 启动线程监听连接
var connectThread = new Thread(ListenClientConnect);
connectThread.Start();
// 心跳包定时检测
_headCheckTimer = new System.Timers.Timer(HEAD_CHECKTIME);
_headCheckTimer.AutoReset = true;
_headCheckTimer.Elapsed += delegate(object sender, ElapsedEventArgs args) { CheckHeadTimeOut(); };
_headCheckTimer.Start();
Debug.Log($"SocketServer Start: {ip}:{port}");
}
/// <summary>
/// 监听客户端连接
/// </summary>
private void ListenClientConnect()
{
while (true)
{
try
{
if (!_isValid) break;
Socket client = _server.Accept();
IPEndPoint ipEndPoint = (IPEndPoint)client.RemoteEndPoint;
Thread receiveThread = new Thread(ReceiveEvent);
ClientInfoDic.Add(client,
new SocketInfo() { IP = ipEndPoint.Address.ToString(), ReceiveThread = receiveThread, HeadTime = GetNowTime() });
receiveThread.Start(client);
PostMainThreadAction<Socket>(OnConnect, client);
}
catch
{
break;
}
}
}
/// <summary>
/// 获取当前时间戳
/// </summary>
/// <returns></returns>
private long GetNowTime()
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalMilliseconds);
}
public void Send(Socket client, UInt16 e, byte[] buff = null, Action<SocketDataPack> onTrigger = null)
{
SendMessage(client, e, buff, onTrigger);
}
private void SendMessage(Socket client, UInt16 e, byte[] buff = null, Action<SocketDataPack> onTrigger = null)
{
buff = buff ?? new byte[] { };
var dataPack = new SocketDataPack(e, buff);
var data = dataPack.Buff;
try
{
client.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback((asyncSend) =>
{
Socket c = (Socket)asyncSend.AsyncState;
c.EndSend(asyncSend);
PostMainThreadAction<SocketDataPack>(onTrigger, dataPack);
PostMainThreadAction<Socket, SocketDataPack>(OnSend, client, dataPack);
}), client);
}
catch (SocketException ex)
{
CloseClient(client);
// onError(ex);
}
}
/// <summary>
/// 线程内接收数据的函数
/// </summary>
private void ReceiveEvent(object client)
{
Socket tsocket = (Socket)client;
while (true)
{
if (!_isValid) return;
if (!ClientInfoDic.ContainsKey(tsocket))
{
return;
}
try
{
byte[] rbytes = new byte[8 * 1024];
int len = tsocket.Receive(rbytes);
if (len > 0)
{
_dataBuffer.AddBuffer(rbytes, len); // 将收到的数据添加到缓存器中
if (_dataBuffer.TryUnpack(out var dataPack)) // 尝试解包
{
if (dataPack.Type == (UInt16)SocketEvent.ClientHead)
{
// 接收到心跳包
ReceiveHead(tsocket);
}
else if (dataPack.Type == (UInt16)SocketEvent.ClientDisconn)
{
// 客户端断开连接
CloseClient(tsocket);
}
else if (dataPack.Type == (UInt16)SocketEvent.ClientMessage)
{
// 收到消息
PostMainThreadAction<Socket, SocketDataPack>(OnReceive, tsocket, dataPack);
}
}
}
else
{
if (tsocket.Poll(-1, SelectMode.SelectRead))
{
CloseClient(tsocket);
return;
}
}
}
catch (SocketException ex)
{
CloseClient(tsocket);
// onError(ex);
return;
}
}
}
/// <summary>
/// 接收到心跳包
/// </summary>
private void ReceiveHead(Socket client)
{
SocketInfo info;
if (ClientInfoDic.TryGetValue(client, out info))
{
long now = GetNowTime();
long offset = now - info.HeadTime;
UnityEngine.Debug.Log("更新心跳时间戳 >>>" + now + " 间隔>>>" + offset);
if (offset > HEAD_TIMEOUT)
{
// 心跳包收到但超时逻辑
}
info.HeadTime = now;
}
}
/// <summary>
/// 检测心跳包超时
/// </summary>
private void CheckHeadTimeOut()
{
var tempList = new List<Socket>();
foreach (var socket in ClientInfoDic.Keys)
{
tempList.Add(socket);
}
foreach (var socket in tempList)
{
var info = ClientInfoDic[socket];
long now = GetNowTime();
long offset = now - info.HeadTime;
if (offset > HEAD_TIMEOUT)
{
// 心跳包超时
KickOut(socket);
}
}
}
public void KickOut(Socket client)
{
// 踢出连接
Send(client, (UInt16)SocketEvent.ServerKickout, null, (dataPack) => { CloseClient(client); });
}
public void KickOutAll()
{
var tempList = new List<Socket>();
foreach (var socket in ClientInfoDic.Keys)
{
tempList.Add(socket);
}
foreach (var socket in tempList)
{
KickOut(socket);
}
}
/// <summary>
/// 清理客户端连接
/// </summary>
/// <param name="client"></param>
private void CloseClient(Socket client)
{
PostMainThreadAction<Socket>((socket) =>
{
if (OnDisconnect != null) OnDisconnect(socket);
ClientInfoDic.Remove(socket);
socket.Close();
}, client);
}
/// <summary>
/// 关闭
/// </summary>
public void Close()
{
if (!_isValid) return;
_isValid = false;
// if (_connectThread != null) _connectThread.Abort();
var tempList = new List<Socket>();
foreach (var socket in ClientInfoDic.Keys)
{
tempList.Add(socket);
}
foreach (var socket in tempList)
{
CloseClient(socket);
}
if (_headCheckTimer != null)
{
_headCheckTimer.Stop();
_headCheckTimer = null;
}
_server.Close();
}
// /// <summary>
// /// 错误回调
// /// </summary>
// /// <param name="e"></param>
// private void onError(SocketException ex)
// {
// PostMainThreadAction<SocketException>(OnError, ex);
// }
// <summary>
/// 通知主线程回调
/// </summary>
private void PostMainThreadAction(Action action)
{
_mainThread.Post(new SendOrPostCallback((o) =>
{
Action e = (Action)o.GetType().GetProperty("action")?.GetValue(o);
if (e != null) e();
}), new { action = action });
}
private void PostMainThreadAction<T>(Action<T> action, T arg1)
{
_mainThread.Post(new SendOrPostCallback((o) =>
{
Action<T> e = (Action<T>)o.GetType().GetProperty("action")?.GetValue(o);
T t1 = (T)o.GetType().GetProperty("arg1")?.GetValue(o);
if (e != null) e(t1);
}), new { action = action, arg1 = arg1 });
}
public void PostMainThreadAction<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2)
{
_mainThread.Post(new SendOrPostCallback((o) =>
{
Action<T1, T2> e = (Action<T1, T2>)o.GetType().GetProperty("action")?.GetValue(o);
T1 t1 = (T1)o.GetType().GetProperty("arg1")?.GetValue(o);
T2 t2 = (T2)o.GetType().GetProperty("arg2")?.GetValue(o);
if (e != null) e(t1, t2);
}), new { action = action, arg1 = arg1, arg2 = arg2 });
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a2c6448e77af258458f5959d7e15c022
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,51 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SocketClientMain : MonoBehaviour
{
private string ip = "127.0.0.1";
private int port = 8080;
private SocketClient _client;
private void Awake()
{
_client = new SocketClient("192.168.31.67", 6854);
_client.Connect(() =>
{
UnityEngine.Debug.Log("连接成功");
// _client.DisConnect();
}, () => { UnityEngine.Debug.Log("连接失败"); });
}
public void StartConnect(string ip = "", int port = 0)
{
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.A))
{
var bytes = System.Text.Encoding.UTF8.GetBytes("我是测试数据");
_client.Send((System.UInt16)SocketEvent.ScTest, bytes);
}
if (Input.GetKeyDown(KeyCode.D))
{
_client.DisConnect();
}
}
private void OnDestroy()
{
// 注意由于Unity编译器环境下游戏开启/关闭只影响主线程的开关游戏关闭回调时需要通过Close函数来关闭服务端/客户端的线程。
if (_client != null)
{
_client.Close();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 052b8595da3036e4a94eabc9a5a2b95b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,45 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SocketServerMain : MonoBehaviour
{
SocketServer _server;
private void Awake()
{
_server = new SocketServer("192.168.31.67", 6854);
_server.OnReceive += (client, data) =>
{
UnityEngine.Debug.LogFormat("[{0}]接收到数据>>>{1} {2}", client.LocalEndPoint.ToString(), (SocketEvent)data.Type, data.Buff.Length);
switch ((SocketEvent)data.Type)
{
case SocketEvent.ScTest:
UnityEngine.Debug.LogFormat("接收到测试数据 >>> {0}", System.Text.Encoding.UTF8.GetString(data.Data));
break;
}
};
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.A))
{
// 踢出连接
foreach (var item in _server.ClientInfoDic.Keys)
{
_server.KickOutAll();
}
}
}
private void OnDestroy()
{
// 注意由于Unity编译器环境下游戏开启/关闭只影响主线程的开关游戏关闭回调时需要通过Close函数来关闭服务端/客户端的线程。
if (_server != null)
{
_server.Close();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6b72333f2bc06a7428fb3c0eadeed0d1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,29 @@
{
"name": "stary.hotfixupdate.main",
"rootNamespace": "",
"references": [
"GUID:e34a5702dd353724aa315fb8011f08c3",
"GUID:d1a793c2b6959e04ea45b972eaa369c8",
"GUID:f51ebe6a0ceec4240a699833d6309b23",
"GUID:4d1926c9df5b052469a1c63448b7609a",
"GUID:006be2ba6588a8b4989d3724bb01d8b7",
"GUID:fe14bc9dd681249d19cf4ef377c7e29e",
"GUID:6447e10b87dc140ab924878c1ecef665",
"GUID:ec45849e30ba03e4dab386099d8c697b",
"GUID:aa8669166ad4e984bb229dc22e7b1e02",
"GUID:66fdc96a271fc954e9da30c76a486d9c",
"GUID:6a5d7223300a2ef48aa366288a446472",
"GUID:10c9b58b77ad42b4193e2a393b1a9899",
"GUID:fad681b9bfe621d4fa07f4f69c311443",
"GUID:ff70fce64a3314305977bdf5610ed86b"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4492e37c9663479418f9522cc4796b57
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: