Compare commits
26 Commits
Author | SHA1 | Date |
---|---|---|
|
dc33abde46 | |
|
aae7b08dd1 | |
|
f70582af1a | |
|
f04d84bb93 | |
|
78693deed6 | |
|
f1a5965b4c | |
|
cd43d0775d | |
|
cf532a84ef | |
|
651b65d148 | |
|
895dde1cc8 | |
|
f6e94c9514 | |
|
da4ba4453c | |
|
53ea8c8002 | |
|
d4549b1228 | |
|
fcf9eff2f6 | |
|
93b58149d2 | |
|
c91c49465b | |
|
e9fa3ead04 | |
|
eff2f1d968 | |
|
cd0a6579b8 | |
|
18c2e232cf | |
|
ad680638ac | |
|
0764061d8f | |
|
d448026250 | |
|
34f553b9e3 | |
|
25d1e32ce9 |
|
@ -2,6 +2,46 @@
|
||||||
|
|
||||||
All notable changes to this package will be documented in this file.
|
All notable changes to this package will be documented in this file.
|
||||||
|
|
||||||
|
## [1.4.16] - 2023-06-14
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- 增加了自动分析冗余资源的开关
|
||||||
|
|
||||||
|
```c#
|
||||||
|
/// <summary>
|
||||||
|
/// 构建参数
|
||||||
|
/// </summary>
|
||||||
|
public class BuildParameters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 自动分析冗余资源
|
||||||
|
/// </summary>
|
||||||
|
public bool AutoAnalyzeRedundancy = true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 太空战机DEMO启用了新的内置资源查询机制。
|
||||||
|
|
||||||
|
## [1.4.15] - 2023-06-09
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- 修复了安卓平台,解压内置文件到沙盒失败后不再重新尝试的问题。
|
||||||
|
- 修复了验证远端下载文件,极小概率失败的问题。
|
||||||
|
- 修复了太空战机DEMO在IOS平台流解密失败的问题。
|
||||||
|
|
||||||
|
## [1.4.14] - 2023-05-26
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- 修复了收集器对着色器未过滤的问题。
|
||||||
|
- 修复了内置着色器Tag特殊情况下未正确传染给依赖资源包的问题。
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Unity2021版本及以上推荐使用可编程构建管线(SBP)
|
||||||
|
|
||||||
## [1.4.13] - 2023-05-12
|
## [1.4.13] - 2023-05-12
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
@ -275,6 +275,7 @@ namespace YooAsset.Editor
|
||||||
buildParameters.PackageName = AssetBundleBuilderSettingData.Setting.BuildPackage;
|
buildParameters.PackageName = AssetBundleBuilderSettingData.Setting.BuildPackage;
|
||||||
buildParameters.PackageVersion = _buildVersionField.value;
|
buildParameters.PackageVersion = _buildVersionField.value;
|
||||||
buildParameters.VerifyBuildingResult = true;
|
buildParameters.VerifyBuildingResult = true;
|
||||||
|
buildParameters.AutoAnalyzeRedundancy = true;
|
||||||
buildParameters.ShareAssetPackRule = new DefaultShareAssetPackRule();
|
buildParameters.ShareAssetPackRule = new DefaultShareAssetPackRule();
|
||||||
buildParameters.EncryptionServices = CreateEncryptionServicesInstance();
|
buildParameters.EncryptionServices = CreateEncryptionServicesInstance();
|
||||||
buildParameters.CompressOption = AssetBundleBuilderSettingData.Setting.CompressOption;
|
buildParameters.CompressOption = AssetBundleBuilderSettingData.Setting.CompressOption;
|
||||||
|
|
|
@ -183,5 +183,27 @@ namespace YooAsset.Editor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断是否为冗余资源
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRedundancyAsset()
|
||||||
|
{
|
||||||
|
if (CollectorType != ECollectorType.None)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (IsRawAsset)
|
||||||
|
throw new Exception("Should never get here !");
|
||||||
|
|
||||||
|
return _referenceBundleNames.Count > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取关联资源包的数量
|
||||||
|
/// </summary>
|
||||||
|
public int GetReferenceBundleCount()
|
||||||
|
{
|
||||||
|
return _referenceBundleNames.Count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,6 +10,11 @@ namespace YooAsset.Editor
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, BuildBundleInfo> _bundleInfoDic = new Dictionary<string, BuildBundleInfo>(10000);
|
private readonly Dictionary<string, BuildBundleInfo> _bundleInfoDic = new Dictionary<string, BuildBundleInfo>(10000);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 冗余的资源列表
|
||||||
|
/// </summary>
|
||||||
|
public readonly List<ReportRedundancyInfo> RedundancyInfos= new List<ReportRedundancyInfo>(1000);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 参与构建的资源总数
|
/// 参与构建的资源总数
|
||||||
/// 说明:包括主动收集的资源以及其依赖的所有资源
|
/// 说明:包括主动收集的资源以及其依赖的所有资源
|
||||||
|
|
|
@ -77,6 +77,11 @@ namespace YooAsset.Editor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool VerifyBuildingResult = false;
|
public bool VerifyBuildingResult = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自动分析冗余资源
|
||||||
|
/// </summary>
|
||||||
|
public bool AutoAnalyzeRedundancy = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 共享资源的打包规则
|
/// 共享资源的打包规则
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -27,6 +27,11 @@ namespace YooAsset.Editor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<ReportBundleInfo> BundleInfos = new List<ReportBundleInfo>();
|
public List<ReportBundleInfo> BundleInfos = new List<ReportBundleInfo>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 冗余的资源列表
|
||||||
|
/// </summary>
|
||||||
|
public List<ReportRedundancyInfo> RedundancyInfos = new List<ReportRedundancyInfo>();
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取资源包信息类
|
/// 获取资源包信息类
|
||||||
|
@ -61,7 +66,7 @@ namespace YooAsset.Editor
|
||||||
File.Delete(savePath);
|
File.Delete(savePath);
|
||||||
|
|
||||||
string json = JsonUtility.ToJson(buildReport, true);
|
string json = JsonUtility.ToJson(buildReport, true);
|
||||||
FileUtility.CreateFile(savePath, json);
|
FileUtility.WriteAllText(savePath, json);
|
||||||
}
|
}
|
||||||
public static BuildReport Deserialize(string jsonData)
|
public static BuildReport Deserialize(string jsonData)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace YooAsset.Editor
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public class ReportRedundancyInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 资源路径
|
||||||
|
/// </summary>
|
||||||
|
public string AssetPath;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 资源类型
|
||||||
|
/// </summary>
|
||||||
|
public string AssetType;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 资源GUID
|
||||||
|
/// 说明:Meta文件记录的GUID
|
||||||
|
/// </summary>
|
||||||
|
public string AssetGUID;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 资源文件大小
|
||||||
|
/// </summary>
|
||||||
|
public long FileSize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 冗余的资源包数量
|
||||||
|
/// </summary>
|
||||||
|
public int Number;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 7bfd05221983858429246096617dff1d
|
guid: 7fbb7b27f54d3b0439a951348fd9d785
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
|
@ -22,7 +22,7 @@ namespace YooAsset.Editor
|
||||||
/// 构建时间
|
/// 构建时间
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string BuildDate;
|
public string BuildDate;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构建耗时(单位:秒)
|
/// 构建耗时(单位:秒)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -63,6 +63,16 @@ namespace YooAsset.Editor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UniqueBundleName;
|
public bool UniqueBundleName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自动分析冗余
|
||||||
|
/// </summary>
|
||||||
|
public bool AutoAnalyzeRedundancy;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 共享资源的打包类名称
|
||||||
|
/// </summary>
|
||||||
|
public string ShareAssetPackRuleClassName;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 加密服务类名称
|
/// 加密服务类名称
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -98,7 +98,7 @@ namespace YooAsset.Editor
|
||||||
{
|
{
|
||||||
string fileName = YooAssetSettingsData.GetPackageHashFileName(buildParameters.PackageName, buildParameters.PackageVersion);
|
string fileName = YooAssetSettingsData.GetPackageHashFileName(buildParameters.PackageName, buildParameters.PackageVersion);
|
||||||
string filePath = $"{packageOutputDirectory}/{fileName}";
|
string filePath = $"{packageOutputDirectory}/{fileName}";
|
||||||
FileUtility.CreateFile(filePath, packageHash);
|
FileUtility.WriteAllText(filePath, packageHash);
|
||||||
BuildLogger.Log($"创建补丁清单哈希文件:{filePath}");
|
BuildLogger.Log($"创建补丁清单哈希文件:{filePath}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ namespace YooAsset.Editor
|
||||||
{
|
{
|
||||||
string fileName = YooAssetSettingsData.GetPackageVersionFileName(buildParameters.PackageName);
|
string fileName = YooAssetSettingsData.GetPackageVersionFileName(buildParameters.PackageName);
|
||||||
string filePath = $"{packageOutputDirectory}/{fileName}";
|
string filePath = $"{packageOutputDirectory}/{fileName}";
|
||||||
FileUtility.CreateFile(filePath, buildParameters.PackageVersion);
|
FileUtility.WriteAllText(filePath, buildParameters.PackageVersion);
|
||||||
BuildLogger.Log($"创建补丁清单版本文件:{filePath}");
|
BuildLogger.Log($"创建补丁清单版本文件:{filePath}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,6 +205,7 @@ namespace YooAsset.Editor
|
||||||
throw new Exception("没有发现着色器资源包!");
|
throw new Exception("没有发现着色器资源包!");
|
||||||
|
|
||||||
// 检测依赖交集并更新依赖ID
|
// 检测依赖交集并更新依赖ID
|
||||||
|
HashSet<string> tagTemps = new HashSet<string>();
|
||||||
foreach (var packageAsset in manifest.AssetList)
|
foreach (var packageAsset in manifest.AssetList)
|
||||||
{
|
{
|
||||||
List<string> dependBundles = GetPackageAssetAllDependBundles(manifest, packageAsset);
|
List<string> dependBundles = GetPackageAssetAllDependBundles(manifest, packageAsset);
|
||||||
|
@ -215,8 +216,23 @@ namespace YooAsset.Editor
|
||||||
if (newDependIDs.Contains(shaderBundleId) == false)
|
if (newDependIDs.Contains(shaderBundleId) == false)
|
||||||
newDependIDs.Add(shaderBundleId);
|
newDependIDs.Add(shaderBundleId);
|
||||||
packageAsset.DependIDs = newDependIDs.ToArray();
|
packageAsset.DependIDs = newDependIDs.ToArray();
|
||||||
|
foreach (var tag in packageAsset.AssetTags)
|
||||||
|
{
|
||||||
|
if (tagTemps.Contains(tag) == false)
|
||||||
|
tagTemps.Add(tag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新资源包标签
|
||||||
|
var packageBundle = manifest.BundleList[shaderBundleId];
|
||||||
|
List<string> newTags = new List<string>(packageBundle.Tags);
|
||||||
|
foreach (var tag in tagTemps)
|
||||||
|
{
|
||||||
|
if (newTags.Contains(tag) == false)
|
||||||
|
newTags.Add(tag);
|
||||||
|
}
|
||||||
|
packageBundle.Tags = newTags.ToArray();
|
||||||
}
|
}
|
||||||
private List<string> GetPackageAssetAllDependBundles(PackageManifest manifest, PackageAsset packageAsset)
|
private List<string> GetPackageAssetAllDependBundles(PackageManifest manifest, PackageAsset packageAsset)
|
||||||
{
|
{
|
||||||
|
@ -302,7 +318,7 @@ namespace YooAsset.Editor
|
||||||
{
|
{
|
||||||
if (packageBundle.IsRawFile)
|
if (packageBundle.IsRawFile)
|
||||||
{
|
{
|
||||||
_cachedBundleDepends.Add(packageBundle.BundleName, new string[] { } );
|
_cachedBundleDepends.Add(packageBundle.BundleName, new string[] { });
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,7 +337,7 @@ namespace YooAsset.Editor
|
||||||
}
|
}
|
||||||
EditorTools.ClearProgressBar();
|
EditorTools.ClearProgressBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int[] GetBundleRefrenceIDs(PackageManifest manifest, PackageBundle targetBundle)
|
private int[] GetBundleRefrenceIDs(PackageManifest manifest, PackageBundle targetBundle)
|
||||||
{
|
{
|
||||||
List<string> referenceList = new List<string>();
|
List<string> referenceList = new List<string>();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
|
|
||||||
namespace YooAsset.Editor
|
namespace YooAsset.Editor
|
||||||
|
@ -46,6 +47,9 @@ namespace YooAsset.Editor
|
||||||
buildReport.Summary.BuildPackageVersion = buildParameters.PackageVersion;
|
buildReport.Summary.BuildPackageVersion = buildParameters.PackageVersion;
|
||||||
buildReport.Summary.EnableAddressable = buildMapContext.EnableAddressable;
|
buildReport.Summary.EnableAddressable = buildMapContext.EnableAddressable;
|
||||||
buildReport.Summary.UniqueBundleName = buildMapContext.UniqueBundleName;
|
buildReport.Summary.UniqueBundleName = buildMapContext.UniqueBundleName;
|
||||||
|
buildReport.Summary.AutoAnalyzeRedundancy = buildParameters.AutoAnalyzeRedundancy;
|
||||||
|
buildReport.Summary.ShareAssetPackRuleClassName = buildParameters.ShareAssetPackRule == null ?
|
||||||
|
"null" : buildParameters.ShareAssetPackRule.GetType().FullName;
|
||||||
buildReport.Summary.EncryptionServicesClassName = buildParameters.EncryptionServices == null ?
|
buildReport.Summary.EncryptionServicesClassName = buildParameters.EncryptionServices == null ?
|
||||||
"null" : buildParameters.EncryptionServices.GetType().FullName;
|
"null" : buildParameters.EncryptionServices.GetType().FullName;
|
||||||
|
|
||||||
|
@ -101,6 +105,9 @@ namespace YooAsset.Editor
|
||||||
buildReport.BundleInfos.Add(reportBundleInfo);
|
buildReport.BundleInfos.Add(reportBundleInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 冗余资源列表
|
||||||
|
buildReport.RedundancyInfos = new List<ReportRedundancyInfo>(buildMapContext.RedundancyInfos);
|
||||||
|
|
||||||
// 序列化文件
|
// 序列化文件
|
||||||
string fileName = YooAssetSettingsData.GetReportFileName(buildParameters.PackageName, buildParameters.PackageVersion);
|
string fileName = YooAssetSettingsData.GetReportFileName(buildParameters.PackageName, buildParameters.PackageVersion);
|
||||||
string filePath = $"{packageOutputDirectory}/{fileName}";
|
string filePath = $"{packageOutputDirectory}/{fileName}";
|
||||||
|
|
|
@ -52,7 +52,7 @@ namespace YooAsset.Editor
|
||||||
}
|
}
|
||||||
|
|
||||||
string filePath = $"{pipelineOutputDirectory}/{bundleInfo.BundleName}.encrypt";
|
string filePath = $"{pipelineOutputDirectory}/{bundleInfo.BundleName}.encrypt";
|
||||||
FileUtility.CreateFile(filePath, encryptResult.EncryptedData);
|
FileUtility.WriteAllBytes(filePath, encryptResult.EncryptedData);
|
||||||
bundleInfo.EncryptedFilePath = filePath;
|
bundleInfo.EncryptedFilePath = filePath;
|
||||||
bundleInfo.LoadMethod = encryptResult.LoadMethod;
|
bundleInfo.LoadMethod = encryptResult.LoadMethod;
|
||||||
BuildLogger.Log($"Bundle文件加密完成:{filePath}");
|
BuildLogger.Log($"Bundle文件加密完成:{filePath}");
|
||||||
|
|
|
@ -13,8 +13,7 @@ namespace YooAsset.Editor
|
||||||
void IBuildTask.Run(BuildContext context)
|
void IBuildTask.Run(BuildContext context)
|
||||||
{
|
{
|
||||||
var buildParametersContext = context.GetContextObject<BuildParametersContext>();
|
var buildParametersContext = context.GetContextObject<BuildParametersContext>();
|
||||||
var buildParameters = buildParametersContext.Parameters;
|
var buildMapContext = CreateBuildMap(buildParametersContext.Parameters);
|
||||||
var buildMapContext = CreateBuildMap(buildParameters.BuildMode, buildParameters.ShareAssetPackRule, buildParameters.PackageName);
|
|
||||||
context.SetContextObject(buildMapContext);
|
context.SetContextObject(buildMapContext);
|
||||||
BuildLogger.Log("构建内容准备完毕!");
|
BuildLogger.Log("构建内容准备完毕!");
|
||||||
|
|
||||||
|
@ -25,8 +24,13 @@ namespace YooAsset.Editor
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 资源构建上下文
|
/// 资源构建上下文
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public BuildMapContext CreateBuildMap(EBuildMode buildMode, IShareAssetPackRule packRule, string packageName)
|
public BuildMapContext CreateBuildMap(BuildParameters buildParameters)
|
||||||
{
|
{
|
||||||
|
EBuildMode buildMode = buildParameters.BuildMode;
|
||||||
|
string packageName = buildParameters.PackageName;
|
||||||
|
IShareAssetPackRule sharePackRule = buildParameters.ShareAssetPackRule;
|
||||||
|
bool autoAnalyzeRedundancy = buildParameters.AutoAnalyzeRedundancy;
|
||||||
|
|
||||||
Dictionary<string, BuildAssetInfo> allBuildAssetInfoDic = new Dictionary<string, BuildAssetInfo>(1000);
|
Dictionary<string, BuildAssetInfo> allBuildAssetInfoDic = new Dictionary<string, BuildAssetInfo>(1000);
|
||||||
|
|
||||||
// 1. 检测配置合法性
|
// 1. 检测配置合法性
|
||||||
|
@ -99,10 +103,30 @@ namespace YooAsset.Editor
|
||||||
context.ShadersBundleName = collectResult.Command.ShadersBundleName;
|
context.ShadersBundleName = collectResult.Command.ShadersBundleName;
|
||||||
|
|
||||||
// 8. 计算共享的资源包名
|
// 8. 计算共享的资源包名
|
||||||
var command = collectResult.Command;
|
if (autoAnalyzeRedundancy)
|
||||||
foreach (var buildAssetInfo in allBuildAssetInfoDic.Values)
|
|
||||||
{
|
{
|
||||||
buildAssetInfo.CalculateShareBundleName(packRule, command.UniqueBundleName, command.PackageName, command.ShadersBundleName);
|
var command = collectResult.Command;
|
||||||
|
foreach (var buildAssetInfo in allBuildAssetInfoDic.Values)
|
||||||
|
{
|
||||||
|
buildAssetInfo.CalculateShareBundleName(sharePackRule, command.UniqueBundleName, command.PackageName, command.ShadersBundleName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 记录冗余资源
|
||||||
|
foreach (var buildAssetInfo in allBuildAssetInfoDic.Values)
|
||||||
|
{
|
||||||
|
if (buildAssetInfo.IsRedundancyAsset())
|
||||||
|
{
|
||||||
|
var redundancyInfo = new ReportRedundancyInfo();
|
||||||
|
redundancyInfo.AssetPath = buildAssetInfo.AssetPath;
|
||||||
|
redundancyInfo.AssetType = AssetDatabase.GetMainAssetTypeAtPath(buildAssetInfo.AssetPath).Name;
|
||||||
|
redundancyInfo.AssetGUID = AssetDatabase.AssetPathToGUID(buildAssetInfo.AssetPath);
|
||||||
|
redundancyInfo.FileSize = FileUtility.GetFileSize(buildAssetInfo.AssetPath);
|
||||||
|
redundancyInfo.Number = buildAssetInfo.GetReferenceBundleCount();
|
||||||
|
context.RedundancyInfos.Add(redundancyInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 9. 移除不参与构建的资源
|
// 9. 移除不参与构建的资源
|
||||||
|
|
|
@ -24,6 +24,13 @@ namespace YooAsset.Editor
|
||||||
|
|
||||||
if (buildParameters.BuildMode != EBuildMode.SimulateBuild)
|
if (buildParameters.BuildMode != EBuildMode.SimulateBuild)
|
||||||
{
|
{
|
||||||
|
#if UNITY_2021_3_OR_NEWER
|
||||||
|
if (buildParameters.BuildPipeline == EBuildPipeline.BuiltinBuildPipeline)
|
||||||
|
{
|
||||||
|
BuildLogger.Warning("推荐使用可编程构建管线(SBP)!");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// 检测当前是否正在构建资源包
|
// 检测当前是否正在构建资源包
|
||||||
if (BuildPipeline.isBuildingPlayer)
|
if (BuildPipeline.isBuildingPlayer)
|
||||||
throw new Exception("当前正在构建资源包,请结束后再试");
|
throw new Exception("当前正在构建资源包,请结束后再试");
|
||||||
|
|
|
@ -251,7 +251,7 @@ namespace YooAsset.Editor
|
||||||
// 检测原生文件是否合规
|
// 检测原生文件是否合规
|
||||||
if (isRawFilePackRule)
|
if (isRawFilePackRule)
|
||||||
{
|
{
|
||||||
string extension = StringUtility.RemoveFirstChar(System.IO.Path.GetExtension(assetPath));
|
string extension = EditorTools.RemoveFirstChar(System.IO.Path.GetExtension(assetPath));
|
||||||
if (extension == EAssetFileExtension.unity.ToString() || extension == EAssetFileExtension.prefab.ToString() ||
|
if (extension == EAssetFileExtension.unity.ToString() || extension == EAssetFileExtension.prefab.ToString() ||
|
||||||
extension == EAssetFileExtension.fbx.ToString() || extension == EAssetFileExtension.mat.ToString() ||
|
extension == EAssetFileExtension.fbx.ToString() || extension == EAssetFileExtension.mat.ToString() ||
|
||||||
extension == EAssetFileExtension.controller.ToString() || extension == EAssetFileExtension.anim.ToString() ||
|
extension == EAssetFileExtension.controller.ToString() || extension == EAssetFileExtension.anim.ToString() ||
|
||||||
|
@ -290,10 +290,6 @@ namespace YooAsset.Editor
|
||||||
}
|
}
|
||||||
private bool IsCollectAsset(string assetPath)
|
private bool IsCollectAsset(string assetPath)
|
||||||
{
|
{
|
||||||
Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
|
|
||||||
if (assetType == typeof(UnityEngine.Shader) || assetType == typeof(UnityEngine.ShaderVariantCollection))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// 根据规则设置过滤资源文件
|
// 根据规则设置过滤资源文件
|
||||||
IFilterRule filterRuleInstance = AssetBundleCollectorSettingData.GetFilterRuleInstance(FilterRuleName);
|
IFilterRule filterRuleInstance = AssetBundleCollectorSettingData.GetFilterRuleInstance(FilterRuleName);
|
||||||
return filterRuleInstance.IsCollectAsset(new FilterRuleData(assetPath));
|
return filterRuleInstance.IsCollectAsset(new FilterRuleData(assetPath));
|
||||||
|
|
|
@ -40,7 +40,7 @@ namespace YooAsset.Editor
|
||||||
{
|
{
|
||||||
PackRuleResult IPackRule.GetPackRuleResult(PackRuleData data)
|
PackRuleResult IPackRule.GetPackRuleResult(PackRuleData data)
|
||||||
{
|
{
|
||||||
string bundleName = StringUtility.RemoveExtension(data.AssetPath);
|
string bundleName = PathUtility.RemoveExtension(data.AssetPath);
|
||||||
PackRuleResult result = new PackRuleResult(bundleName, DefaultPackRule.AssetBundleFileExtension);
|
PackRuleResult result = new PackRuleResult(bundleName, DefaultPackRule.AssetBundleFileExtension);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ namespace YooAsset.Editor
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bundleName = StringUtility.RemoveExtension(collectPath);
|
bundleName = PathUtility.RemoveExtension(collectPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
PackRuleResult result = new PackRuleResult(bundleName, DefaultPackRule.AssetBundleFileExtension);
|
PackRuleResult result = new PackRuleResult(bundleName, DefaultPackRule.AssetBundleFileExtension);
|
||||||
|
|
|
@ -278,7 +278,7 @@ namespace YooAsset.Editor
|
||||||
|
|
||||||
string filePath = $"{resultPath}/{nameof(DebugReport)}_{_currentReport.FrameCount}.json";
|
string filePath = $"{resultPath}/{nameof(DebugReport)}_{_currentReport.FrameCount}.json";
|
||||||
string fileContent = JsonUtility.ToJson(_currentReport, true);
|
string fileContent = JsonUtility.ToJson(_currentReport, true);
|
||||||
FileUtility.CreateFile(filePath, fileContent);
|
FileUtility.WriteAllText(filePath, fileContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void OnSearchKeyWordChange(ChangeEvent<string> e)
|
private void OnSearchKeyWordChange(ChangeEvent<string> e)
|
||||||
|
|
|
@ -35,12 +35,18 @@ namespace YooAsset.Editor
|
||||||
/// 资源包视图
|
/// 资源包视图
|
||||||
/// </summary>
|
/// </summary>
|
||||||
BundleView,
|
BundleView,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 冗余资源试图
|
||||||
|
/// </summary>
|
||||||
|
Redundancy,
|
||||||
}
|
}
|
||||||
|
|
||||||
private ToolbarMenu _viewModeMenu;
|
private ToolbarMenu _viewModeMenu;
|
||||||
private ReporterSummaryViewer _summaryViewer;
|
private ReporterSummaryViewer _summaryViewer;
|
||||||
private ReporterAssetListViewer _assetListViewer;
|
private ReporterAssetListViewer _assetListViewer;
|
||||||
private ReporterBundleListViewer _bundleListViewer;
|
private ReporterBundleListViewer _bundleListViewer;
|
||||||
|
private ReporterRedundancyListViewer _redundancyListViewer;
|
||||||
|
|
||||||
private EViewMode _viewMode;
|
private EViewMode _viewMode;
|
||||||
private BuildReport _buildReport;
|
private BuildReport _buildReport;
|
||||||
|
@ -70,6 +76,7 @@ namespace YooAsset.Editor
|
||||||
_viewModeMenu.menu.AppendAction(EViewMode.Summary.ToString(), ViewModeMenuAction0, ViewModeMenuFun0);
|
_viewModeMenu.menu.AppendAction(EViewMode.Summary.ToString(), ViewModeMenuAction0, ViewModeMenuFun0);
|
||||||
_viewModeMenu.menu.AppendAction(EViewMode.AssetView.ToString(), ViewModeMenuAction1, ViewModeMenuFun1);
|
_viewModeMenu.menu.AppendAction(EViewMode.AssetView.ToString(), ViewModeMenuAction1, ViewModeMenuFun1);
|
||||||
_viewModeMenu.menu.AppendAction(EViewMode.BundleView.ToString(), ViewModeMenuAction2, ViewModeMenuFun2);
|
_viewModeMenu.menu.AppendAction(EViewMode.BundleView.ToString(), ViewModeMenuAction2, ViewModeMenuFun2);
|
||||||
|
_viewModeMenu.menu.AppendAction(EViewMode.Redundancy.ToString(), ViewModeMenuAction3, ViewModeMenuFun3);
|
||||||
|
|
||||||
// 搜索栏
|
// 搜索栏
|
||||||
var searchField = root.Q<ToolbarSearchField>("SearchField");
|
var searchField = root.Q<ToolbarSearchField>("SearchField");
|
||||||
|
@ -87,6 +94,10 @@ namespace YooAsset.Editor
|
||||||
_bundleListViewer = new ReporterBundleListViewer();
|
_bundleListViewer = new ReporterBundleListViewer();
|
||||||
_bundleListViewer.InitViewer();
|
_bundleListViewer.InitViewer();
|
||||||
|
|
||||||
|
// 加载试图
|
||||||
|
_redundancyListViewer = new ReporterRedundancyListViewer();
|
||||||
|
_redundancyListViewer.InitViewer();
|
||||||
|
|
||||||
// 显示视图
|
// 显示视图
|
||||||
_viewMode = EViewMode.Summary;
|
_viewMode = EViewMode.Summary;
|
||||||
_viewModeMenu.text = EViewMode.Summary.ToString();
|
_viewModeMenu.text = EViewMode.Summary.ToString();
|
||||||
|
@ -111,9 +122,10 @@ namespace YooAsset.Editor
|
||||||
_reportFilePath = selectFilePath;
|
_reportFilePath = selectFilePath;
|
||||||
string jsonData = FileUtility.ReadAllText(_reportFilePath);
|
string jsonData = FileUtility.ReadAllText(_reportFilePath);
|
||||||
_buildReport = BuildReport.Deserialize(jsonData);
|
_buildReport = BuildReport.Deserialize(jsonData);
|
||||||
|
_summaryViewer.FillViewData(_buildReport);
|
||||||
_assetListViewer.FillViewData(_buildReport, _searchKeyWord);
|
_assetListViewer.FillViewData(_buildReport, _searchKeyWord);
|
||||||
_bundleListViewer.FillViewData(_buildReport, _reportFilePath, _searchKeyWord);
|
_bundleListViewer.FillViewData(_buildReport, _reportFilePath, _searchKeyWord);
|
||||||
_summaryViewer.FillViewData(_buildReport);
|
_redundancyListViewer.FillViewData(_buildReport, _searchKeyWord);
|
||||||
}
|
}
|
||||||
private void OnSearchKeyWordChange(ChangeEvent<string> e)
|
private void OnSearchKeyWordChange(ChangeEvent<string> e)
|
||||||
{
|
{
|
||||||
|
@ -134,6 +146,7 @@ namespace YooAsset.Editor
|
||||||
_summaryViewer.AttachParent(root);
|
_summaryViewer.AttachParent(root);
|
||||||
_assetListViewer.DetachParent();
|
_assetListViewer.DetachParent();
|
||||||
_bundleListViewer.DetachParent();
|
_bundleListViewer.DetachParent();
|
||||||
|
_redundancyListViewer.DetachParent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void ViewModeMenuAction1(DropdownMenuAction action)
|
private void ViewModeMenuAction1(DropdownMenuAction action)
|
||||||
|
@ -146,6 +159,7 @@ namespace YooAsset.Editor
|
||||||
_summaryViewer.DetachParent();
|
_summaryViewer.DetachParent();
|
||||||
_assetListViewer.AttachParent(root);
|
_assetListViewer.AttachParent(root);
|
||||||
_bundleListViewer.DetachParent();
|
_bundleListViewer.DetachParent();
|
||||||
|
_redundancyListViewer.DetachParent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void ViewModeMenuAction2(DropdownMenuAction action)
|
private void ViewModeMenuAction2(DropdownMenuAction action)
|
||||||
|
@ -158,6 +172,20 @@ namespace YooAsset.Editor
|
||||||
_summaryViewer.DetachParent();
|
_summaryViewer.DetachParent();
|
||||||
_assetListViewer.DetachParent();
|
_assetListViewer.DetachParent();
|
||||||
_bundleListViewer.AttachParent(root);
|
_bundleListViewer.AttachParent(root);
|
||||||
|
_redundancyListViewer.DetachParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void ViewModeMenuAction3(DropdownMenuAction action)
|
||||||
|
{
|
||||||
|
if (_viewMode != EViewMode.Redundancy)
|
||||||
|
{
|
||||||
|
_viewMode = EViewMode.Redundancy;
|
||||||
|
VisualElement root = this.rootVisualElement;
|
||||||
|
_viewModeMenu.text = EViewMode.Redundancy.ToString();
|
||||||
|
_summaryViewer.DetachParent();
|
||||||
|
_assetListViewer.DetachParent();
|
||||||
|
_bundleListViewer.DetachParent();
|
||||||
|
_redundancyListViewer.AttachParent(root);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private DropdownMenuAction.Status ViewModeMenuFun0(DropdownMenuAction action)
|
private DropdownMenuAction.Status ViewModeMenuFun0(DropdownMenuAction action)
|
||||||
|
@ -181,6 +209,13 @@ namespace YooAsset.Editor
|
||||||
else
|
else
|
||||||
return DropdownMenuAction.Status.Normal;
|
return DropdownMenuAction.Status.Normal;
|
||||||
}
|
}
|
||||||
|
private DropdownMenuAction.Status ViewModeMenuFun3(DropdownMenuAction action)
|
||||||
|
{
|
||||||
|
if (_viewMode == EViewMode.Redundancy)
|
||||||
|
return DropdownMenuAction.Status.Checked;
|
||||||
|
else
|
||||||
|
return DropdownMenuAction.Status.Normal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
|
@ -0,0 +1,317 @@
|
||||||
|
#if UNITY_2019_4_OR_NEWER
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor.UIElements;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
|
||||||
|
namespace YooAsset.Editor
|
||||||
|
{
|
||||||
|
internal class ReporterRedundancyListViewer
|
||||||
|
{
|
||||||
|
private enum ESortMode
|
||||||
|
{
|
||||||
|
AssetPath,
|
||||||
|
AssetType,
|
||||||
|
FileSize,
|
||||||
|
Number,
|
||||||
|
}
|
||||||
|
|
||||||
|
private VisualTreeAsset _visualAsset;
|
||||||
|
private TemplateContainer _root;
|
||||||
|
|
||||||
|
private ToolbarButton _topBar1;
|
||||||
|
private ToolbarButton _topBar2;
|
||||||
|
private ToolbarButton _topBar3;
|
||||||
|
private ToolbarButton _topBar4;
|
||||||
|
private ListView _assetListView;
|
||||||
|
|
||||||
|
private BuildReport _buildReport;
|
||||||
|
private string _searchKeyWord;
|
||||||
|
private ESortMode _sortMode = ESortMode.AssetPath;
|
||||||
|
private bool _descendingSort = false;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化页面
|
||||||
|
/// </summary>
|
||||||
|
public void InitViewer()
|
||||||
|
{
|
||||||
|
// 加载布局文件
|
||||||
|
_visualAsset = UxmlLoader.LoadWindowUXML<ReporterRedundancyListViewer>();
|
||||||
|
if (_visualAsset == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_root = _visualAsset.CloneTree();
|
||||||
|
_root.style.flexGrow = 1f;
|
||||||
|
|
||||||
|
// 顶部按钮栏
|
||||||
|
_topBar1 = _root.Q<ToolbarButton>("TopBar1");
|
||||||
|
_topBar2 = _root.Q<ToolbarButton>("TopBar2");
|
||||||
|
_topBar3 = _root.Q<ToolbarButton>("TopBar3");
|
||||||
|
_topBar4 = _root.Q<ToolbarButton>("TopBar4");
|
||||||
|
_topBar1.clicked += TopBar1_clicked;
|
||||||
|
_topBar2.clicked += TopBar2_clicked;
|
||||||
|
_topBar3.clicked += TopBar3_clicked;
|
||||||
|
_topBar4.clicked += TopBar4_clicked;
|
||||||
|
|
||||||
|
// 资源列表
|
||||||
|
_assetListView = _root.Q<ListView>("TopListView");
|
||||||
|
_assetListView.makeItem = MakeAssetListViewItem;
|
||||||
|
_assetListView.bindItem = BindAssetListViewItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 填充页面数据
|
||||||
|
/// </summary>
|
||||||
|
public void FillViewData(BuildReport buildReport, string searchKeyWord)
|
||||||
|
{
|
||||||
|
_buildReport = buildReport;
|
||||||
|
_searchKeyWord = searchKeyWord;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
private void RefreshView()
|
||||||
|
{
|
||||||
|
_assetListView.Clear();
|
||||||
|
_assetListView.ClearSelection();
|
||||||
|
_assetListView.itemsSource = FilterAndSortViewItems();
|
||||||
|
_assetListView.Rebuild();
|
||||||
|
RefreshSortingSymbol();
|
||||||
|
}
|
||||||
|
private List<ReportRedundancyInfo> FilterAndSortViewItems()
|
||||||
|
{
|
||||||
|
List<ReportRedundancyInfo> result = new List<ReportRedundancyInfo>(_buildReport.RedundancyInfos.Count);
|
||||||
|
|
||||||
|
// 过滤列表
|
||||||
|
foreach (var redundancyInfo in _buildReport.RedundancyInfos)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_searchKeyWord) == false)
|
||||||
|
{
|
||||||
|
if (redundancyInfo.AssetPath.Contains(_searchKeyWord) == false)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result.Add(redundancyInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排序列表
|
||||||
|
if (_sortMode == ESortMode.AssetPath)
|
||||||
|
{
|
||||||
|
if (_descendingSort)
|
||||||
|
return result.OrderByDescending(a => a.AssetPath).ToList();
|
||||||
|
else
|
||||||
|
return result.OrderBy(a => a.AssetPath).ToList();
|
||||||
|
}
|
||||||
|
else if(_sortMode == ESortMode.AssetType)
|
||||||
|
{
|
||||||
|
if (_descendingSort)
|
||||||
|
return result.OrderByDescending(a => a.AssetType).ToList();
|
||||||
|
else
|
||||||
|
return result.OrderBy(a => a.AssetType).ToList();
|
||||||
|
}
|
||||||
|
else if (_sortMode == ESortMode.FileSize)
|
||||||
|
{
|
||||||
|
if (_descendingSort)
|
||||||
|
return result.OrderByDescending(a => a.FileSize).ToList();
|
||||||
|
else
|
||||||
|
return result.OrderBy(a => a.FileSize).ToList();
|
||||||
|
}
|
||||||
|
else if (_sortMode == ESortMode.Number)
|
||||||
|
{
|
||||||
|
if (_descendingSort)
|
||||||
|
return result.OrderByDescending(a => a.Number).ToList();
|
||||||
|
else
|
||||||
|
return result.OrderBy(a => a.Number).ToList();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void RefreshSortingSymbol()
|
||||||
|
{
|
||||||
|
_topBar1.text = $"Asset Path ({_assetListView.itemsSource.Count})";
|
||||||
|
_topBar2.text = "Asset Type";
|
||||||
|
_topBar3.text = "File Size";
|
||||||
|
_topBar4.text = "Redundancy Num";
|
||||||
|
|
||||||
|
if (_sortMode == ESortMode.AssetPath)
|
||||||
|
{
|
||||||
|
if (_descendingSort)
|
||||||
|
_topBar1.text = $"Asset Path ({_assetListView.itemsSource.Count}) ↓";
|
||||||
|
else
|
||||||
|
_topBar1.text = $"Asset Path ({_assetListView.itemsSource.Count}) ↑";
|
||||||
|
}
|
||||||
|
else if(_sortMode == ESortMode.AssetType)
|
||||||
|
{
|
||||||
|
if (_descendingSort)
|
||||||
|
_topBar2.text = "Asset Type ↓";
|
||||||
|
else
|
||||||
|
_topBar2.text = "Asset Type ↑";
|
||||||
|
}
|
||||||
|
else if (_sortMode == ESortMode.FileSize)
|
||||||
|
{
|
||||||
|
if (_descendingSort)
|
||||||
|
_topBar3.text = "File Size ↓";
|
||||||
|
else
|
||||||
|
_topBar3.text = "File Size ↑";
|
||||||
|
}
|
||||||
|
else if (_sortMode == ESortMode.Number)
|
||||||
|
{
|
||||||
|
if (_descendingSort)
|
||||||
|
_topBar4.text = "Redundancy Num ↓";
|
||||||
|
else
|
||||||
|
_topBar4.text = "Redundancy Num ↑";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 挂接到父类页面上
|
||||||
|
/// </summary>
|
||||||
|
public void AttachParent(VisualElement parent)
|
||||||
|
{
|
||||||
|
parent.Add(_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从父类页面脱离开
|
||||||
|
/// </summary>
|
||||||
|
public void DetachParent()
|
||||||
|
{
|
||||||
|
_root.RemoveFromHierarchy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 资源列表相关
|
||||||
|
private VisualElement MakeAssetListViewItem()
|
||||||
|
{
|
||||||
|
VisualElement element = new VisualElement();
|
||||||
|
element.style.flexDirection = FlexDirection.Row;
|
||||||
|
|
||||||
|
{
|
||||||
|
var label = new Label();
|
||||||
|
label.name = "Label1";
|
||||||
|
label.style.unityTextAlign = TextAnchor.MiddleLeft;
|
||||||
|
label.style.marginLeft = 3f;
|
||||||
|
label.style.flexGrow = 1f;
|
||||||
|
label.style.width = 280;
|
||||||
|
element.Add(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var label = new Label();
|
||||||
|
label.name = "Label2";
|
||||||
|
label.style.unityTextAlign = TextAnchor.MiddleLeft;
|
||||||
|
label.style.marginLeft = 3f;
|
||||||
|
label.style.flexGrow = 0;
|
||||||
|
label.style.width = 125;
|
||||||
|
element.Add(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var label = new Label();
|
||||||
|
label.name = "Label3";
|
||||||
|
label.style.unityTextAlign = TextAnchor.MiddleLeft;
|
||||||
|
label.style.marginLeft = 3f;
|
||||||
|
label.style.flexGrow = 0;
|
||||||
|
label.style.width = 125;
|
||||||
|
element.Add(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var label = new Label();
|
||||||
|
label.name = "Label4";
|
||||||
|
label.style.unityTextAlign = TextAnchor.MiddleLeft;
|
||||||
|
label.style.marginLeft = 3f;
|
||||||
|
label.style.flexGrow = 0;
|
||||||
|
label.style.width = 125;
|
||||||
|
element.Add(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
private void BindAssetListViewItem(VisualElement element, int index)
|
||||||
|
{
|
||||||
|
var sourceData = _assetListView.itemsSource as List<ReportRedundancyInfo>;
|
||||||
|
var redundancyInfo = sourceData[index];
|
||||||
|
|
||||||
|
// Asset Path
|
||||||
|
var label1 = element.Q<Label>("Label1");
|
||||||
|
label1.text = redundancyInfo.AssetPath;
|
||||||
|
|
||||||
|
// Asset Type
|
||||||
|
var label2 = element.Q<Label>("Label2");
|
||||||
|
label2.text = redundancyInfo.AssetType;
|
||||||
|
|
||||||
|
// File Size
|
||||||
|
var label3 = element.Q<Label>("Label3");
|
||||||
|
label3.text = EditorUtility.FormatBytes(redundancyInfo.FileSize);
|
||||||
|
|
||||||
|
// Number
|
||||||
|
var label4 = element.Q<Label>("Label4");
|
||||||
|
label4.text = redundancyInfo.Number.ToString();
|
||||||
|
}
|
||||||
|
private void TopBar1_clicked()
|
||||||
|
{
|
||||||
|
if (_sortMode != ESortMode.AssetPath)
|
||||||
|
{
|
||||||
|
_sortMode = ESortMode.AssetPath;
|
||||||
|
_descendingSort = false;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_descendingSort = !_descendingSort;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void TopBar2_clicked()
|
||||||
|
{
|
||||||
|
if (_sortMode != ESortMode.AssetType)
|
||||||
|
{
|
||||||
|
_sortMode = ESortMode.AssetType;
|
||||||
|
_descendingSort = false;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_descendingSort = !_descendingSort;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void TopBar3_clicked()
|
||||||
|
{
|
||||||
|
if (_sortMode != ESortMode.FileSize)
|
||||||
|
{
|
||||||
|
_sortMode = ESortMode.FileSize;
|
||||||
|
_descendingSort = false;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_descendingSort = !_descendingSort;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void TopBar4_clicked()
|
||||||
|
{
|
||||||
|
if (_sortMode != ESortMode.Number)
|
||||||
|
{
|
||||||
|
_sortMode = ESortMode.Number;
|
||||||
|
_descendingSort = false;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_descendingSort = !_descendingSort;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a72c4edf1a81c9942a9d43e9d2a77b53
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,11 @@
|
||||||
|
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
|
||||||
|
<ui:VisualElement name="TopGroup" style="flex-grow: 1; border-left-width: 1px; border-right-width: 1px; border-top-width: 1px; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-right-color: rgb(0, 0, 0); border-top-color: rgb(0, 0, 0); border-bottom-color: rgb(0, 0, 0); margin-left: 0; margin-right: 0; margin-top: 2px; margin-bottom: 1px; display: flex;">
|
||||||
|
<uie:Toolbar name="TopBar" style="height: 25px; margin-left: 1px; margin-right: 1px;">
|
||||||
|
<uie:ToolbarButton text="Asset Path" display-tooltip-when-elided="true" name="TopBar1" style="width: 280px; -unity-text-align: middle-left; flex-grow: 1;" />
|
||||||
|
<uie:ToolbarButton text="Asset Type" display-tooltip-when-elided="true" name="TopBar2" style="width: 125px; -unity-text-align: middle-left; flex-grow: 0; flex-shrink: 1;" />
|
||||||
|
<uie:ToolbarButton text="File Size" display-tooltip-when-elided="true" name="TopBar3" style="width: 125px; -unity-text-align: middle-left; flex-grow: 0; flex-shrink: 1;" />
|
||||||
|
<uie:ToolbarButton text="Redundancy Num" display-tooltip-when-elided="true" name="TopBar4" style="width: 125px; -unity-text-align: middle-left; flex-grow: 0; flex-shrink: 1;" />
|
||||||
|
</uie:Toolbar>
|
||||||
|
<ui:ListView focusable="true" name="TopListView" item-height="18" virtualization-method="DynamicHeight" style="flex-grow: 1; flex-basis: 60px;" />
|
||||||
|
</ui:VisualElement>
|
||||||
|
</ui:UXML>
|
|
@ -0,0 +1,10 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a5296d9c037ce3944b5c197cbdd78a8b
|
||||||
|
ScriptedImporter:
|
||||||
|
internalIDToNameTable: []
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}
|
|
@ -68,7 +68,9 @@ namespace YooAsset.Editor
|
||||||
|
|
||||||
_items.Add(new ItemWrapper("启用可寻址资源定位", $"{buildReport.Summary.EnableAddressable}"));
|
_items.Add(new ItemWrapper("启用可寻址资源定位", $"{buildReport.Summary.EnableAddressable}"));
|
||||||
_items.Add(new ItemWrapper("资源包名唯一化", $"{buildReport.Summary.UniqueBundleName}"));
|
_items.Add(new ItemWrapper("资源包名唯一化", $"{buildReport.Summary.UniqueBundleName}"));
|
||||||
_items.Add(new ItemWrapper("加密服务类名称", $"{buildReport.Summary.EncryptionServicesClassName}"));
|
_items.Add(new ItemWrapper("自动分析冗余资源", $"{buildReport.Summary.AutoAnalyzeRedundancy}"));
|
||||||
|
_items.Add(new ItemWrapper("共享资源的打包类名称", buildReport.Summary.ShareAssetPackRuleClassName));
|
||||||
|
_items.Add(new ItemWrapper("加密服务类名称", buildReport.Summary.EncryptionServicesClassName));
|
||||||
|
|
||||||
_items.Add(new ItemWrapper(string.Empty, string.Empty));
|
_items.Add(new ItemWrapper(string.Empty, string.Empty));
|
||||||
_items.Add(new ItemWrapper("构建参数", string.Empty));
|
_items.Add(new ItemWrapper("构建参数", string.Empty));
|
||||||
|
|
|
@ -286,6 +286,18 @@ namespace YooAsset.Editor
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region StringUtility
|
#region StringUtility
|
||||||
|
public static string RemoveFirstChar(string str)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(str))
|
||||||
|
return str;
|
||||||
|
return str.Substring(1);
|
||||||
|
}
|
||||||
|
public static string RemoveLastChar(string str)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(str))
|
||||||
|
return str;
|
||||||
|
return str.Substring(0, str.Length - 1);
|
||||||
|
}
|
||||||
public static List<string> StringToStringList(string str, char separator)
|
public static List<string> StringToStringList(string str, char separator)
|
||||||
{
|
{
|
||||||
List<string> result = new List<string>();
|
List<string> result = new List<string>();
|
||||||
|
@ -303,7 +315,6 @@ namespace YooAsset.Editor
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T NameToEnum<T>(string name)
|
public static T NameToEnum<T>(string name)
|
||||||
{
|
{
|
||||||
if (Enum.IsDefined(typeof(T), name) == false)
|
if (Enum.IsDefined(typeof(T), name) == false)
|
||||||
|
|
|
@ -86,9 +86,7 @@ namespace YooAsset
|
||||||
if (IsValidWithWarning == false)
|
if (IsValidWithWarning == false)
|
||||||
return null;
|
return null;
|
||||||
string filePath = Provider.RawFilePath;
|
string filePath = Provider.RawFilePath;
|
||||||
if (File.Exists(filePath) == false)
|
return FileUtility.ReadAllText(filePath);
|
||||||
return null;
|
|
||||||
return File.ReadAllText(filePath, Encoding.UTF8);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -103,13 +103,14 @@ namespace YooAsset
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_steps = ESteps.LoadFile;
|
_steps = ESteps.LoadFile;
|
||||||
|
return; //下载完毕等待一帧再去加载!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 内置文件解压
|
// 3. 内置文件解压
|
||||||
if (_steps == ESteps.Unpack)
|
if (_steps == ESteps.Unpack)
|
||||||
{
|
{
|
||||||
int failedTryAgain = 1;
|
int failedTryAgain = Impl.DownloadFailedTryAgain;
|
||||||
var bundleInfo = ManifestTools.GetUnpackInfo(MainBundleInfo.Bundle);
|
var bundleInfo = ManifestTools.GetUnpackInfo(MainBundleInfo.Bundle);
|
||||||
_unpacker = DownloadSystem.BeginDownload(bundleInfo, failedTryAgain);
|
_unpacker = DownloadSystem.BeginDownload(bundleInfo, failedTryAgain);
|
||||||
_steps = ESteps.CheckUnpack;
|
_steps = ESteps.CheckUnpack;
|
||||||
|
@ -245,7 +246,7 @@ namespace YooAsset
|
||||||
var result = CacheSystem.VerifyingRecordFile(MainBundleInfo.Bundle.PackageName, MainBundleInfo.Bundle.CacheGUID);
|
var result = CacheSystem.VerifyingRecordFile(MainBundleInfo.Bundle.PackageName, MainBundleInfo.Bundle.CacheGUID);
|
||||||
if (result != EVerifyResult.Succeed)
|
if (result != EVerifyResult.Succeed)
|
||||||
{
|
{
|
||||||
YooLogger.Error($"Found possibly corrupt file ! {MainBundleInfo.Bundle.CacheGUID}");
|
YooLogger.Error($"Found possibly corrupt file ! {MainBundleInfo.Bundle.CacheGUID} Verify result : {result}");
|
||||||
CacheSystem.DiscardFile(MainBundleInfo.Bundle.PackageName, MainBundleInfo.Bundle.CacheGUID);
|
CacheSystem.DiscardFile(MainBundleInfo.Bundle.PackageName, MainBundleInfo.Bundle.CacheGUID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,7 @@ namespace YooAsset
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_steps = ESteps.LoadCacheFile;
|
_steps = ESteps.LoadCacheFile;
|
||||||
|
return; //下载完毕等待一帧再去加载!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ namespace YooAsset
|
||||||
// 3. 解压内置文件
|
// 3. 解压内置文件
|
||||||
if (_steps == ESteps.Unpack)
|
if (_steps == ESteps.Unpack)
|
||||||
{
|
{
|
||||||
int failedTryAgain = 1;
|
int failedTryAgain = Impl.DownloadFailedTryAgain;
|
||||||
var bundleInfo = ManifestTools.GetUnpackInfo(MainBundleInfo.Bundle);
|
var bundleInfo = ManifestTools.GetUnpackInfo(MainBundleInfo.Bundle);
|
||||||
_unpacker = DownloadSystem.BeginDownload(bundleInfo, failedTryAgain);
|
_unpacker = DownloadSystem.BeginDownload(bundleInfo, failedTryAgain);
|
||||||
_steps = ESteps.CheckUnpack;
|
_steps = ESteps.CheckUnpack;
|
||||||
|
|
|
@ -89,7 +89,7 @@ namespace YooAsset
|
||||||
// 3. 从站点下载
|
// 3. 从站点下载
|
||||||
if (_steps == ESteps.Website)
|
if (_steps == ESteps.Website)
|
||||||
{
|
{
|
||||||
int failedTryAgain = 1;
|
int failedTryAgain = Impl.DownloadFailedTryAgain;
|
||||||
var bundleInfo = ManifestTools.GetUnpackInfo(MainBundleInfo.Bundle);
|
var bundleInfo = ManifestTools.GetUnpackInfo(MainBundleInfo.Bundle);
|
||||||
_website = DownloadSystem.BeginDownload(bundleInfo, failedTryAgain);
|
_website = DownloadSystem.BeginDownload(bundleInfo, failedTryAgain);
|
||||||
_steps = ESteps.CheckWebsite;
|
_steps = ESteps.CheckWebsite;
|
||||||
|
|
|
@ -6,6 +6,11 @@ namespace YooAsset
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal enum EVerifyResult
|
internal enum EVerifyResult
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 验证异常
|
||||||
|
/// </summary>
|
||||||
|
Exception = -7,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 未找到缓存信息
|
/// 未找到缓存信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -37,9 +42,9 @@ namespace YooAsset
|
||||||
FileCrcError = -1,
|
FileCrcError = -1,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 验证异常
|
/// 默认状态(校验未完成)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Exception = 0,
|
None = 0,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 验证成功
|
/// 验证成功
|
||||||
|
|
|
@ -60,11 +60,12 @@ namespace YooAsset
|
||||||
|
|
||||||
if (_steps == ESteps.Waiting)
|
if (_steps == ESteps.Waiting)
|
||||||
{
|
{
|
||||||
if (_element.IsDone == false)
|
int result = _element.Result;
|
||||||
|
if (result == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
VerifyResult = _element.Result;
|
VerifyResult = (EVerifyResult)result;
|
||||||
if (_element.Result == EVerifyResult.Succeed)
|
if (VerifyResult == EVerifyResult.Succeed)
|
||||||
{
|
{
|
||||||
_steps = ESteps.Done;
|
_steps = ESteps.Done;
|
||||||
Status = EOperationStatus.Succeed;
|
Status = EOperationStatus.Succeed;
|
||||||
|
@ -73,7 +74,7 @@ namespace YooAsset
|
||||||
{
|
{
|
||||||
_steps = ESteps.Done;
|
_steps = ESteps.Done;
|
||||||
Status = EOperationStatus.Failed;
|
Status = EOperationStatus.Failed;
|
||||||
Error = $"Failed verify file : {_element.TempDataFilePath} ! ErrorCode : {_element.Result}";
|
Error = $"Failed verify file : {_element.TempDataFilePath} ! ErrorCode : {VerifyResult}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,8 +86,8 @@ namespace YooAsset
|
||||||
private void VerifyInThread(object obj)
|
private void VerifyInThread(object obj)
|
||||||
{
|
{
|
||||||
VerifyTempElement element = (VerifyTempElement)obj;
|
VerifyTempElement element = (VerifyTempElement)obj;
|
||||||
element.Result = CacheSystem.VerifyingTempFile(element);
|
int result = (int)CacheSystem.VerifyingTempFile(element);
|
||||||
element.IsDone = true;
|
element.Result = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,11 +121,10 @@ namespace YooAsset
|
||||||
|
|
||||||
if (_steps == ESteps.VerifyFile)
|
if (_steps == ESteps.VerifyFile)
|
||||||
{
|
{
|
||||||
_element.Result = CacheSystem.VerifyingTempFile(_element);
|
_element.Result = (int)CacheSystem.VerifyingTempFile(_element);
|
||||||
_element.IsDone = true;
|
|
||||||
|
|
||||||
VerifyResult = _element.Result;
|
VerifyResult = (EVerifyResult)_element.Result;
|
||||||
if (_element.Result == EVerifyResult.Succeed)
|
if (VerifyResult == EVerifyResult.Succeed)
|
||||||
{
|
{
|
||||||
_steps = ESteps.Done;
|
_steps = ESteps.Done;
|
||||||
Status = EOperationStatus.Succeed;
|
Status = EOperationStatus.Succeed;
|
||||||
|
@ -133,7 +133,7 @@ namespace YooAsset
|
||||||
{
|
{
|
||||||
_steps = ESteps.Done;
|
_steps = ESteps.Done;
|
||||||
Status = EOperationStatus.Failed;
|
Status = EOperationStatus.Failed;
|
||||||
Error = $"Failed verify file : {_element.TempDataFilePath} ! ErrorCode : {_element.Result}";
|
Error = $"Failed verify file : {_element.TempDataFilePath} ! ErrorCode : {VerifyResult}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ namespace YooAsset
|
||||||
{
|
{
|
||||||
internal static class PersistentTools
|
internal static class PersistentTools
|
||||||
{
|
{
|
||||||
|
private const string SandboxFolderName = "Sandbox";
|
||||||
private const string CacheFolderName = "CacheFiles";
|
private const string CacheFolderName = "CacheFiles";
|
||||||
private const string CachedBundleFileFolder = "BundleFiles";
|
private const string CachedBundleFileFolder = "BundleFiles";
|
||||||
private const string CachedRawFileFolder = "RawFiles";
|
private const string CachedRawFileFolder = "RawFiles";
|
||||||
|
@ -32,29 +33,24 @@ namespace YooAsset
|
||||||
// 注意:为了方便调试查看,编辑器下把存储目录放到项目里
|
// 注意:为了方便调试查看,编辑器下把存储目录放到项目里
|
||||||
if (string.IsNullOrEmpty(_sandboxPath))
|
if (string.IsNullOrEmpty(_sandboxPath))
|
||||||
{
|
{
|
||||||
string directory = Path.GetDirectoryName(UnityEngine.Application.dataPath);
|
string projectPath = Path.GetDirectoryName(UnityEngine.Application.dataPath);
|
||||||
string projectPath = GetRegularPath(directory);
|
projectPath = PathUtility.RegularPath(projectPath);
|
||||||
_sandboxPath = StringUtility.Format("{0}/Sandbox", projectPath);
|
_sandboxPath = PathUtility.Combine(projectPath, SandboxFolderName);
|
||||||
}
|
}
|
||||||
#elif UNITY_STANDALONE
|
#elif UNITY_STANDALONE
|
||||||
if (string.IsNullOrEmpty(_sandboxPath))
|
if (string.IsNullOrEmpty(_sandboxPath))
|
||||||
{
|
{
|
||||||
_sandboxPath = StringUtility.Format("{0}/Sandbox", UnityEngine.Application.dataPath);
|
_sandboxPath = PathUtility.Combine(UnityEngine.Application.dataPath, SandboxFolderName);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (string.IsNullOrEmpty(_sandboxPath))
|
if (string.IsNullOrEmpty(_sandboxPath))
|
||||||
{
|
{
|
||||||
_sandboxPath = StringUtility.Format("{0}/Sandbox", UnityEngine.Application.persistentDataPath);
|
_sandboxPath = PathUtility.Combine(UnityEngine.Application.persistentDataPath, SandboxFolderName);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return _sandboxPath;
|
return _sandboxPath;
|
||||||
}
|
}
|
||||||
private static string GetRegularPath(string path)
|
|
||||||
{
|
|
||||||
return path.Replace('\\', '/').Replace("\\", "/"); //替换为Linux路径格式
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取基于流文件夹的加载路径
|
/// 获取基于流文件夹的加载路径
|
||||||
|
@ -63,9 +59,9 @@ namespace YooAsset
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(_buildinPath))
|
if (string.IsNullOrEmpty(_buildinPath))
|
||||||
{
|
{
|
||||||
_buildinPath = StringUtility.Format("{0}/{1}", UnityEngine.Application.streamingAssetsPath, YooAssetSettings.StreamingAssetsBuildinFolder);
|
_buildinPath = PathUtility.Combine(UnityEngine.Application.streamingAssetsPath, YooAssetSettings.StreamingAssetsBuildinFolder);
|
||||||
}
|
}
|
||||||
return StringUtility.Format("{0}/{1}", _buildinPath, path);
|
return PathUtility.Combine(_buildinPath, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -74,7 +70,17 @@ namespace YooAsset
|
||||||
public static string MakePersistentLoadPath(string path)
|
public static string MakePersistentLoadPath(string path)
|
||||||
{
|
{
|
||||||
string root = GetPersistentRootPath();
|
string root = GetPersistentRootPath();
|
||||||
return StringUtility.Format("{0}/{1}", root, path);
|
return PathUtility.Combine(root, path);
|
||||||
|
}
|
||||||
|
public static string MakePersistentLoadPath(string path1, string path2)
|
||||||
|
{
|
||||||
|
string root = GetPersistentRootPath();
|
||||||
|
return PathUtility.Combine(root, path1, path2);
|
||||||
|
}
|
||||||
|
public static string MakePersistentLoadPath(string path1, string path2, string path3)
|
||||||
|
{
|
||||||
|
string root = GetPersistentRootPath();
|
||||||
|
return PathUtility.Combine(root, path1, path2, path3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -101,7 +107,7 @@ namespace YooAsset
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void DeleteSandbox()
|
public static void DeleteSandbox()
|
||||||
{
|
{
|
||||||
string directoryPath = MakePersistentLoadPath(string.Empty);
|
string directoryPath = GetPersistentRootPath();
|
||||||
if (Directory.Exists(directoryPath))
|
if (Directory.Exists(directoryPath))
|
||||||
Directory.Delete(directoryPath, true);
|
Directory.Delete(directoryPath, true);
|
||||||
}
|
}
|
||||||
|
@ -135,8 +141,7 @@ namespace YooAsset
|
||||||
{
|
{
|
||||||
if (_cachedBundleFileFolder.TryGetValue(packageName, out string value) == false)
|
if (_cachedBundleFileFolder.TryGetValue(packageName, out string value) == false)
|
||||||
{
|
{
|
||||||
string root = MakePersistentLoadPath(CacheFolderName);
|
value = MakePersistentLoadPath(CacheFolderName, packageName, CachedBundleFileFolder);
|
||||||
value = $"{root}/{packageName}/{CachedBundleFileFolder}";
|
|
||||||
_cachedBundleFileFolder.Add(packageName, value);
|
_cachedBundleFileFolder.Add(packageName, value);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
|
@ -150,8 +155,7 @@ namespace YooAsset
|
||||||
{
|
{
|
||||||
if (_cachedRawFileFolder.TryGetValue(packageName, out string value) == false)
|
if (_cachedRawFileFolder.TryGetValue(packageName, out string value) == false)
|
||||||
{
|
{
|
||||||
string root = MakePersistentLoadPath(CacheFolderName);
|
value = MakePersistentLoadPath(CacheFolderName, packageName, CachedRawFileFolder);
|
||||||
value = $"{root}/{packageName}/{CachedRawFileFolder}";
|
|
||||||
_cachedRawFileFolder.Add(packageName, value);
|
_cachedRawFileFolder.Add(packageName, value);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
|
@ -171,7 +175,7 @@ namespace YooAsset
|
||||||
public static string GetCacheManifestFilePath(string packageName, string packageVersion)
|
public static string GetCacheManifestFilePath(string packageName, string packageVersion)
|
||||||
{
|
{
|
||||||
string fileName = YooAssetSettingsData.GetManifestBinaryFileName(packageName, packageVersion);
|
string fileName = YooAssetSettingsData.GetManifestBinaryFileName(packageName, packageVersion);
|
||||||
return MakePersistentLoadPath($"{ManifestFolderName}/{fileName}");
|
return MakePersistentLoadPath(ManifestFolderName, fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -180,7 +184,7 @@ namespace YooAsset
|
||||||
public static string GetCachePackageHashFilePath(string packageName, string packageVersion)
|
public static string GetCachePackageHashFilePath(string packageName, string packageVersion)
|
||||||
{
|
{
|
||||||
string fileName = YooAssetSettingsData.GetPackageHashFileName(packageName, packageVersion);
|
string fileName = YooAssetSettingsData.GetPackageHashFileName(packageName, packageVersion);
|
||||||
return MakePersistentLoadPath($"{ManifestFolderName}/{fileName}");
|
return MakePersistentLoadPath(ManifestFolderName, fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -189,7 +193,7 @@ namespace YooAsset
|
||||||
public static string GetCachePackageVersionFilePath(string packageName)
|
public static string GetCachePackageVersionFilePath(string packageName)
|
||||||
{
|
{
|
||||||
string fileName = YooAssetSettingsData.GetPackageVersionFileName(packageName);
|
string fileName = YooAssetSettingsData.GetPackageVersionFileName(packageName);
|
||||||
return MakePersistentLoadPath($"{ManifestFolderName}/{fileName}");
|
return MakePersistentLoadPath(ManifestFolderName, fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -198,7 +202,7 @@ namespace YooAsset
|
||||||
public static void SaveCachePackageVersionFile(string packageName, string version)
|
public static void SaveCachePackageVersionFile(string packageName, string version)
|
||||||
{
|
{
|
||||||
string filePath = GetCachePackageVersionFilePath(packageName);
|
string filePath = GetCachePackageVersionFilePath(packageName);
|
||||||
FileUtility.CreateFile(filePath, version);
|
FileUtility.WriteAllText(filePath, version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -49,8 +49,7 @@ namespace YooAsset
|
||||||
public string FileCRC { private set; get; }
|
public string FileCRC { private set; get; }
|
||||||
public long FileSize { private set; get; }
|
public long FileSize { private set; get; }
|
||||||
|
|
||||||
public bool IsDone = false;
|
public int Result = 0; // 注意:原子操作对象
|
||||||
public EVerifyResult Result;
|
|
||||||
|
|
||||||
public VerifyTempElement(string tempDataFilePath, string fileCRC, long fileSize)
|
public VerifyTempElement(string tempDataFilePath, string fileCRC, long fileSize)
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace YooAsset
|
||||||
public static void SerializeToJson(string savePath, PackageManifest manifest)
|
public static void SerializeToJson(string savePath, PackageManifest manifest)
|
||||||
{
|
{
|
||||||
string json = JsonUtility.ToJson(manifest, true);
|
string json = JsonUtility.ToJson(manifest, true);
|
||||||
FileUtility.CreateFile(savePath, json);
|
FileUtility.WriteAllText(savePath, json);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -410,7 +410,7 @@ namespace YooAsset
|
||||||
_footPrint = Application.buildGUID;
|
_footPrint = Application.buildGUID;
|
||||||
#endif
|
#endif
|
||||||
string footPrintFilePath = PersistentTools.GetAppFootPrintFilePath();
|
string footPrintFilePath = PersistentTools.GetAppFootPrintFilePath();
|
||||||
FileUtility.CreateFile(footPrintFilePath, _footPrint);
|
FileUtility.WriteAllText(footPrintFilePath, _footPrint);
|
||||||
YooLogger.Log($"Save application foot print : {_footPrint}");
|
YooLogger.Log($"Save application foot print : {_footPrint}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,12 +75,13 @@ namespace YooAsset
|
||||||
if (IsRawFile)
|
if (IsRawFile)
|
||||||
{
|
{
|
||||||
string cacheRoot = PersistentTools.GetCachedRawFileFolderPath(PackageName);
|
string cacheRoot = PersistentTools.GetCachedRawFileFolderPath(PackageName);
|
||||||
_cachedDataFilePath = $"{cacheRoot}/{folderName}/{CacheGUID}/{YooAssetSettings.CacheBundleDataFileName}{_fileExtension}";
|
_cachedDataFilePath = PathUtility.Combine(cacheRoot, folderName, CacheGUID, YooAssetSettings.CacheBundleDataFileName);
|
||||||
|
_cachedDataFilePath += _fileExtension;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string cacheRoot = PersistentTools.GetCachedBundleFileFolderPath(PackageName);
|
string cacheRoot = PersistentTools.GetCachedBundleFileFolderPath(PackageName);
|
||||||
_cachedDataFilePath = $"{cacheRoot}/{folderName}/{CacheGUID}/{YooAssetSettings.CacheBundleDataFileName}";
|
_cachedDataFilePath = PathUtility.Combine(cacheRoot, folderName, CacheGUID, YooAssetSettings.CacheBundleDataFileName);
|
||||||
}
|
}
|
||||||
return _cachedDataFilePath;
|
return _cachedDataFilePath;
|
||||||
}
|
}
|
||||||
|
@ -101,12 +102,12 @@ namespace YooAsset
|
||||||
if (IsRawFile)
|
if (IsRawFile)
|
||||||
{
|
{
|
||||||
string cacheRoot = PersistentTools.GetCachedRawFileFolderPath(PackageName);
|
string cacheRoot = PersistentTools.GetCachedRawFileFolderPath(PackageName);
|
||||||
_cachedInfoFilePath = $"{cacheRoot}/{folderName}/{CacheGUID}/{YooAssetSettings.CacheBundleInfoFileName}";
|
_cachedInfoFilePath = PathUtility.Combine(cacheRoot, folderName, CacheGUID, YooAssetSettings.CacheBundleInfoFileName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string cacheRoot = PersistentTools.GetCachedBundleFileFolderPath(PackageName);
|
string cacheRoot = PersistentTools.GetCachedBundleFileFolderPath(PackageName);
|
||||||
_cachedInfoFilePath = $"{cacheRoot}/{folderName}/{CacheGUID}/{YooAssetSettings.CacheBundleInfoFileName}";
|
_cachedInfoFilePath = PathUtility.Combine(cacheRoot, folderName, CacheGUID, YooAssetSettings.CacheBundleInfoFileName);
|
||||||
}
|
}
|
||||||
return _cachedInfoFilePath;
|
return _cachedInfoFilePath;
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,7 +114,7 @@ namespace YooAsset
|
||||||
// 添加无后缀名路径的映射
|
// 添加无后缀名路径的映射
|
||||||
if (Path.HasExtension(location))
|
if (Path.HasExtension(location))
|
||||||
{
|
{
|
||||||
string locationWithoutExtension = StringUtility.RemoveExtension(location);
|
string locationWithoutExtension = PathUtility.RemoveExtension(location);
|
||||||
if (AssetPathMapping.ContainsKey(locationWithoutExtension))
|
if (AssetPathMapping.ContainsKey(locationWithoutExtension))
|
||||||
YooLogger.Warning($"AssetPath have existed : {locationWithoutExtension}");
|
YooLogger.Warning($"AssetPath have existed : {locationWithoutExtension}");
|
||||||
else
|
else
|
||||||
|
|
|
@ -7,13 +7,67 @@ using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace YooAsset
|
namespace YooAsset
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 路径工具类
|
||||||
|
/// </summary>
|
||||||
|
internal static class PathUtility
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 路径归一化
|
||||||
|
/// 注意:替换为Linux路径格式
|
||||||
|
/// </summary>
|
||||||
|
public static string RegularPath(string path)
|
||||||
|
{
|
||||||
|
return path.Replace('\\', '/').Replace("\\", "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 移除路径里的后缀名
|
||||||
|
/// </summary>
|
||||||
|
public static string RemoveExtension(string str)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(str))
|
||||||
|
return str;
|
||||||
|
|
||||||
|
int index = str.LastIndexOf(".");
|
||||||
|
if (index == -1)
|
||||||
|
return str;
|
||||||
|
else
|
||||||
|
return str.Remove(index); //"assets/config/test.unity3d" --> "assets/config/test"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 合并路径
|
||||||
|
/// </summary>
|
||||||
|
public static string Combine(string path1, string path2)
|
||||||
|
{
|
||||||
|
return StringUtility.Format("{0}/{1}", path1, path2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 合并路径
|
||||||
|
/// </summary>
|
||||||
|
public static string Combine(string path1, string path2, string path3)
|
||||||
|
{
|
||||||
|
return StringUtility.Format("{0}/{1}/{2}", path1, path2, path3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 合并路径
|
||||||
|
/// </summary>
|
||||||
|
public static string Combine(string path1, string path2, string path3, string path4)
|
||||||
|
{
|
||||||
|
return StringUtility.Format("{0}/{1}/{2}/{3}", path1, path2, path3, path4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 字符串工具类
|
/// 字符串工具类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class StringUtility
|
internal static class StringUtility
|
||||||
{
|
{
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static StringBuilder _cacheBuilder = new StringBuilder(1024);
|
private static StringBuilder _cacheBuilder = new StringBuilder(2048);
|
||||||
|
|
||||||
public static string Format(string format, object arg0)
|
public static string Format(string format, object arg0)
|
||||||
{
|
{
|
||||||
|
@ -54,30 +108,6 @@ namespace YooAsset
|
||||||
_cacheBuilder.AppendFormat(format, args);
|
_cacheBuilder.AppendFormat(format, args);
|
||||||
return _cacheBuilder.ToString();
|
return _cacheBuilder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string RemoveFirstChar(string str)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(str))
|
|
||||||
return str;
|
|
||||||
return str.Substring(1);
|
|
||||||
}
|
|
||||||
public static string RemoveLastChar(string str)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(str))
|
|
||||||
return str;
|
|
||||||
return str.Substring(0, str.Length - 1);
|
|
||||||
}
|
|
||||||
public static string RemoveExtension(string str)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(str))
|
|
||||||
return str;
|
|
||||||
|
|
||||||
int index = str.LastIndexOf(".");
|
|
||||||
if (index == -1)
|
|
||||||
return str;
|
|
||||||
else
|
|
||||||
return str.Remove(index); //"assets/config/test.unity3d" --> "assets/config/test"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -106,46 +136,26 @@ namespace YooAsset
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建文件(如果已经存在则删除旧文件)
|
/// 写入文本数据(会覆盖指定路径的文件)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void CreateFile(string filePath, string content)
|
public static void WriteAllText(string filePath, string content)
|
||||||
{
|
{
|
||||||
// 删除旧文件
|
|
||||||
if (File.Exists(filePath))
|
|
||||||
File.Delete(filePath);
|
|
||||||
|
|
||||||
// 创建文件夹路径
|
// 创建文件夹路径
|
||||||
CreateFileDirectory(filePath);
|
CreateFileDirectory(filePath);
|
||||||
|
|
||||||
// 创建新文件
|
|
||||||
byte[] bytes = Encoding.UTF8.GetBytes(content);
|
byte[] bytes = Encoding.UTF8.GetBytes(content);
|
||||||
using (FileStream fs = File.Create(filePath))
|
File.WriteAllBytes(filePath, bytes); //避免写入BOM标记
|
||||||
{
|
|
||||||
fs.Write(bytes, 0, bytes.Length);
|
|
||||||
fs.Flush();
|
|
||||||
fs.Close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建文件(如果已经存在则删除旧文件)
|
/// 写入字节数据(会覆盖指定路径的文件)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void CreateFile(string filePath, byte[] data)
|
public static void WriteAllBytes(string filePath, byte[] data)
|
||||||
{
|
{
|
||||||
// 删除旧文件
|
|
||||||
if (File.Exists(filePath))
|
|
||||||
File.Delete(filePath);
|
|
||||||
|
|
||||||
// 创建文件夹路径
|
// 创建文件夹路径
|
||||||
CreateFileDirectory(filePath);
|
CreateFileDirectory(filePath);
|
||||||
|
|
||||||
// 创建新文件
|
File.WriteAllBytes(filePath, data);
|
||||||
using (FileStream fs = File.Create(filePath))
|
|
||||||
{
|
|
||||||
fs.Write(data, 0, data.Length);
|
|
||||||
fs.Flush();
|
|
||||||
fs.Close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -19,7 +19,10 @@ namespace YooAsset
|
||||||
public static void Initialize(ILogger logger = null)
|
public static void Initialize(ILogger logger = null)
|
||||||
{
|
{
|
||||||
if (_isInitialize)
|
if (_isInitialize)
|
||||||
throw new Exception($"{nameof(YooAssets)} is initialized !");
|
{
|
||||||
|
UnityEngine.Debug.LogWarning($"{nameof(YooAssets)} is initialized !");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_isInitialize == false)
|
if (_isInitialize == false)
|
||||||
{
|
{
|
||||||
|
@ -113,7 +116,7 @@ namespace YooAsset
|
||||||
{
|
{
|
||||||
var package = TryGetPackage(packageName);
|
var package = TryGetPackage(packageName);
|
||||||
if (package == null)
|
if (package == null)
|
||||||
YooLogger.Error($"Not found assets package : {packageName}");
|
YooLogger.Error($"Not found resource package : {packageName}");
|
||||||
return package;
|
return package;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,20 +47,6 @@ public class FileStreamEncryption : IEncryptionServices
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadFromFileOffset
|
|
||||||
if (fileInfo.BundleName.Contains("_gameres_uiimage"))
|
|
||||||
{
|
|
||||||
var fileData = File.ReadAllBytes(fileInfo.FilePath);
|
|
||||||
int offset = 32;
|
|
||||||
var temper = new byte[fileData.Length + offset];
|
|
||||||
Buffer.BlockCopy(fileData, 0, temper, offset, fileData.Length);
|
|
||||||
|
|
||||||
EncryptResult result = new EncryptResult();
|
|
||||||
result.LoadMethod = EBundleLoadMethod.LoadFromFileOffset;
|
|
||||||
result.EncryptedData = temper;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normal
|
// Normal
|
||||||
{
|
{
|
||||||
EncryptResult result = new EncryptResult();
|
EncryptResult result = new EncryptResult();
|
||||||
|
|
|
@ -8,7 +8,7 @@ public class BundleStream : FileStream
|
||||||
{
|
{
|
||||||
public const byte KEY = 64;
|
public const byte KEY = 64;
|
||||||
|
|
||||||
public BundleStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) : base(path, mode, access, share, bufferSize, useAsync)
|
public BundleStream(string path, FileMode mode, FileAccess access, FileShare share) : base(path, mode, access, share)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
public BundleStream(string path, FileMode mode) : base(path, mode)
|
public BundleStream(string path, FileMode mode) : base(path, mode)
|
||||||
|
|
|
@ -74,7 +74,7 @@ internal class FsmInitialize : IStateNode
|
||||||
}
|
}
|
||||||
|
|
||||||
yield return initializationOperation;
|
yield return initializationOperation;
|
||||||
if (package.InitializeStatus == EOperationStatus.Succeed)
|
if (initializationOperation.Status == EOperationStatus.Succeed)
|
||||||
{
|
{
|
||||||
_machine.ChangeState<FsmUpdateVersion>();
|
_machine.ChangeState<FsmUpdateVersion>();
|
||||||
}
|
}
|
||||||
|
@ -92,41 +92,29 @@ internal class FsmInitialize : IStateNode
|
||||||
{
|
{
|
||||||
//string hostServerIP = "http://10.0.2.2"; //安卓模拟器地址
|
//string hostServerIP = "http://10.0.2.2"; //安卓模拟器地址
|
||||||
string hostServerIP = "http://127.0.0.1";
|
string hostServerIP = "http://127.0.0.1";
|
||||||
string gameVersion = "v1.0";
|
string appVersion = "v1.0";
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.Android)
|
if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.Android)
|
||||||
return $"{hostServerIP}/CDN/Android/{gameVersion}";
|
return $"{hostServerIP}/CDN/Android/{appVersion}";
|
||||||
else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.iOS)
|
else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.iOS)
|
||||||
return $"{hostServerIP}/CDN/IPhone/{gameVersion}";
|
return $"{hostServerIP}/CDN/IPhone/{appVersion}";
|
||||||
else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.WebGL)
|
else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.WebGL)
|
||||||
return $"{hostServerIP}/CDN/WebGL/{gameVersion}";
|
return $"{hostServerIP}/CDN/WebGL/{appVersion}";
|
||||||
else
|
else
|
||||||
return $"{hostServerIP}/CDN/PC/{gameVersion}";
|
return $"{hostServerIP}/CDN/PC/{appVersion}";
|
||||||
#else
|
#else
|
||||||
if (Application.platform == RuntimePlatform.Android)
|
if (Application.platform == RuntimePlatform.Android)
|
||||||
return $"{hostServerIP}/CDN/Android/{gameVersion}";
|
return $"{hostServerIP}/CDN/Android/{appVersion}";
|
||||||
else if (Application.platform == RuntimePlatform.IPhonePlayer)
|
else if (Application.platform == RuntimePlatform.IPhonePlayer)
|
||||||
return $"{hostServerIP}/CDN/IPhone/{gameVersion}";
|
return $"{hostServerIP}/CDN/IPhone/{appVersion}";
|
||||||
else if (Application.platform == RuntimePlatform.WebGLPlayer)
|
else if (Application.platform == RuntimePlatform.WebGLPlayer)
|
||||||
return $"{hostServerIP}/CDN/WebGL/{gameVersion}";
|
return $"{hostServerIP}/CDN/WebGL/{appVersion}";
|
||||||
else
|
else
|
||||||
return $"{hostServerIP}/CDN/PC/{gameVersion}";
|
return $"{hostServerIP}/CDN/PC/{appVersion}";
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 内置文件查询服务类
|
|
||||||
/// </summary>
|
|
||||||
private class GameQueryServices : IQueryServices
|
|
||||||
{
|
|
||||||
public bool QueryStreamingAssets(string fileName)
|
|
||||||
{
|
|
||||||
string buildinFolderName = YooAssets.GetStreamingAssetBuildinFolderName();
|
|
||||||
return StreamingAssetsHelper.FileExists($"{buildinFolderName}/{fileName}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 资源文件解密服务类
|
/// 资源文件解密服务类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -144,7 +132,7 @@ internal class FsmInitialize : IStateNode
|
||||||
|
|
||||||
public Stream LoadFromStream(DecryptFileInfo fileInfo)
|
public Stream LoadFromStream(DecryptFileInfo fileInfo)
|
||||||
{
|
{
|
||||||
BundleStream bundleStream = new BundleStream(fileInfo.FilePath, FileMode.Open);
|
BundleStream bundleStream = new BundleStream(fileInfo.FilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
return bundleStream;
|
return bundleStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/StreamingAssetsHelper/BuildinFileManifest.cs
vendored
Normal file
10
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/StreamingAssetsHelper/BuildinFileManifest.cs
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 内置资源清单
|
||||||
|
/// </summary>
|
||||||
|
public class BuildinFileManifest : ScriptableObject
|
||||||
|
{
|
||||||
|
public List<string> BuildinFiles = new List<string>();
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 71b02dfa7aa9d4545b3417a18477fbee
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -1,57 +0,0 @@
|
||||||
//-------------------------------------
|
|
||||||
// 作者:Stark
|
|
||||||
//-------------------------------------
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
public sealed class StreamingAssetsHelper
|
|
||||||
{
|
|
||||||
private static readonly Dictionary<string, bool> _cacheData = new Dictionary<string, bool>(1000);
|
|
||||||
|
|
||||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
|
||||||
private static AndroidJavaClass _unityPlayerClass;
|
|
||||||
public static AndroidJavaClass UnityPlayerClass
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_unityPlayerClass == null)
|
|
||||||
_unityPlayerClass = new UnityEngine.AndroidJavaClass("com.unity3d.player.UnityPlayer");
|
|
||||||
return _unityPlayerClass;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AndroidJavaObject _currentActivity;
|
|
||||||
public static AndroidJavaObject CurrentActivity
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_currentActivity == null)
|
|
||||||
_currentActivity = UnityPlayerClass.GetStatic<AndroidJavaObject>("currentActivity");
|
|
||||||
return _currentActivity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 利用安卓原生接口查询内置文件是否存在
|
|
||||||
/// </summary>
|
|
||||||
public static bool FileExists(string filePath)
|
|
||||||
{
|
|
||||||
if (_cacheData.TryGetValue(filePath, out bool result) == false)
|
|
||||||
{
|
|
||||||
result = CurrentActivity.Call<bool>("CheckAssetExist", filePath);
|
|
||||||
_cacheData.Add(filePath, result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
public static bool FileExists(string filePath)
|
|
||||||
{
|
|
||||||
if (_cacheData.TryGetValue(filePath, out bool result) == false)
|
|
||||||
{
|
|
||||||
result = System.IO.File.Exists(System.IO.Path.Combine(Application.streamingAssetsPath, filePath));
|
|
||||||
_cacheData.Add(filePath, result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
97
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/StreamingAssetsHelper/StreamingAssetsHelper.cs
vendored
Normal file
97
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/StreamingAssetsHelper/StreamingAssetsHelper.cs
vendored
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using YooAsset;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 内置文件查询服务类
|
||||||
|
/// </summary>
|
||||||
|
public class GameQueryServices : IQueryServices
|
||||||
|
{
|
||||||
|
public bool QueryStreamingAssets(string fileName)
|
||||||
|
{
|
||||||
|
// 注意:fileName包含文件格式
|
||||||
|
return StreamingAssetsHelper.FileExists(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// StreamingAssets目录下资源查询帮助类
|
||||||
|
/// </summary>
|
||||||
|
public sealed class StreamingAssetsHelper
|
||||||
|
{
|
||||||
|
private static bool _isInit = false;
|
||||||
|
private static readonly HashSet<string> _cacheData = new HashSet<string>();
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
public static void Init() { _isInit = true; }
|
||||||
|
public static bool FileExists(string fileName)
|
||||||
|
{
|
||||||
|
return File.Exists(System.IO.Path.Combine(Application.streamingAssetsPath, "BuildinFiles", fileName));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化
|
||||||
|
/// </summary>
|
||||||
|
public static void Init()
|
||||||
|
{
|
||||||
|
if (_isInit == false)
|
||||||
|
{
|
||||||
|
_isInit = true;
|
||||||
|
var manifest = Resources.Load<BuildinFileManifest>("BuildinFileManifest");
|
||||||
|
foreach (string fileName in manifest.BuildinFiles)
|
||||||
|
{
|
||||||
|
_cacheData.Add(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 内置文件查询方法
|
||||||
|
/// </summary>
|
||||||
|
public static bool FileExists(string fileName)
|
||||||
|
{
|
||||||
|
if (_isInit == false)
|
||||||
|
Init();
|
||||||
|
|
||||||
|
return _cacheData.Contains(fileName);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
internal class PreprocessBuild : UnityEditor.Build.IPreprocessBuildWithReport
|
||||||
|
{
|
||||||
|
public int callbackOrder { get { return 0; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在构建应用程序前处理
|
||||||
|
/// </summary>
|
||||||
|
public void OnPreprocessBuild(UnityEditor.Build.Reporting.BuildReport report)
|
||||||
|
{
|
||||||
|
var manifest = ScriptableObject.CreateInstance<BuildinFileManifest>();
|
||||||
|
|
||||||
|
string folderPath = $"{Application.dataPath}/StreamingAssets/BuildinFiles";
|
||||||
|
DirectoryInfo root = new DirectoryInfo(folderPath);
|
||||||
|
FileInfo[] files = root.GetFiles();
|
||||||
|
foreach (var fileInfo in files)
|
||||||
|
{
|
||||||
|
if (fileInfo.Extension == ".meta")
|
||||||
|
continue;
|
||||||
|
if (fileInfo.Name.StartsWith("PackageManifest_"))
|
||||||
|
continue;
|
||||||
|
manifest.BuildinFiles.Add(fileInfo.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
string saveFilePath = "Assets/Resources/BuildinFileManifest.asset";
|
||||||
|
if (File.Exists(saveFilePath))
|
||||||
|
File.Delete(saveFilePath);
|
||||||
|
if (Directory.Exists("Assets/Resources") == false)
|
||||||
|
Directory.CreateDirectory("Assets/Resources");
|
||||||
|
UnityEditor.AssetDatabase.CreateAsset(manifest, saveFilePath);
|
||||||
|
UnityEditor.AssetDatabase.SaveAssets();
|
||||||
|
UnityEditor.AssetDatabase.Refresh();
|
||||||
|
Debug.Log($"内置资源清单保存成功 : {saveFilePath}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ca0617f5ec2b4504b923e3205dc77f54
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -1,6 +1,78 @@
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
// 作者:Stark
|
// 作者:Stark
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using YooAsset;
|
||||||
|
|
||||||
|
/*
|
||||||
|
/// <summary>
|
||||||
|
/// 内置文件查询服务类
|
||||||
|
/// </summary>
|
||||||
|
public class GameQueryServices : IQueryServices
|
||||||
|
{
|
||||||
|
public bool QueryStreamingAssets(string fileName)
|
||||||
|
{
|
||||||
|
string buildinFolderName = YooAssets.GetStreamingAssetBuildinFolderName();
|
||||||
|
return StreamingAssetsHelper.FileExists($"{buildinFolderName}/{fileName}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// StreamingAssets目录下资源查询帮助类
|
||||||
|
/// </summary>
|
||||||
|
public sealed class StreamingAssetsHelper
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<string, bool> _cacheData = new Dictionary<string, bool>(1000);
|
||||||
|
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
private static AndroidJavaClass _unityPlayerClass;
|
||||||
|
public static AndroidJavaClass UnityPlayerClass
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_unityPlayerClass == null)
|
||||||
|
_unityPlayerClass = new UnityEngine.AndroidJavaClass("com.unity3d.player.UnityPlayer");
|
||||||
|
return _unityPlayerClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AndroidJavaObject _currentActivity;
|
||||||
|
public static AndroidJavaObject CurrentActivity
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_currentActivity == null)
|
||||||
|
_currentActivity = UnityPlayerClass.GetStatic<AndroidJavaObject>("currentActivity");
|
||||||
|
return _currentActivity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 利用安卓原生接口查询内置文件是否存在
|
||||||
|
/// </summary>
|
||||||
|
public static bool FileExists(string filePath)
|
||||||
|
{
|
||||||
|
if (_cacheData.TryGetValue(filePath, out bool result) == false)
|
||||||
|
{
|
||||||
|
result = CurrentActivity.Call<bool>("CheckAssetExist", filePath);
|
||||||
|
_cacheData.Add(filePath, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
public static bool FileExists(string filePath)
|
||||||
|
{
|
||||||
|
if (_cacheData.TryGetValue(filePath, out bool result) == false)
|
||||||
|
{
|
||||||
|
result = System.IO.File.Exists(System.IO.Path.Combine(Application.streamingAssetsPath, filePath));
|
||||||
|
_cacheData.Add(filePath, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#if UNITY_ANDROID && UNITY_EDITOR
|
#if UNITY_ANDROID && UNITY_EDITOR
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -52,6 +124,8 @@ internal class AndroidPost : UnityEditor.Android.IPostGenerateGradleAndroidProje
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
//auto-gen-function
|
//auto-gen-function
|
||||||
|
@ -69,7 +143,7 @@ public boolean CheckAssetExist(String filePath)
|
||||||
}
|
}
|
||||||
catch(java.io.IOException e)
|
catch(java.io.IOException e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
//e.printStackTrace();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 4b1fa97e8bb8c5c46ad030b9554e1b5c
|
guid: 22c4a6746deb208479f4b7a040eed7f3
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
5
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniAnimation/README.md
vendored
Normal file
5
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniAnimation/README.md
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# UniFramework.Animation
|
||||||
|
|
||||||
|
一个轻量级的高效率的动画系统。
|
||||||
|
|
||||||
|
支持新的动画文件格式,不再依赖Animator文件来驱动动画,使用方式非常类似于老的Animation系统。
|
7
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniAnimation/README.md.meta
vendored
Normal file
7
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniAnimation/README.md.meta
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6a7da62cb3785ef45b2dda8fa0b3c8e5
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -1,5 +1,5 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: b19a7385dee2d0141901167dbfda9300
|
guid: 8664294e17a47c14c8d12545da2349a1
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
106
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniAnimation/Runtime/AnimClip.cs
vendored
Normal file
106
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniAnimation/Runtime/AnimClip.cs
vendored
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Playables;
|
||||||
|
using UnityEngine.Animations;
|
||||||
|
|
||||||
|
namespace UniFramework.Animation
|
||||||
|
{
|
||||||
|
internal sealed class AnimClip : AnimNode
|
||||||
|
{
|
||||||
|
public readonly string Name;
|
||||||
|
private readonly AnimationClip _clip;
|
||||||
|
public AnimationClipPlayable _clipPlayable;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动画层级
|
||||||
|
/// </summary>
|
||||||
|
public int Layer = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动画长度
|
||||||
|
/// </summary>
|
||||||
|
public float ClipLength
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_clip == null)
|
||||||
|
return 0f;
|
||||||
|
if (Speed == 0f)
|
||||||
|
return Mathf.Infinity;
|
||||||
|
return _clip.length / Speed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 归一化时间轴
|
||||||
|
/// </summary>
|
||||||
|
public float NormalizedTime
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_clip == null)
|
||||||
|
return;
|
||||||
|
Time = _clip.length * value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_clip == null)
|
||||||
|
return 1f;
|
||||||
|
return Time / _clip.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动画模式
|
||||||
|
/// </summary>
|
||||||
|
public WrapMode WrapMode
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_clip != null)
|
||||||
|
_clip.wrapMode = value;
|
||||||
|
}
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_clip == null)
|
||||||
|
return WrapMode.Default;
|
||||||
|
return _clip.wrapMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动画状态
|
||||||
|
/// </summary>
|
||||||
|
public AnimState State { private set; get; }
|
||||||
|
|
||||||
|
public AnimClip(PlayableGraph graph, AnimationClip clip, string name, int layer) : base(graph)
|
||||||
|
{
|
||||||
|
_clip = clip;
|
||||||
|
Name = name;
|
||||||
|
Layer = layer;
|
||||||
|
|
||||||
|
_clipPlayable = AnimationClipPlayable.Create(graph, clip);
|
||||||
|
_clipPlayable.SetApplyFootIK(false);
|
||||||
|
_clipPlayable.SetApplyPlayableIK(false);
|
||||||
|
SetSourcePlayable(_clipPlayable);
|
||||||
|
|
||||||
|
if (clip.wrapMode == WrapMode.Once)
|
||||||
|
{
|
||||||
|
_clipPlayable.SetDuration(clip.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
State = new AnimState(this);
|
||||||
|
}
|
||||||
|
public override void PlayNode()
|
||||||
|
{
|
||||||
|
if (_clip.wrapMode == WrapMode.Once || _clip.wrapMode == WrapMode.ClampForever)
|
||||||
|
{
|
||||||
|
Time = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.PlayNode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 606e5729d04cc8742a38f58e595c02f9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
196
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniAnimation/Runtime/AnimMixer.cs
vendored
Normal file
196
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniAnimation/Runtime/AnimMixer.cs
vendored
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Playables;
|
||||||
|
using UnityEngine.Animations;
|
||||||
|
|
||||||
|
namespace UniFramework.Animation
|
||||||
|
{
|
||||||
|
internal sealed class AnimMixer : AnimNode
|
||||||
|
{
|
||||||
|
private const float HIDE_DURATION = 0.25f;
|
||||||
|
private readonly List<AnimClip> _animClips = new List<AnimClip>(10);
|
||||||
|
private AnimationMixerPlayable _mixer;
|
||||||
|
private bool _isQuiting = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动画层级
|
||||||
|
/// </summary>
|
||||||
|
public int Layer { private set; get; }
|
||||||
|
|
||||||
|
|
||||||
|
public AnimMixer(PlayableGraph graph, int layer) : base(graph)
|
||||||
|
{
|
||||||
|
Layer = layer;
|
||||||
|
|
||||||
|
_mixer = AnimationMixerPlayable.Create(graph);
|
||||||
|
SetSourcePlayable(_mixer);
|
||||||
|
}
|
||||||
|
public override void Update(float deltaTime)
|
||||||
|
{
|
||||||
|
base.Update(deltaTime);
|
||||||
|
|
||||||
|
for (int i = 0; i < _animClips.Count; i++)
|
||||||
|
{
|
||||||
|
var animClip = _animClips[i];
|
||||||
|
if (animClip != null)
|
||||||
|
animClip.Update(deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isAllDone = true;
|
||||||
|
for (int i = 0; i < _animClips.Count; i++)
|
||||||
|
{
|
||||||
|
var animClip = _animClips[i];
|
||||||
|
if (animClip != null)
|
||||||
|
{
|
||||||
|
if (animClip.IsDone == false)
|
||||||
|
isAllDone = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当子节点都已经完成的时候断开连接
|
||||||
|
if (isAllDone && _isQuiting == false)
|
||||||
|
{
|
||||||
|
_isQuiting = true;
|
||||||
|
StartWeightFade(0, HIDE_DURATION);
|
||||||
|
}
|
||||||
|
if (_isQuiting)
|
||||||
|
{
|
||||||
|
if (Mathf.Approximately(Weight, 0f))
|
||||||
|
DisconnectMixer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 播放指定动画
|
||||||
|
/// </summary>
|
||||||
|
public void Play(AnimClip newAnimClip, float fadeDuration)
|
||||||
|
{
|
||||||
|
// 重新激活混合器
|
||||||
|
_isQuiting = false;
|
||||||
|
StartWeightFade(1f, 0);
|
||||||
|
|
||||||
|
if (IsContains(newAnimClip) == false)
|
||||||
|
{
|
||||||
|
// 优先插入到一个空位
|
||||||
|
int index = _animClips.FindIndex(s => s == null);
|
||||||
|
if (index == -1)
|
||||||
|
{
|
||||||
|
// Increase input count
|
||||||
|
int inputCount = _mixer.GetInputCount();
|
||||||
|
_mixer.SetInputCount(inputCount + 1);
|
||||||
|
|
||||||
|
newAnimClip.Connect(_mixer, inputCount);
|
||||||
|
_animClips.Add(newAnimClip);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newAnimClip.Connect(_mixer, index);
|
||||||
|
_animClips[index] = newAnimClip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _animClips.Count; i++)
|
||||||
|
{
|
||||||
|
var animClip = _animClips[i];
|
||||||
|
if (animClip == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (animClip == newAnimClip)
|
||||||
|
{
|
||||||
|
animClip.StartWeightFade(1f, fadeDuration);
|
||||||
|
animClip.PlayNode();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
animClip.StartWeightFade(0f, fadeDuration);
|
||||||
|
animClip.PauseNode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 停止指定动画,恢复为初始状态
|
||||||
|
/// </summary>
|
||||||
|
public void Stop(string name)
|
||||||
|
{
|
||||||
|
AnimClip animClip = FindClip(name);
|
||||||
|
if (animClip == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
animClip.PauseNode();
|
||||||
|
animClip.ResetNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 暂停所有动画
|
||||||
|
/// </summary>
|
||||||
|
public void PauseAll()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _animClips.Count; i++)
|
||||||
|
{
|
||||||
|
var animClip = _animClips[i];
|
||||||
|
if (animClip == null)
|
||||||
|
continue;
|
||||||
|
animClip.PauseNode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否包含该动画
|
||||||
|
/// </summary>
|
||||||
|
public bool IsContains(AnimClip clip)
|
||||||
|
{
|
||||||
|
foreach (var animClip in _animClips)
|
||||||
|
{
|
||||||
|
if (animClip == clip)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 移除一个动画
|
||||||
|
/// </summary>
|
||||||
|
public void RemoveClip(string name)
|
||||||
|
{
|
||||||
|
var animClip = FindClip(name);
|
||||||
|
if (animClip == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_animClips[animClip.InputPort] = null;
|
||||||
|
animClip.Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取指定的动画
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>如果没有返回NULL</returns>
|
||||||
|
private AnimClip FindClip(string name)
|
||||||
|
{
|
||||||
|
foreach (var animClip in _animClips)
|
||||||
|
{
|
||||||
|
if (animClip != null && animClip.Name == name)
|
||||||
|
return animClip;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniLogger.Warning($"${nameof(AnimClip)} doesn't exist : {name}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DisconnectMixer()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _animClips.Count; i++)
|
||||||
|
{
|
||||||
|
var animClip = _animClips[i];
|
||||||
|
if (animClip == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
animClip.Disconnect();
|
||||||
|
_animClips[i] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b33204afd07c00c4f8b425276778b8bf
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
221
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniAnimation/Runtime/AnimNode.cs
vendored
Normal file
221
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniAnimation/Runtime/AnimNode.cs
vendored
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Playables;
|
||||||
|
using UnityEngine.Animations;
|
||||||
|
|
||||||
|
namespace UniFramework.Animation
|
||||||
|
{
|
||||||
|
internal abstract class AnimNode
|
||||||
|
{
|
||||||
|
private readonly PlayableGraph _graph;
|
||||||
|
private Playable _source;
|
||||||
|
private Playable _parent;
|
||||||
|
|
||||||
|
private float _fadeSpeed = 0f;
|
||||||
|
private float _fadeWeight = 0f;
|
||||||
|
private bool _isFading = false;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否已经连接
|
||||||
|
/// </summary>
|
||||||
|
public bool IsConnect { get; private set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 输入端口
|
||||||
|
/// </summary>
|
||||||
|
public int InputPort { private set; get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否已经完成
|
||||||
|
/// If the duration of the playable is set, when the time of the playable reaches its duration during playback this flag will be set to true.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsDone
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _source.IsDone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否有效
|
||||||
|
/// if the Playable is properly constructed by the PlayableGraph and has not been destroyed, false otherwise.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsValid
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _source.IsValid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否正在播放中
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPlaying
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _source.GetPlayState() == PlayState.Playing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 时间轴
|
||||||
|
/// </summary>
|
||||||
|
public float Time
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_source.SetTime(value);
|
||||||
|
}
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (float)_source.GetTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 播放速度
|
||||||
|
/// </summary>
|
||||||
|
public float Speed
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_source.SetSpeed(value);
|
||||||
|
}
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (float)_source.GetSpeed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 权重值
|
||||||
|
/// </summary>
|
||||||
|
public float Weight
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_parent.SetInputWeight(InputPort, value);
|
||||||
|
}
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _parent.GetInputWeight(InputPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public AnimNode(PlayableGraph graph)
|
||||||
|
{
|
||||||
|
_graph = graph;
|
||||||
|
}
|
||||||
|
public virtual void Update(float deltaTime)
|
||||||
|
{
|
||||||
|
if (_isFading)
|
||||||
|
{
|
||||||
|
Weight = Mathf.MoveTowards(Weight, _fadeWeight, _fadeSpeed * deltaTime);
|
||||||
|
if (Mathf.Approximately(Weight, _fadeWeight))
|
||||||
|
{
|
||||||
|
_isFading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public virtual void Destroy()
|
||||||
|
{
|
||||||
|
if (IsValid)
|
||||||
|
{
|
||||||
|
_graph.DestroySubgraph(_source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public virtual void PlayNode()
|
||||||
|
{
|
||||||
|
// NOTE : When playing, the local time of this Playable will be updated during the evaluation of the PlayableGraph.
|
||||||
|
_source.Play();
|
||||||
|
|
||||||
|
// NOTE : Changes a flag indicating that a playable has completed its operation.
|
||||||
|
// Playable that reach the end of their duration are automatically marked as done.
|
||||||
|
_source.SetDone(false);
|
||||||
|
}
|
||||||
|
public virtual void PauseNode()
|
||||||
|
{
|
||||||
|
// NOTE : When paused, the local time of this Playable will not be updated during the evaluation of the PlayableGraph.
|
||||||
|
_source.Pause();
|
||||||
|
|
||||||
|
// NOTE : Changes a flag indicating that a playable has completed its operation.
|
||||||
|
// Playable that reach the end of their duration are automatically marked as done.
|
||||||
|
_source.SetDone(true);
|
||||||
|
}
|
||||||
|
public virtual void ResetNode()
|
||||||
|
{
|
||||||
|
_fadeSpeed = 0;
|
||||||
|
_fadeWeight = 0;
|
||||||
|
_isFading = false;
|
||||||
|
|
||||||
|
Time = 0;
|
||||||
|
Speed = 1;
|
||||||
|
Weight = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连接到父节点
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent">父节点对象</param>
|
||||||
|
/// <param name="inputPort">父节点上的输入端口</param>
|
||||||
|
public void Connect(Playable parent, int parentInputPort)
|
||||||
|
{
|
||||||
|
if (IsConnect)
|
||||||
|
throw new System.Exception("AnimNode is connected.");
|
||||||
|
|
||||||
|
_parent = parent;
|
||||||
|
InputPort = parentInputPort;
|
||||||
|
|
||||||
|
// 重置节点
|
||||||
|
ResetNode();
|
||||||
|
|
||||||
|
// 连接
|
||||||
|
_graph.Connect(_source, 0, parent, parentInputPort);
|
||||||
|
IsConnect = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 同父节点断开连接
|
||||||
|
/// </summary>
|
||||||
|
public void Disconnect()
|
||||||
|
{
|
||||||
|
if (IsConnect == false)
|
||||||
|
throw new System.Exception("AnimNode is disconnected.");
|
||||||
|
|
||||||
|
// 断开
|
||||||
|
_graph.Disconnect(_parent, InputPort);
|
||||||
|
IsConnect = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 开始权重值过渡
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="destWeight">目标权重值</param>
|
||||||
|
/// <param name="fadeDuration">过渡时间</param>
|
||||||
|
public void StartWeightFade(float destWeight, float fadeDuration)
|
||||||
|
{
|
||||||
|
if (fadeDuration <= 0)
|
||||||
|
{
|
||||||
|
Weight = destWeight;
|
||||||
|
_isFading = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//注意:保持统一的渐变速度
|
||||||
|
_fadeSpeed = 1f / fadeDuration;
|
||||||
|
_fadeWeight = destWeight;
|
||||||
|
_isFading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void SetSourcePlayable(Playable playable)
|
||||||
|
{
|
||||||
|
_source = playable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b6928afd60ab2ed489e492e00d9ddaba
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
238
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniAnimation/Runtime/AnimPlayable.cs
vendored
Normal file
238
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniAnimation/Runtime/AnimPlayable.cs
vendored
Normal file
|
@ -0,0 +1,238 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Playables;
|
||||||
|
using UnityEngine.Animations;
|
||||||
|
|
||||||
|
namespace UniFramework.Animation
|
||||||
|
{
|
||||||
|
internal class AnimPlayable
|
||||||
|
{
|
||||||
|
private readonly List<AnimClip> _animClips = new List<AnimClip>(10);
|
||||||
|
private readonly List<AnimMixer> _animMixers = new List<AnimMixer>(10);
|
||||||
|
|
||||||
|
private PlayableGraph _graph;
|
||||||
|
private AnimationPlayableOutput _output;
|
||||||
|
private AnimationLayerMixerPlayable _mixerRoot;
|
||||||
|
|
||||||
|
public void Create(Animator animator)
|
||||||
|
{
|
||||||
|
string name = animator.gameObject.name;
|
||||||
|
_graph = PlayableGraph.Create(name);
|
||||||
|
_graph.SetTimeUpdateMode(DirectorUpdateMode.Manual);
|
||||||
|
|
||||||
|
_mixerRoot = AnimationLayerMixerPlayable.Create(_graph);
|
||||||
|
_output = AnimationPlayableOutput.Create(_graph, name, animator);
|
||||||
|
_output.SetSourcePlayable(_mixerRoot);
|
||||||
|
}
|
||||||
|
public void Update(float deltaTime)
|
||||||
|
{
|
||||||
|
_graph.Evaluate(deltaTime);
|
||||||
|
|
||||||
|
// 更新所有层级
|
||||||
|
for (int i = 0; i < _animMixers.Count; i++)
|
||||||
|
{
|
||||||
|
var mixer = _animMixers[i];
|
||||||
|
if(mixer.IsConnect)
|
||||||
|
mixer.Update(deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void Destroy()
|
||||||
|
{
|
||||||
|
_graph.Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Play the graph
|
||||||
|
/// </summary>
|
||||||
|
public void PlayGraph()
|
||||||
|
{
|
||||||
|
_graph.Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stop the graph
|
||||||
|
/// </summary>
|
||||||
|
public void StopGraph()
|
||||||
|
{
|
||||||
|
_graph.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取动画的状态
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">动画名称</param>
|
||||||
|
/// <returns>如果动画不存在返回空</returns>
|
||||||
|
public AnimState GetAnimState(string name)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _animClips.Count; i++)
|
||||||
|
{
|
||||||
|
if (_animClips[i].Name == name)
|
||||||
|
return _animClips[i].State;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检测动画是否正在播放
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">动画名称</param>
|
||||||
|
public bool IsPlaying(string name)
|
||||||
|
{
|
||||||
|
AnimClip animClip = GetAnimClip(name);
|
||||||
|
if (animClip == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return animClip.IsConnect && animClip.IsPlaying;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 播放一个动画
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">动画名称</param>
|
||||||
|
/// <param name="fadeLength">融合时间</param>
|
||||||
|
public void Play(string name, float fadeLength)
|
||||||
|
{
|
||||||
|
var animClip = GetAnimClip(name);
|
||||||
|
if (animClip == null)
|
||||||
|
{
|
||||||
|
UniLogger.Warning($"Not found animation {name}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int layer = animClip.Layer;
|
||||||
|
var animMixer = GetAnimMixer(layer);
|
||||||
|
if (animMixer == null)
|
||||||
|
animMixer = CreateAnimMixer(layer);
|
||||||
|
|
||||||
|
if(animMixer.IsConnect == false)
|
||||||
|
animMixer.Connect(_mixerRoot, animMixer.Layer);
|
||||||
|
|
||||||
|
animMixer.Play(animClip, fadeLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 停止一个动画
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">动画名称</param>
|
||||||
|
public void Stop(string name)
|
||||||
|
{
|
||||||
|
var animClip = GetAnimClip(name);
|
||||||
|
if (animClip == null)
|
||||||
|
{
|
||||||
|
UniLogger.Warning($"Not found animation {name}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (animClip.IsConnect == false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var animMixer = GetAnimMixer(animClip.Layer);
|
||||||
|
if (animMixer == null)
|
||||||
|
throw new System.Exception("Should never get here.");
|
||||||
|
|
||||||
|
animMixer.Stop(animClip.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加一个动画片段
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">动画名称</param>
|
||||||
|
/// <param name="clip">动画片段</param>
|
||||||
|
/// <param name="layer">动画层级</param>
|
||||||
|
public bool AddAnimation(string name, AnimationClip clip, int layer = 0)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(name))
|
||||||
|
throw new System.ArgumentException("Name is null or empty.");
|
||||||
|
if (clip == null)
|
||||||
|
throw new System.ArgumentNullException();
|
||||||
|
if (layer < 0)
|
||||||
|
throw new System.Exception("Layer must be greater than zero.");
|
||||||
|
|
||||||
|
if (IsContains(name))
|
||||||
|
{
|
||||||
|
UniLogger.Warning($"Animation already exists : {name}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimClip animClip = new AnimClip(_graph, clip, name, layer);
|
||||||
|
_animClips.Add(animClip);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 移除一个动画片段
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">动画名称</param>
|
||||||
|
public bool RemoveAnimation(string name)
|
||||||
|
{
|
||||||
|
if (IsContains(name) == false)
|
||||||
|
{
|
||||||
|
UniLogger.Warning($"Not found Animation : {name}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimClip animClip = GetAnimClip(name);
|
||||||
|
AnimMixer animMixer = GetAnimMixer(animClip.Layer);
|
||||||
|
if(animMixer != null)
|
||||||
|
animMixer.RemoveClip(animClip.Name);
|
||||||
|
|
||||||
|
animClip.Destroy();
|
||||||
|
_animClips.Remove(animClip);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否包含一个动画状态
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">动画名称</param>
|
||||||
|
public bool IsContains(string name)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _animClips.Count; i++)
|
||||||
|
{
|
||||||
|
if (_animClips[i].Name == name)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnimClip GetAnimClip(string name)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _animClips.Count; i++)
|
||||||
|
{
|
||||||
|
if (_animClips[i].Name == name)
|
||||||
|
return _animClips[i];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
private AnimMixer GetAnimMixer(int layer)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _animMixers.Count; i++)
|
||||||
|
{
|
||||||
|
if (_animMixers[i].Layer == layer)
|
||||||
|
return _animMixers[i];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
private AnimMixer CreateAnimMixer(int layer)
|
||||||
|
{
|
||||||
|
// Increase input count
|
||||||
|
int inputCount = _mixerRoot.GetInputCount();
|
||||||
|
if(layer == 0 && inputCount == 0)
|
||||||
|
{
|
||||||
|
_mixerRoot.SetInputCount(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (layer > inputCount - 1)
|
||||||
|
{
|
||||||
|
_mixerRoot.SetInputCount(layer + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var animMixer = new AnimMixer(_graph, layer);
|
||||||
|
_animMixers.Add(animMixer);
|
||||||
|
return animMixer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 772b8a9edd8466646bab12d6f5ba1084
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
126
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniAnimation/Runtime/AnimState.cs
vendored
Normal file
126
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniAnimation/Runtime/AnimState.cs
vendored
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
using System.Collections;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Playables;
|
||||||
|
using UnityEngine.Animations;
|
||||||
|
|
||||||
|
namespace UniFramework.Animation
|
||||||
|
{
|
||||||
|
public class AnimState
|
||||||
|
{
|
||||||
|
private readonly AnimClip _animClip;
|
||||||
|
|
||||||
|
private AnimState()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
internal AnimState(AnimClip animClip)
|
||||||
|
{
|
||||||
|
_animClip = animClip;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of animation.
|
||||||
|
/// </summary>
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _animClip.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The length of the animation clip in seconds.
|
||||||
|
/// </summary>
|
||||||
|
public float Length
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _animClip.ClipLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The layer of animation.
|
||||||
|
/// </summary>
|
||||||
|
public int Layer
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _animClip.Layer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wrapping mode of the animation.
|
||||||
|
/// </summary>
|
||||||
|
public WrapMode WrapMode
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _animClip.WrapMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The weight of animation.
|
||||||
|
/// </summary>
|
||||||
|
public float Weight
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _animClip.Weight;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_animClip.Weight = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current time of the animation.
|
||||||
|
/// </summary>
|
||||||
|
public float Time
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _animClip.Time;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_animClip.Time = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The normalized time of the animation.
|
||||||
|
/// </summary>
|
||||||
|
public float NormalizedTime
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _animClip.NormalizedTime;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_animClip.NormalizedTime = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The playback speed of the animation. 1 is normal playback speed.
|
||||||
|
/// </summary>
|
||||||
|
public float Speed
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _animClip.Speed;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_animClip.Speed = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5660d9e37e5c2a44f818506c9fe76624
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
182
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniAnimation/Runtime/UniAnimation.cs
vendored
Normal file
182
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniAnimation/Runtime/UniAnimation.cs
vendored
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UniFramework.Animation
|
||||||
|
{
|
||||||
|
[RequireComponent(typeof(Animator))]
|
||||||
|
public class UniAnimation : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public class AnimationWrapper
|
||||||
|
{
|
||||||
|
public int Layer;
|
||||||
|
public WrapMode Mode;
|
||||||
|
public AnimationClip Clip;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnimPlayable _animPlayable;
|
||||||
|
private Animator _animator;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
private AnimationWrapper[] _animations;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
private bool _playAutomatically = true;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
private bool _animatePhysics = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自动播放动画
|
||||||
|
/// </summary>
|
||||||
|
public bool PlayAutomatically
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _playAutomatically;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_playAutomatically = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 物理更新模式
|
||||||
|
/// </summary>
|
||||||
|
public bool AnimatePhysics
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _animatePhysics;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_animatePhysics = value;
|
||||||
|
_animator.updateMode = _animatePhysics ? AnimatorUpdateMode.AnimatePhysics : AnimatorUpdateMode.Normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Awake()
|
||||||
|
{
|
||||||
|
_animator = GetComponent<Animator>();
|
||||||
|
_animator.updateMode = _animatePhysics ? AnimatorUpdateMode.AnimatePhysics : AnimatorUpdateMode.Normal;
|
||||||
|
|
||||||
|
_animPlayable = new AnimPlayable();
|
||||||
|
_animPlayable.Create(_animator);
|
||||||
|
|
||||||
|
// 添加列表动作
|
||||||
|
for (int i = 0; i < _animations.Length; i++)
|
||||||
|
{
|
||||||
|
var wrapper = _animations[i];
|
||||||
|
if (wrapper == null || wrapper.Clip == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
wrapper.Clip.wrapMode = wrapper.Mode;
|
||||||
|
_animPlayable.AddAnimation(wrapper.Clip.name, wrapper.Clip, wrapper.Layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void OnEnable()
|
||||||
|
{
|
||||||
|
_animPlayable.PlayGraph();
|
||||||
|
|
||||||
|
if (PlayAutomatically)
|
||||||
|
{
|
||||||
|
var wrapper = GetDefaultWrapper();
|
||||||
|
if (wrapper != null)
|
||||||
|
{
|
||||||
|
Play(wrapper.Clip.name, 0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_animPlayable.Update(float.MaxValue);
|
||||||
|
}
|
||||||
|
public void OnDisable()
|
||||||
|
{
|
||||||
|
_animPlayable.StopGraph();
|
||||||
|
}
|
||||||
|
public void OnDestroy()
|
||||||
|
{
|
||||||
|
_animPlayable.Destroy();
|
||||||
|
}
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
_animPlayable.Update(Time.deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加一个动画
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clip">动画片段</param>
|
||||||
|
/// <param name="layer">动画层级</param>
|
||||||
|
public bool AddAnimation(AnimationClip clip, int layer = 0)
|
||||||
|
{
|
||||||
|
return _animPlayable.AddAnimation(clip.name, clip, layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 移除动画
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">动画名称</param>
|
||||||
|
public bool RemoveAnimation(string name)
|
||||||
|
{
|
||||||
|
return _animPlayable.RemoveAnimation(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取动画状态
|
||||||
|
/// </summary>
|
||||||
|
public AnimState GetState(string name)
|
||||||
|
{
|
||||||
|
return _animPlayable.GetAnimState(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动画是否在播放中
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPlaying(string name)
|
||||||
|
{
|
||||||
|
return _animPlayable.IsPlaying(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否包含动画片段
|
||||||
|
/// </summary>
|
||||||
|
public bool IsContains(string name)
|
||||||
|
{
|
||||||
|
return _animPlayable.IsContains(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 播放动画
|
||||||
|
/// </summary>
|
||||||
|
public void Play(string name, float fadeLength = 0.25f)
|
||||||
|
{
|
||||||
|
_animPlayable.Play(name, fadeLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 停止动画
|
||||||
|
/// </summary>
|
||||||
|
public void Stop(string name)
|
||||||
|
{
|
||||||
|
_animPlayable.Stop(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnimationWrapper GetDefaultWrapper()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _animations.Length; i++)
|
||||||
|
{
|
||||||
|
var wrapper = _animations[i];
|
||||||
|
if (wrapper == null || wrapper.Clip == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ef52174816df6104587b15c5b250201a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -1,12 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "UniWindow",
|
"name": "UniFramework.Animation",
|
||||||
"rootNamespace": "",
|
"rootNamespace": "",
|
||||||
"references": [
|
"references": [],
|
||||||
"GUID:e34a5702dd353724aa315fb8011f08c3"
|
|
||||||
],
|
|
||||||
"includePlatforms": [],
|
"includePlatforms": [],
|
||||||
"excludePlatforms": [],
|
"excludePlatforms": [],
|
||||||
"allowUnsafeCode": true,
|
"allowUnsafeCode": false,
|
||||||
"overrideReferences": false,
|
"overrideReferences": false,
|
||||||
"precompiledReferences": [],
|
"precompiledReferences": [],
|
||||||
"autoReferenced": true,
|
"autoReferenced": true,
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ccbd3ee89c7c8614bb89388a769680c1
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
21
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniAnimation/Runtime/UniLogger.cs
vendored
Normal file
21
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniAnimation/Runtime/UniLogger.cs
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace UniFramework.Animation
|
||||||
|
{
|
||||||
|
internal static class UniLogger
|
||||||
|
{
|
||||||
|
[Conditional("DEBUG")]
|
||||||
|
public static void Log(string info)
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.Log(info);
|
||||||
|
}
|
||||||
|
public static void Warning(string info)
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.LogWarning(info);
|
||||||
|
}
|
||||||
|
public static void Error(string info)
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.LogError(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5c11371eb114ef24daad9fc81ac6e0cd
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -1,5 +1,5 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: f2801816f5bf9e74eb7f2e2b340ad4cc
|
guid: c1cc49e5bd383f040b0f9d481de62f04
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "UniEvent",
|
"name": "UniFramework.Event",
|
||||||
"rootNamespace": "",
|
"rootNamespace": "",
|
||||||
"references": [
|
"references": [
|
||||||
"GUID:e34a5702dd353724aa315fb8011f08c3"
|
"GUID:e34a5702dd353724aa315fb8011f08c3"
|
|
@ -1,5 +1,5 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 16d76e3e6337b0c4cae28696d4be4c7c
|
guid: 72dcc846338391f44a4134c21f94723a
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "UniPooling",
|
"name": "UniFramework.Machine",
|
||||||
"rootNamespace": "",
|
"rootNamespace": "",
|
||||||
"references": [
|
"references": [
|
||||||
"GUID:e34a5702dd353724aa315fb8011f08c3"
|
"GUID:e34a5702dd353724aa315fb8011f08c3"
|
8
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniNetwork.meta
vendored
Normal file
8
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniNetwork.meta
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 99ad37bbd721c834d8157bb257d4b8bb
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
89
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniNetwork/README.md
vendored
Normal file
89
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniNetwork/README.md
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
# UniFramework.Network
|
||||||
|
|
||||||
|
一个高效的基于IOCP模型的网络系统。
|
||||||
|
|
||||||
|
```c#
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using UniFramework.Network;
|
||||||
|
|
||||||
|
// 登录请求消息
|
||||||
|
class LoginRequestMessage
|
||||||
|
{
|
||||||
|
public string Name;
|
||||||
|
public string Password;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录反馈消息
|
||||||
|
class LoginResponseMessage
|
||||||
|
{
|
||||||
|
public string Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCP客户端
|
||||||
|
UniFramework.Network.TcpClient _client = null;
|
||||||
|
|
||||||
|
// 创建TCP客户端
|
||||||
|
void CreateClient()
|
||||||
|
{
|
||||||
|
// 初始化网络系统
|
||||||
|
UniNetwork.Initalize();
|
||||||
|
|
||||||
|
// 创建TCP客户端
|
||||||
|
int packageMaxSize = short.MaxValue;
|
||||||
|
var encoder = new DefaultNetPackageEncoder();
|
||||||
|
var decoder = new DefaultNetPackageDecoder();
|
||||||
|
_client = UniNetwork.CreateTcpClient(packageMaxSize, encoder, decoder);
|
||||||
|
|
||||||
|
// 连接服务器
|
||||||
|
var remote = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8000);
|
||||||
|
_client.ConnectAsync(remote, OnConnectServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭TCP客户端
|
||||||
|
void CloseClient()
|
||||||
|
{
|
||||||
|
if(_client != null)
|
||||||
|
{
|
||||||
|
_client.Dispose();
|
||||||
|
_client = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnConnectServer(SocketError error)
|
||||||
|
{
|
||||||
|
Debug.Log($"Server connect result : {error}");
|
||||||
|
if (error == SocketError.Success)
|
||||||
|
Debug.Log("服务器连接成功!");
|
||||||
|
else
|
||||||
|
Debug.Log("服务器连接失败!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
// 每帧去获取解析的网络包
|
||||||
|
DefaultNetPackage networkPackage = client.PickPackage() as DefaultNetPackage;
|
||||||
|
if(networkPackage != null)
|
||||||
|
{
|
||||||
|
string json = Encoding.UTF8.GetString(networkPackage.BodyBytes);
|
||||||
|
LoginResponseMessage message = JsonUtility.FromJson<LoginResponseMessage>(json);
|
||||||
|
Debug.Log(message.Result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送登录请求消息
|
||||||
|
void SendLoginMessage()
|
||||||
|
{
|
||||||
|
LoginRequestMessage message = new LoginRequestMessage();
|
||||||
|
message.Name = "hevinci";
|
||||||
|
message.Password = "1234567";
|
||||||
|
|
||||||
|
DefaultNetPackage networkPackage = new DefaultNetPackage();
|
||||||
|
networkPackage.MsgID = 10001;
|
||||||
|
networkPackage.BodyBytes = Encoding.UTF8.GetBytes(JsonUtility.ToJson(message));
|
||||||
|
_client.SendPackage(networkPackage);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
7
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniNetwork/README.md.meta
vendored
Normal file
7
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniNetwork/README.md.meta
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9325e3d425f106a41977ea6e672fecdb
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniNetwork/Runtime.meta
vendored
Normal file
8
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniNetwork/Runtime.meta
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: efa01143ee531b6478df4aca44367f8a
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3cb02cc27ea1caf479c14cebf66a5dab
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,16 @@
|
||||||
|
|
||||||
|
namespace UniFramework.Network
|
||||||
|
{
|
||||||
|
public class DefaultNetPackage : INetPackage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 消息ID
|
||||||
|
/// </summary>
|
||||||
|
public int MsgID { set; get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 包体数据
|
||||||
|
/// </summary>
|
||||||
|
public byte[] BodyBytes { set; get; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 77bfd5d9f7d9c79478b19cf99a103c47
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,86 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace UniFramework.Network
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 网络包解码器
|
||||||
|
/// </summary>
|
||||||
|
public class DefaultNetPackageDecoder : INetPackageDecoder
|
||||||
|
{
|
||||||
|
private HandleErrorDelegate _handleErrorCallback;
|
||||||
|
private const int PackageHeaderLengthFiledSize = 4; //int类型
|
||||||
|
private const int PackageHeaderIDFiledSize = 4; //int类型
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取包头的尺寸
|
||||||
|
/// </summary>
|
||||||
|
public int GetPackageHeaderSize()
|
||||||
|
{
|
||||||
|
return PackageHeaderLengthFiledSize + PackageHeaderIDFiledSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册异常错误回调方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="callback"></param>
|
||||||
|
public void RigistHandleErrorCallback(HandleErrorDelegate callback)
|
||||||
|
{
|
||||||
|
_handleErrorCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 网络消息解码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="packageBodyMaxSize">包体的最大尺寸</param>
|
||||||
|
/// <param name="ringBuffer">解码需要的字节缓冲区</param>
|
||||||
|
/// <param name="outputPackages">接收的包裹列表</param>
|
||||||
|
public void Decode(int packageBodyMaxSize, RingBuffer ringBuffer, List<INetPackage> outputPackages)
|
||||||
|
{
|
||||||
|
// 循环解包
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// 如果数据不够判断消息长度
|
||||||
|
if (ringBuffer.ReadableBytes < PackageHeaderLengthFiledSize)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ringBuffer.MarkReaderIndex();
|
||||||
|
|
||||||
|
// 读取Package长度
|
||||||
|
int packageSize = ringBuffer.ReadInt();
|
||||||
|
|
||||||
|
// 如果剩余可读数据小于Package长度
|
||||||
|
if (ringBuffer.ReadableBytes < packageSize)
|
||||||
|
{
|
||||||
|
ringBuffer.ResetReaderIndex();
|
||||||
|
break; //需要退出读够数据再解包
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultNetPackage package = new DefaultNetPackage();
|
||||||
|
|
||||||
|
// 读取包头
|
||||||
|
{
|
||||||
|
// 读取消息ID
|
||||||
|
package.MsgID = ringBuffer.ReadInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测包体长度
|
||||||
|
int bodySize = packageSize - PackageHeaderIDFiledSize;
|
||||||
|
if (bodySize > packageBodyMaxSize)
|
||||||
|
{
|
||||||
|
_handleErrorCallback(true, $"The decode package {package.MsgID} body size is larger than {packageBodyMaxSize} !");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取包体
|
||||||
|
{
|
||||||
|
package.BodyBytes = ringBuffer.ReadBytes(bodySize);
|
||||||
|
outputPackages.Add(package);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注意:将剩余数据移至起始
|
||||||
|
ringBuffer.DiscardReadBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6b9693105d824c8409392d251d920346
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,84 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace UniFramework.Network
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 网络包编码器
|
||||||
|
/// </summary>
|
||||||
|
public class DefaultNetPackageEncoder : INetPackageEncoder
|
||||||
|
{
|
||||||
|
private HandleErrorDelegate _handleErrorCallback;
|
||||||
|
private const int PackageHeaderLengthFiledSize = 4; //int类型
|
||||||
|
private const int PackageHeaderIDFiledSize = 4; //int类型
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取包头的尺寸
|
||||||
|
/// </summary>
|
||||||
|
public int GetPackageHeaderSize()
|
||||||
|
{
|
||||||
|
return PackageHeaderLengthFiledSize + PackageHeaderIDFiledSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册异常错误回调方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="callback"></param>
|
||||||
|
public void RigistHandleErrorCallback(HandleErrorDelegate callback)
|
||||||
|
{
|
||||||
|
_handleErrorCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 编码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="packageBodyMaxSize">包体的最大尺寸</param>
|
||||||
|
/// <param name="ringBuffer">编码填充的字节缓冲区</param>
|
||||||
|
/// <param name="encodePackage">发送的包裹</param>
|
||||||
|
public void Encode(int packageBodyMaxSize, RingBuffer ringBuffer, INetPackage encodePackage)
|
||||||
|
{
|
||||||
|
if (encodePackage == null)
|
||||||
|
{
|
||||||
|
_handleErrorCallback(false, "The encode package object is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultNetPackage package = (DefaultNetPackage)encodePackage;
|
||||||
|
if (package == null)
|
||||||
|
{
|
||||||
|
_handleErrorCallback(false, $"The encode package object is invalid : {encodePackage.GetType()}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测逻辑是否合法
|
||||||
|
if (package.BodyBytes == null)
|
||||||
|
{
|
||||||
|
_handleErrorCallback(false, $"The encode package BodyBytes field is null : {encodePackage.GetType()}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取包体数据
|
||||||
|
byte[] bodyData = package.BodyBytes;
|
||||||
|
|
||||||
|
// 检测包体长度
|
||||||
|
if (bodyData.Length > packageBodyMaxSize)
|
||||||
|
{
|
||||||
|
_handleErrorCallback(false, $"The encode package {package.MsgID} body size is larger than {packageBodyMaxSize}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入长度
|
||||||
|
int packetLength = PackageHeaderIDFiledSize + bodyData.Length;
|
||||||
|
ringBuffer.WriteInt(packetLength);
|
||||||
|
|
||||||
|
// 写入包头
|
||||||
|
{
|
||||||
|
// 写入消息ID
|
||||||
|
ringBuffer.WriteInt(package.MsgID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入包体
|
||||||
|
ringBuffer.WriteBytes(bodyData, 0, bodyData.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a91c21858dd0d2645b67361f76d9e747
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
namespace UniFramework.Network
|
||||||
|
{
|
||||||
|
public delegate void HandleErrorDelegate(bool isDispose, string error);
|
||||||
|
|
||||||
|
public interface INetPackage
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 00149ae4ecfb570418760bc2d359e7ca
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace UniFramework.Network
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 网络包解码器
|
||||||
|
/// </summary>
|
||||||
|
public interface INetPackageDecoder
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取包头的尺寸
|
||||||
|
/// </summary>
|
||||||
|
int GetPackageHeaderSize();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册异常错误回调方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="callback"></param>
|
||||||
|
void RigistHandleErrorCallback(HandleErrorDelegate callback);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 网络消息解码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="packageBodyMaxSize">包体的最大尺寸</param>
|
||||||
|
/// <param name="ringBuffer">解码需要的字节缓冲区</param>
|
||||||
|
/// <param name="outputPackages">接收的包裹列表</param>
|
||||||
|
void Decode(int packageBodyMaxSize, RingBuffer ringBuffer, List<INetPackage> outputPackages);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8c549df6ac813a74b9fd787e3991a254
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace UniFramework.Network
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 网络包编码器
|
||||||
|
/// </summary>
|
||||||
|
public interface INetPackageEncoder
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取包头的尺寸
|
||||||
|
/// </summary>
|
||||||
|
int GetPackageHeaderSize();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册异常错误回调方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="callback"></param>
|
||||||
|
void RigistHandleErrorCallback(HandleErrorDelegate callback);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 编码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="packageBodyMaxSize">包体的最大尺寸</param>
|
||||||
|
/// <param name="ringBuffer">编码填充的字节缓冲区</param>
|
||||||
|
/// <param name="encodePackage">发送的包裹</param>
|
||||||
|
void Encode(int packageBodyMaxSize, RingBuffer ringBuffer, INetPackage encodePackage);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0c4bce3e31495bc4483ab697d895fd62
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
537
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniNetwork/Runtime/RingBuffer.cs
vendored
Normal file
537
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniNetwork/Runtime/RingBuffer.cs
vendored
Normal file
|
@ -0,0 +1,537 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UniFramework.Network
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 环形缓冲区
|
||||||
|
/// </summary>
|
||||||
|
public class RingBuffer
|
||||||
|
{
|
||||||
|
private readonly byte[] _buffer;
|
||||||
|
private int _readerIndex = 0;
|
||||||
|
private int _writerIndex = 0;
|
||||||
|
private int _markedReaderIndex = 0;
|
||||||
|
private int _markedWriterIndex = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 字节缓冲区
|
||||||
|
/// </summary>
|
||||||
|
public RingBuffer(int capacity)
|
||||||
|
{
|
||||||
|
_buffer = new byte[capacity];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 字节缓冲区
|
||||||
|
/// </summary>
|
||||||
|
public RingBuffer(byte[] data)
|
||||||
|
{
|
||||||
|
_buffer = data;
|
||||||
|
_writerIndex = data.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取数据
|
||||||
|
/// </summary>
|
||||||
|
public byte[] GetBuffer()
|
||||||
|
{
|
||||||
|
return _buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 缓冲区容量
|
||||||
|
/// </summary>
|
||||||
|
public int Capacity
|
||||||
|
{
|
||||||
|
get { return _buffer.Length; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清空缓冲区
|
||||||
|
/// </summary>
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_readerIndex = 0;
|
||||||
|
_writerIndex = 0;
|
||||||
|
_markedReaderIndex = 0;
|
||||||
|
_markedWriterIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除已读部分,重新初始化数组
|
||||||
|
/// </summary>
|
||||||
|
public void DiscardReadBytes()
|
||||||
|
{
|
||||||
|
if (_readerIndex == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_readerIndex == _writerIndex)
|
||||||
|
{
|
||||||
|
_readerIndex = 0;
|
||||||
|
_writerIndex = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0, j = _readerIndex, length = _writerIndex - _readerIndex; i < length; i++, j++)
|
||||||
|
{
|
||||||
|
_buffer[i] = _buffer[j];
|
||||||
|
}
|
||||||
|
_writerIndex -= _readerIndex;
|
||||||
|
_readerIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region 读取相关
|
||||||
|
/// <summary>
|
||||||
|
/// 读取的下标位置
|
||||||
|
/// </summary>
|
||||||
|
public int ReaderIndex
|
||||||
|
{
|
||||||
|
get { return _readerIndex; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前可读数据量
|
||||||
|
/// </summary>
|
||||||
|
public int ReadableBytes
|
||||||
|
{
|
||||||
|
get { return _writerIndex - _readerIndex; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询是否可以读取
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="size">读取数据量</param>
|
||||||
|
public bool IsReadable(int size = 1)
|
||||||
|
{
|
||||||
|
return _writerIndex - _readerIndex >= size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 标记读取的下标位置,便于某些时候回退到该位置
|
||||||
|
/// </summary>
|
||||||
|
public void MarkReaderIndex()
|
||||||
|
{
|
||||||
|
_markedReaderIndex = _readerIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 回退到标记的读取下标位置
|
||||||
|
/// </summary>
|
||||||
|
public void ResetReaderIndex()
|
||||||
|
{
|
||||||
|
_readerIndex = _markedReaderIndex;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 写入相关
|
||||||
|
/// <summary>
|
||||||
|
/// 写入的下标位置
|
||||||
|
/// </summary>
|
||||||
|
public int WriterIndex
|
||||||
|
{
|
||||||
|
get { return _writerIndex; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前可写入数据量
|
||||||
|
/// </summary>
|
||||||
|
public int WriteableBytes
|
||||||
|
{
|
||||||
|
get { return Capacity - _writerIndex; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询是否可以写入
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="size">写入数据量</param>
|
||||||
|
public bool IsWriteable(int size = 1)
|
||||||
|
{
|
||||||
|
return Capacity - _writerIndex >= size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 标记写入的下标位置,便于某些时候回退到该位置。
|
||||||
|
/// </summary>
|
||||||
|
public void MarkWriterIndex()
|
||||||
|
{
|
||||||
|
_markedWriterIndex = _writerIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 回退到标记的写入下标位置
|
||||||
|
/// </summary>
|
||||||
|
public void ResetWriterIndex()
|
||||||
|
{
|
||||||
|
_writerIndex = _markedWriterIndex;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 读取操作
|
||||||
|
[Conditional("DEBUG")]
|
||||||
|
private void CheckReaderIndex(int length)
|
||||||
|
{
|
||||||
|
if (_readerIndex + length > _writerIndex)
|
||||||
|
{
|
||||||
|
throw new IndexOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] ReadBytes(int count)
|
||||||
|
{
|
||||||
|
CheckReaderIndex(count);
|
||||||
|
var data = new byte[count];
|
||||||
|
Buffer.BlockCopy(_buffer, _readerIndex, data, 0, count);
|
||||||
|
_readerIndex += count;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
public bool ReadBool()
|
||||||
|
{
|
||||||
|
CheckReaderIndex(1);
|
||||||
|
return _buffer[_readerIndex++] == 1;
|
||||||
|
}
|
||||||
|
public byte ReadByte()
|
||||||
|
{
|
||||||
|
CheckReaderIndex(1);
|
||||||
|
return _buffer[_readerIndex++];
|
||||||
|
}
|
||||||
|
public sbyte ReadSbyte()
|
||||||
|
{
|
||||||
|
return (sbyte)ReadByte();
|
||||||
|
}
|
||||||
|
public short ReadShort()
|
||||||
|
{
|
||||||
|
CheckReaderIndex(2);
|
||||||
|
short result = BitConverter.ToInt16(_buffer, _readerIndex);
|
||||||
|
_readerIndex += 2;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public ushort ReadUShort()
|
||||||
|
{
|
||||||
|
CheckReaderIndex(2);
|
||||||
|
ushort result = BitConverter.ToUInt16(_buffer, _readerIndex);
|
||||||
|
_readerIndex += 2;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public int ReadInt()
|
||||||
|
{
|
||||||
|
CheckReaderIndex(4);
|
||||||
|
int result = BitConverter.ToInt32(_buffer, _readerIndex);
|
||||||
|
_readerIndex += 4;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public uint ReadUInt()
|
||||||
|
{
|
||||||
|
CheckReaderIndex(4);
|
||||||
|
uint result = BitConverter.ToUInt32(_buffer, _readerIndex);
|
||||||
|
_readerIndex += 4;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public long ReadLong()
|
||||||
|
{
|
||||||
|
CheckReaderIndex(8);
|
||||||
|
long result = BitConverter.ToInt64(_buffer, _readerIndex);
|
||||||
|
_readerIndex += 8;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public ulong ReadULong()
|
||||||
|
{
|
||||||
|
CheckReaderIndex(8);
|
||||||
|
ulong result = BitConverter.ToUInt64(_buffer, _readerIndex);
|
||||||
|
_readerIndex += 8;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public float ReadFloat()
|
||||||
|
{
|
||||||
|
CheckReaderIndex(4);
|
||||||
|
float result = BitConverter.ToSingle(_buffer, _readerIndex);
|
||||||
|
_readerIndex += 4;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public double ReadDouble()
|
||||||
|
{
|
||||||
|
CheckReaderIndex(8);
|
||||||
|
double result = BitConverter.ToDouble(_buffer, _readerIndex);
|
||||||
|
_readerIndex += 8;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public string ReadUTF()
|
||||||
|
{
|
||||||
|
ushort count = ReadUShort();
|
||||||
|
CheckReaderIndex(count);
|
||||||
|
string result = Encoding.UTF8.GetString(_buffer, _readerIndex, count - 1); // 注意:读取的时候忽略字符串末尾写入结束符
|
||||||
|
_readerIndex += count;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<int> ReadListInt()
|
||||||
|
{
|
||||||
|
List<int> result = new List<int>();
|
||||||
|
int count = ReadInt();
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
result.Add(ReadInt());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public List<long> ReadListLong()
|
||||||
|
{
|
||||||
|
List<long> result = new List<long>();
|
||||||
|
int count = ReadInt();
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
result.Add(ReadLong());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public List<float> ReadListFloat()
|
||||||
|
{
|
||||||
|
List<float> result = new List<float>();
|
||||||
|
int count = ReadInt();
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
result.Add(ReadFloat());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public List<double> ReadListDouble()
|
||||||
|
{
|
||||||
|
List<double> result = new List<double>();
|
||||||
|
int count = ReadInt();
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
result.Add(ReadDouble());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public List<string> ReadListUTF()
|
||||||
|
{
|
||||||
|
List<string> result = new List<string>();
|
||||||
|
int count = ReadInt();
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
result.Add(ReadUTF());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2 ReadVector2()
|
||||||
|
{
|
||||||
|
float x = ReadFloat();
|
||||||
|
float y = ReadFloat();
|
||||||
|
return new Vector2(x, y);
|
||||||
|
}
|
||||||
|
public Vector3 ReadVector3()
|
||||||
|
{
|
||||||
|
float x = ReadFloat();
|
||||||
|
float y = ReadFloat();
|
||||||
|
float z = ReadFloat();
|
||||||
|
return new Vector3(x, y, z);
|
||||||
|
}
|
||||||
|
public Vector4 ReadVector4()
|
||||||
|
{
|
||||||
|
float x = ReadFloat();
|
||||||
|
float y = ReadFloat();
|
||||||
|
float z = ReadFloat();
|
||||||
|
float w = ReadFloat();
|
||||||
|
return new Vector4(x, y, z, w);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 写入操作
|
||||||
|
[Conditional("DEBUG")]
|
||||||
|
private void CheckWriterIndex(int length)
|
||||||
|
{
|
||||||
|
if (_writerIndex + length > Capacity)
|
||||||
|
{
|
||||||
|
throw new IndexOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteBytes(byte[] data)
|
||||||
|
{
|
||||||
|
WriteBytes(data, 0, data.Length);
|
||||||
|
}
|
||||||
|
public void WriteBytes(byte[] data, int offset, int count)
|
||||||
|
{
|
||||||
|
CheckWriterIndex(count);
|
||||||
|
Buffer.BlockCopy(data, offset, _buffer, _writerIndex, count);
|
||||||
|
_writerIndex += count;
|
||||||
|
}
|
||||||
|
public void WriteBool(bool value)
|
||||||
|
{
|
||||||
|
WriteByte((byte)(value ? 1 : 0));
|
||||||
|
}
|
||||||
|
public void WriteByte(byte value)
|
||||||
|
{
|
||||||
|
CheckWriterIndex(1);
|
||||||
|
_buffer[_writerIndex++] = value;
|
||||||
|
}
|
||||||
|
public void WriteSbyte(sbyte value)
|
||||||
|
{
|
||||||
|
// 注意:从sbyte强转到byte不会有数据变化或丢失
|
||||||
|
WriteByte((byte)value);
|
||||||
|
}
|
||||||
|
public void WriteShort(short value)
|
||||||
|
{
|
||||||
|
byte[] bytes = BitConverter.GetBytes(value);
|
||||||
|
WriteBytes(bytes);
|
||||||
|
}
|
||||||
|
public void WriteUShort(ushort value)
|
||||||
|
{
|
||||||
|
byte[] bytes = BitConverter.GetBytes(value);
|
||||||
|
WriteBytes(bytes);
|
||||||
|
}
|
||||||
|
public void WriteInt(int value)
|
||||||
|
{
|
||||||
|
byte[] bytes = BitConverter.GetBytes(value);
|
||||||
|
WriteBytes(bytes);
|
||||||
|
}
|
||||||
|
public void WriteUInt(uint value)
|
||||||
|
{
|
||||||
|
byte[] bytes = BitConverter.GetBytes(value);
|
||||||
|
WriteBytes(bytes);
|
||||||
|
}
|
||||||
|
public void WriteLong(long value)
|
||||||
|
{
|
||||||
|
byte[] bytes = BitConverter.GetBytes(value);
|
||||||
|
WriteBytes(bytes);
|
||||||
|
}
|
||||||
|
public void WriteULong(ulong value)
|
||||||
|
{
|
||||||
|
byte[] bytes = BitConverter.GetBytes(value);
|
||||||
|
WriteBytes(bytes);
|
||||||
|
}
|
||||||
|
public void WriteFloat(float value)
|
||||||
|
{
|
||||||
|
byte[] bytes = BitConverter.GetBytes(value);
|
||||||
|
WriteBytes(bytes);
|
||||||
|
}
|
||||||
|
public void WriteDouble(double value)
|
||||||
|
{
|
||||||
|
byte[] bytes = BitConverter.GetBytes(value);
|
||||||
|
WriteBytes(bytes);
|
||||||
|
}
|
||||||
|
public void WriteUTF(string value)
|
||||||
|
{
|
||||||
|
byte[] bytes = Encoding.UTF8.GetBytes(value);
|
||||||
|
int num = bytes.Length + 1; // 注意:字符串末尾写入结束符
|
||||||
|
if (num > ushort.MaxValue)
|
||||||
|
throw new FormatException($"String length cannot be greater than {ushort.MaxValue} !");
|
||||||
|
|
||||||
|
WriteUShort(Convert.ToUInt16(num));
|
||||||
|
WriteBytes(bytes);
|
||||||
|
WriteByte((byte)'\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteListInt(List<int> values)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
if (values != null)
|
||||||
|
count = values.Count;
|
||||||
|
|
||||||
|
WriteInt(count);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
WriteInt(values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void WriteListLong(List<long> values)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
if (values != null)
|
||||||
|
count = values.Count;
|
||||||
|
|
||||||
|
WriteInt(count);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
WriteLong(values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void WriteListFloat(List<float> values)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
if (values != null)
|
||||||
|
count = values.Count;
|
||||||
|
|
||||||
|
WriteInt(count);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
WriteFloat(values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void WriteListDouble(List<double> values)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
if (values != null)
|
||||||
|
count = values.Count;
|
||||||
|
|
||||||
|
WriteInt(count);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
WriteDouble(values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void WriteListUTF(List<string> values)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
if (values != null)
|
||||||
|
count = values.Count;
|
||||||
|
|
||||||
|
WriteInt(count);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
WriteUTF(values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteVector2(Vector2 value)
|
||||||
|
{
|
||||||
|
WriteFloat(value.x);
|
||||||
|
WriteFloat(value.y);
|
||||||
|
}
|
||||||
|
public void WriteVector3(Vector3 value)
|
||||||
|
{
|
||||||
|
WriteFloat(value.x);
|
||||||
|
WriteFloat(value.y);
|
||||||
|
WriteFloat(value.z);
|
||||||
|
}
|
||||||
|
public void WriteVector4(Vector4 value)
|
||||||
|
{
|
||||||
|
WriteFloat(value.x);
|
||||||
|
WriteFloat(value.y);
|
||||||
|
WriteFloat(value.z);
|
||||||
|
WriteFloat(value.w);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 大小端转换
|
||||||
|
/// </summary>
|
||||||
|
public static void ReverseOrder(byte[] data)
|
||||||
|
{
|
||||||
|
ReverseOrder(data, 0, data.Length);
|
||||||
|
}
|
||||||
|
public static void ReverseOrder(byte[] data, int offset, int length)
|
||||||
|
{
|
||||||
|
if (length <= 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int end = offset + length - 1;
|
||||||
|
int max = offset + length / 2;
|
||||||
|
byte temp;
|
||||||
|
for (int index = offset; index < max; index++, end--)
|
||||||
|
{
|
||||||
|
temp = data[end];
|
||||||
|
data[end] = data[index];
|
||||||
|
data[index] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: dcd2ef8207d26994497ffeb6e8f30d13
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniNetwork/Runtime/TCP.meta
vendored
Normal file
8
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniNetwork/Runtime/TCP.meta
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f491992d09ff2c1498b496c4746ff01a
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
301
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniNetwork/Runtime/TCP/TcpChannel.cs
vendored
Normal file
301
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniNetwork/Runtime/TCP/TcpChannel.cs
vendored
Normal file
|
@ -0,0 +1,301 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
|
||||||
|
namespace UniFramework.Network
|
||||||
|
{
|
||||||
|
internal class TcpChannel : IDisposable
|
||||||
|
{
|
||||||
|
private readonly SocketAsyncEventArgs _receiveArgs = new SocketAsyncEventArgs();
|
||||||
|
private readonly SocketAsyncEventArgs _sendArgs = new SocketAsyncEventArgs();
|
||||||
|
|
||||||
|
private readonly Queue<INetPackage> _sendQueue = new Queue<INetPackage>(10000);
|
||||||
|
private readonly Queue<INetPackage> _receiveQueue = new Queue<INetPackage>(10000);
|
||||||
|
private readonly List<INetPackage> _decodeTempList = new List<INetPackage>(100);
|
||||||
|
|
||||||
|
private byte[] _receiveBuffer;
|
||||||
|
private RingBuffer _encodeBuffer;
|
||||||
|
private RingBuffer _decodeBuffer;
|
||||||
|
private int _packageBodyMaxSize;
|
||||||
|
private INetPackageEncoder _packageEncoder;
|
||||||
|
private INetPackageDecoder _packageDecoder;
|
||||||
|
private bool _isSending = false;
|
||||||
|
private bool _isReceiving = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 通信Socket
|
||||||
|
/// </summary>
|
||||||
|
private Socket _socket;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 同步上下文
|
||||||
|
/// </summary>
|
||||||
|
private ThreadSyncContext _context;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化频道
|
||||||
|
/// </summary>
|
||||||
|
internal void InitChannel(ThreadSyncContext context, Socket socket, int packageBodyMaxSize, INetPackageEncoder encoder, INetPackageDecoder decoder)
|
||||||
|
{
|
||||||
|
if (packageBodyMaxSize <= 0)
|
||||||
|
throw new System.ArgumentException($"PackageMaxSize is invalid : {packageBodyMaxSize}");
|
||||||
|
|
||||||
|
_context = context;
|
||||||
|
_socket = socket;
|
||||||
|
_socket.NoDelay = true;
|
||||||
|
|
||||||
|
// 创建编码解码器
|
||||||
|
_packageBodyMaxSize = packageBodyMaxSize;
|
||||||
|
_packageEncoder = encoder;
|
||||||
|
_packageEncoder.RigistHandleErrorCallback(HandleError);
|
||||||
|
_packageDecoder = decoder;
|
||||||
|
_packageDecoder.RigistHandleErrorCallback(HandleError);
|
||||||
|
|
||||||
|
// 创建字节缓冲类
|
||||||
|
// 注意:字节缓冲区长度,推荐4倍最大包体长度
|
||||||
|
int encoderPackageMaxSize = packageBodyMaxSize + _packageEncoder.GetPackageHeaderSize();
|
||||||
|
int decoderPakcageMaxSize = packageBodyMaxSize + _packageDecoder.GetPackageHeaderSize();
|
||||||
|
_encodeBuffer = new RingBuffer(encoderPackageMaxSize * 4);
|
||||||
|
_decodeBuffer = new RingBuffer(decoderPakcageMaxSize * 4);
|
||||||
|
_receiveBuffer = new byte[decoderPakcageMaxSize];
|
||||||
|
|
||||||
|
// 创建IOCP接收类
|
||||||
|
_receiveArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
|
||||||
|
_receiveArgs.SetBuffer(_receiveBuffer, 0, _receiveBuffer.Length);
|
||||||
|
|
||||||
|
// 创建IOCP发送类
|
||||||
|
_sendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
|
||||||
|
_sendArgs.SetBuffer(_encodeBuffer.GetBuffer(), 0, _encodeBuffer.Capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检测Socket是否已连接
|
||||||
|
/// </summary>
|
||||||
|
public bool IsConnected()
|
||||||
|
{
|
||||||
|
if (_socket == null)
|
||||||
|
return false;
|
||||||
|
return _socket.Connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_socket != null)
|
||||||
|
_socket.Shutdown(SocketShutdown.Both);
|
||||||
|
|
||||||
|
_receiveArgs.Dispose();
|
||||||
|
_sendArgs.Dispose();
|
||||||
|
|
||||||
|
_sendQueue.Clear();
|
||||||
|
_receiveQueue.Clear();
|
||||||
|
_decodeTempList.Clear();
|
||||||
|
|
||||||
|
_encodeBuffer.Clear();
|
||||||
|
_decodeBuffer.Clear();
|
||||||
|
|
||||||
|
_isSending = false;
|
||||||
|
_isReceiving = false;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// throws if client process has already closed, so it is not necessary to catch.
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (_socket != null)
|
||||||
|
{
|
||||||
|
_socket.Close();
|
||||||
|
_socket = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 主线程内更新
|
||||||
|
/// </summary>
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
if (_socket == null || _socket.Connected == false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 接收数据
|
||||||
|
UpdateReceiving();
|
||||||
|
|
||||||
|
// 发送数据
|
||||||
|
UpdateSending();
|
||||||
|
}
|
||||||
|
private void UpdateReceiving()
|
||||||
|
{
|
||||||
|
if (_isReceiving == false)
|
||||||
|
{
|
||||||
|
_isReceiving = true;
|
||||||
|
|
||||||
|
// 请求操作
|
||||||
|
bool willRaiseEvent = _socket.ReceiveAsync(_receiveArgs);
|
||||||
|
if (!willRaiseEvent)
|
||||||
|
{
|
||||||
|
ProcessReceive(_receiveArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void UpdateSending()
|
||||||
|
{
|
||||||
|
if (_isSending == false && _sendQueue.Count > 0)
|
||||||
|
{
|
||||||
|
_isSending = true;
|
||||||
|
|
||||||
|
// 清空缓存
|
||||||
|
_encodeBuffer.Clear();
|
||||||
|
|
||||||
|
// 合并数据一起发送
|
||||||
|
while (_sendQueue.Count > 0)
|
||||||
|
{
|
||||||
|
// 如果不够写入一个最大的消息包
|
||||||
|
int encoderPackageMaxSize = _packageBodyMaxSize + _packageEncoder.GetPackageHeaderSize();
|
||||||
|
if (_encodeBuffer.WriteableBytes < encoderPackageMaxSize)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// 数据压码
|
||||||
|
INetPackage package = _sendQueue.Dequeue();
|
||||||
|
_packageEncoder.Encode(_packageBodyMaxSize, _encodeBuffer, package);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求操作
|
||||||
|
_sendArgs.SetBuffer(0, _encodeBuffer.ReadableBytes);
|
||||||
|
bool willRaiseEvent = _socket.SendAsync(_sendArgs);
|
||||||
|
if (!willRaiseEvent)
|
||||||
|
{
|
||||||
|
ProcessSend(_sendArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送网络包
|
||||||
|
/// </summary>
|
||||||
|
public void SendPackage(INetPackage package)
|
||||||
|
{
|
||||||
|
lock (_sendQueue)
|
||||||
|
{
|
||||||
|
_sendQueue.Enqueue(package);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取网络包
|
||||||
|
/// </summary>
|
||||||
|
public INetPackage PickPackage()
|
||||||
|
{
|
||||||
|
INetPackage package = null;
|
||||||
|
lock (_receiveQueue)
|
||||||
|
{
|
||||||
|
if (_receiveQueue.Count > 0)
|
||||||
|
package = _receiveQueue.Dequeue();
|
||||||
|
}
|
||||||
|
return package;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method is called whenever a receive or send operation is completed on a socket
|
||||||
|
/// </summary>
|
||||||
|
private void IO_Completed(object sender, SocketAsyncEventArgs e)
|
||||||
|
{
|
||||||
|
// determine which type of operation just completed and call the associated handler
|
||||||
|
switch (e.LastOperation)
|
||||||
|
{
|
||||||
|
case SocketAsyncOperation.Receive:
|
||||||
|
_context.Post(ProcessReceive, e);
|
||||||
|
break;
|
||||||
|
case SocketAsyncOperation.Send:
|
||||||
|
_context.Post(ProcessSend, e);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentException("The last operation completed on the socket was not a receive or send");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 数据接收完成时
|
||||||
|
/// </summary>
|
||||||
|
private void ProcessReceive(object obj)
|
||||||
|
{
|
||||||
|
if (_socket == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SocketAsyncEventArgs e = obj as SocketAsyncEventArgs;
|
||||||
|
|
||||||
|
// check if the remote host closed the connection
|
||||||
|
if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
|
||||||
|
{
|
||||||
|
// 如果数据写穿
|
||||||
|
if (_decodeBuffer.IsWriteable(e.BytesTransferred) == false)
|
||||||
|
{
|
||||||
|
HandleError(true, "The channel fatal error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拷贝数据
|
||||||
|
_decodeBuffer.WriteBytes(e.Buffer, 0, e.BytesTransferred);
|
||||||
|
|
||||||
|
// 数据解码
|
||||||
|
_decodeTempList.Clear();
|
||||||
|
_packageDecoder.Decode(_packageBodyMaxSize, _decodeBuffer, _decodeTempList);
|
||||||
|
lock (_receiveQueue)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _decodeTempList.Count; i++)
|
||||||
|
{
|
||||||
|
_receiveQueue.Enqueue(_decodeTempList[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为接收下一段数据,投递接收请求
|
||||||
|
e.SetBuffer(0, _receiveBuffer.Length);
|
||||||
|
bool willRaiseEvent = _socket.ReceiveAsync(e);
|
||||||
|
if (!willRaiseEvent)
|
||||||
|
{
|
||||||
|
ProcessReceive(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HandleError(true, $"ProcessReceive error : {e.SocketError}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 数据发送完成时
|
||||||
|
/// </summary>
|
||||||
|
private void ProcessSend(object obj)
|
||||||
|
{
|
||||||
|
if (_socket == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SocketAsyncEventArgs e = obj as SocketAsyncEventArgs;
|
||||||
|
if (e.SocketError == SocketError.Success)
|
||||||
|
{
|
||||||
|
_isSending = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HandleError(true, $"ProcessSend error : {e.SocketError}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 捕获异常错误
|
||||||
|
/// </summary>
|
||||||
|
private void HandleError(bool isDispose, string error)
|
||||||
|
{
|
||||||
|
UniLogger.Error(error);
|
||||||
|
if (isDispose) Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 841a2723c2b01744fb4bcd170411249f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
159
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniNetwork/Runtime/TCP/TcpClient.cs
vendored
Normal file
159
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/UniFramework/UniNetwork/Runtime/TCP/TcpClient.cs
vendored
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
|
||||||
|
namespace UniFramework.Network
|
||||||
|
{
|
||||||
|
public class TcpClient : IDisposable
|
||||||
|
{
|
||||||
|
private class UserToken
|
||||||
|
{
|
||||||
|
public System.Action<SocketError> Callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TcpChannel _channel;
|
||||||
|
private readonly int _packageBodyMaxSize;
|
||||||
|
private readonly INetPackageEncoder _encoder;
|
||||||
|
private readonly INetPackageDecoder _decoder;
|
||||||
|
private readonly ThreadSyncContext _syncContext;
|
||||||
|
|
||||||
|
private TcpClient()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
internal TcpClient(int packageBodyMaxSize, INetPackageEncoder encoder, INetPackageDecoder decoder)
|
||||||
|
{
|
||||||
|
_packageBodyMaxSize = packageBodyMaxSize;
|
||||||
|
_encoder = encoder;
|
||||||
|
_decoder = decoder;
|
||||||
|
_syncContext = new ThreadSyncContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新网络
|
||||||
|
/// </summary>
|
||||||
|
internal void Update()
|
||||||
|
{
|
||||||
|
_syncContext.Update();
|
||||||
|
|
||||||
|
if (_channel != null)
|
||||||
|
_channel.Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 销毁网络
|
||||||
|
/// </summary>
|
||||||
|
internal void Destroy()
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_channel != null)
|
||||||
|
{
|
||||||
|
_channel.Dispose();
|
||||||
|
_channel = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送网络包
|
||||||
|
/// </summary>
|
||||||
|
public void SendPackage(INetPackage package)
|
||||||
|
{
|
||||||
|
if (_channel != null)
|
||||||
|
_channel.SendPackage(package);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取网络包
|
||||||
|
/// </summary>
|
||||||
|
public INetPackage PickPackage()
|
||||||
|
{
|
||||||
|
if (_channel == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return _channel.PickPackage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检测Socket是否已连接
|
||||||
|
/// </summary>
|
||||||
|
public bool IsConnected()
|
||||||
|
{
|
||||||
|
if (_channel == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return _channel.IsConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步连接
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="remote">IP终端</param>
|
||||||
|
/// <param name="callback">连接回调</param>
|
||||||
|
public void ConnectAsync(IPEndPoint remote, System.Action<SocketError> callback)
|
||||||
|
{
|
||||||
|
UserToken token = new UserToken()
|
||||||
|
{
|
||||||
|
Callback = callback,
|
||||||
|
};
|
||||||
|
|
||||||
|
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
|
||||||
|
args.RemoteEndPoint = remote;
|
||||||
|
args.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
|
||||||
|
args.UserToken = token;
|
||||||
|
|
||||||
|
Socket clientSock = new Socket(remote.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||||
|
bool willRaiseEvent = clientSock.ConnectAsync(args);
|
||||||
|
if (!willRaiseEvent)
|
||||||
|
{
|
||||||
|
ProcessConnected(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理连接请求
|
||||||
|
/// </summary>
|
||||||
|
private void ProcessConnected(object obj)
|
||||||
|
{
|
||||||
|
SocketAsyncEventArgs e = obj as SocketAsyncEventArgs;
|
||||||
|
UserToken token = (UserToken)e.UserToken;
|
||||||
|
if (e.SocketError == SocketError.Success)
|
||||||
|
{
|
||||||
|
if (_channel != null)
|
||||||
|
throw new Exception("TcpClient channel is created.");
|
||||||
|
|
||||||
|
// 创建频道
|
||||||
|
_channel = new TcpChannel();
|
||||||
|
_channel.InitChannel(_syncContext, e.ConnectSocket, _packageBodyMaxSize, _encoder, _decoder);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UniLogger.Error($"Network connecte error : {e.SocketError}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回调函数
|
||||||
|
if (token.Callback != null)
|
||||||
|
token.Callback.Invoke(e.SocketError);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
|
||||||
|
{
|
||||||
|
switch (e.LastOperation)
|
||||||
|
{
|
||||||
|
case SocketAsyncOperation.Connect:
|
||||||
|
_syncContext.Post(ProcessConnected, e);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentException("The last operation completed on the socket was not a connect");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 23ae68566c8488c4099ef4652dd317c2
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,38 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace UniFramework.Network
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 同步其它线程里的回调到主线程里
|
||||||
|
/// 注意:Unity3D中需要设置Scripting Runtime Version为.NET4.6
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class ThreadSyncContext : SynchronizationContext
|
||||||
|
{
|
||||||
|
private readonly ConcurrentQueue<Action> _safeQueue = new ConcurrentQueue<Action>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新同步队列
|
||||||
|
/// </summary>
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (_safeQueue.TryDequeue(out Action action) == false)
|
||||||
|
return;
|
||||||
|
action.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 向同步队列里投递一个回调方法
|
||||||
|
/// </summary>
|
||||||
|
public override void Post(SendOrPostCallback callback, object state)
|
||||||
|
{
|
||||||
|
Action action = new Action(() => { callback(state); });
|
||||||
|
_safeQueue.Enqueue(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue