TE6 打飞机Demo

TE6 打飞机Demo
This commit is contained in:
ALEXTANGXIAO
2025-04-26 23:23:39 +08:00
parent aaf7ddbee8
commit 1e195ed3b4
1921 changed files with 47050 additions and 44359 deletions

View File

@@ -0,0 +1,15 @@
using TEngine;
namespace Procedure
{
public abstract class ProcedureBase : TEngine.ProcedureBase
{
/// <summary>
/// 获取流程是否使用原生对话框
/// 在一些特殊的流程(如游戏逻辑对话框资源更新完成前的流程)中,可以考虑调用原生对话框进行消息提示行为
/// </summary>
public abstract bool UseNativeDialog { get; }
protected readonly IResourceModule _resourceModule = ModuleSystem.GetModule<IResourceModule>();
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 531a832bf8dfd734395de52f4141082f
timeCreated: 1528026158
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,35 @@
using Launcher;
using TEngine;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
namespace Procedure
{
/// <summary>
/// 流程 => 清理缓存。
/// </summary>
public class ProcedureClearCache : ProcedureBase
{
public override bool UseNativeDialog { get; }
private ProcedureOwner _procedureOwner;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
_procedureOwner = procedureOwner;
Log.Info("清理未使用的缓存文件!");
LauncherMgr.Show(UIDefine.UILoadUpdate, $"清理未使用的缓存文件...");
var operation = _resourceModule.ClearCacheFilesAsync();
operation.Completed += Operation_Completed;
}
private void Operation_Completed(YooAsset.AsyncOperationBase obj)
{
LauncherMgr.Show(UIDefine.UILoadUpdate, $"清理完成 即将进入游戏...");
ChangeState<ProcedurePreload>(_procedureOwner);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b9e990ba01f24ac584014438abc7f3b7
timeCreated: 1679635352

View File

@@ -0,0 +1,77 @@
using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using Launcher;
using TEngine;
using UnityEngine;
using UnityEngine.Networking;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
using Utility = TEngine.Utility;
namespace Procedure
{
public class ProcedureCreateDownloader : ProcedureBase
{
private int _curTryCount;
private const int MAX_TRY_COUNT = 3;
public override bool UseNativeDialog { get; }
private ProcedureOwner _procedureOwner;
private ResourceDownloaderOperation _downloader;
private int _totalDownloadCount;
private string _totalSizeMb;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
_procedureOwner = procedureOwner;
Log.Info("创建补丁下载器");
LauncherMgr.Show(UIDefine.UILoadUpdate, $"创建补丁下载器...");
CreateDownloader().Forget();
}
private async UniTaskVoid CreateDownloader()
{
await UniTask.Delay(TimeSpan.FromSeconds(0.5f));
_downloader = _resourceModule.CreateResourceDownloader();
if (_downloader.TotalDownloadCount == 0)
{
Log.Info("Not found any download files !");
ChangeState<ProcedureDownloadOver>(_procedureOwner);
}
else
{
//A total of 10 files were found that need to be downloaded
Log.Info($"Found total {_downloader.TotalDownloadCount} files that need download ");
// 发现新更新文件后,挂起流程系统
// 注意:开发者需要在下载前检测磁盘空间不足
_totalDownloadCount = _downloader.TotalDownloadCount;
long totalDownloadBytes = _downloader.TotalDownloadBytes;
float sizeMb = totalDownloadBytes / 1048576f;
sizeMb = Mathf.Clamp(sizeMb, 0.1f, float.MaxValue);
_totalSizeMb = sizeMb.ToString("f1");
LauncherMgr.ShowMessageBox($"Found update patch files, Total count {_totalDownloadCount} Total size {_totalSizeMb}MB", MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_StartUpdate_Notice
, StartDownFile, Application.Quit);
}
}
void StartDownFile()
{
ChangeState<ProcedureDownloadFile>(_procedureOwner);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2323d6b64e5a4a1195b9ed1795a64bdd
timeCreated: 1679634708

View File

@@ -0,0 +1,106 @@
using System;
using Cysharp.Threading.Tasks;
using Launcher;
using TEngine;
using UnityEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
using Utility = TEngine.Utility;
namespace Procedure
{
public class ProcedureDownloadFile : ProcedureBase
{
public override bool UseNativeDialog { get; }
private ProcedureOwner _procedureOwner;
private float _lastUpdateDownloadedSize;
private float _totalSpeed;
private int _speedSampleCount;
private float CurrentSpeed
{
get
{
float interval = Math.Max(Time.deltaTime, 0.01f); // 防止deltaTime过小
var sizeDiff = _resourceModule.Downloader.CurrentDownloadBytes - _lastUpdateDownloadedSize;
_lastUpdateDownloadedSize = _resourceModule.Downloader.CurrentDownloadBytes;
var speed = sizeDiff / interval;
// 使用滑动窗口计算平均速度
_totalSpeed += speed;
_speedSampleCount++;
return _totalSpeed / _speedSampleCount;
}
}
protected override void OnEnter(ProcedureOwner procedureOwner)
{
_procedureOwner = procedureOwner;
Log.Info("开始下载更新文件!");
LauncherMgr.Show(UIDefine.UILoadUpdate, "开始下载更新文件...");
BeginDownload().Forget();
}
private async UniTaskVoid BeginDownload()
{
var downloader = _resourceModule.Downloader;
// 注册下载回调
downloader.DownloadErrorCallback = OnDownloadErrorCallback;
downloader.DownloadUpdateCallback = OnDownloadProgressCallback;
downloader.BeginDownload();
await downloader;
// 检测下载结果
if (downloader.Status != EOperationStatus.Succeed)
return;
ChangeState<ProcedureDownloadOver>(_procedureOwner);
}
private void OnDownloadErrorCallback(DownloadErrorData downloadErrorData)
{
LauncherMgr.ShowMessageBox($"Failed to download file : {downloadErrorData.FileName}",
MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Default,
() => { ChangeState<ProcedureCreateDownloader>(_procedureOwner); }, UnityEngine.Application.Quit);
}
private void OnDownloadProgressCallback(DownloadUpdateData downloadUpdateData)
{
string currentSizeMb = (downloadUpdateData.CurrentDownloadBytes / 1048576f).ToString("f1");
string totalSizeMb = (downloadUpdateData.TotalDownloadBytes / 1048576f).ToString("f1");
float progressPercentage = _resourceModule.Downloader.Progress * 100;
string speed = Utility.File.GetLengthString((int)CurrentSpeed);
string line1 = Utility.Text.Format("正在更新,已更新 {0}/{1} ({2:F2}%)", downloadUpdateData.CurrentDownloadCount,
downloadUpdateData.TotalDownloadCount, progressPercentage);
string line2 = Utility.Text.Format("已更新大小 {0}MB/{1}MB", currentSizeMb, totalSizeMb);
string line3 = Utility.Text.Format("当前网速 {0}/s剩余时间 {1}", speed,
GetRemainingTime(downloadUpdateData.TotalDownloadBytes, downloadUpdateData.CurrentDownloadBytes,
CurrentSpeed));
LauncherMgr.UpdateUIProgress(_resourceModule.Downloader.Progress);
LauncherMgr.Show(UIDefine.UILoadUpdate, $"{line1}\n{line2}\n{line3}");
Log.Info($"{line1} {line2} {line3}");
}
private string GetRemainingTime(long totalBytes, long currentBytes, float speed)
{
int needTime = 0;
if (speed > 0)
{
needTime = (int)((totalBytes - currentBytes) / speed);
}
TimeSpan ts = new TimeSpan(0, 0, needTime);
return ts.ToString(@"mm\:ss");
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 19b2799aad474898b96d8551856e6b40
timeCreated: 1679642638

View File

@@ -0,0 +1,36 @@
using Launcher;
using TEngine;
using UnityEngine;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
namespace Procedure
{
public class ProcedureDownloadOver : ProcedureBase
{
public override bool UseNativeDialog { get; }
private bool _needClearCache;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
Log.Info("下载完成!!!");
LauncherMgr.Show(UIDefine.UILoadUpdate, $"下载完成...");
// 下载完成之后再保存本地版本。
Utility.PlayerPrefs.SetString("GAME_VERSION", _resourceModule.PackageVersion);
}
protected override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds)
{
if (_needClearCache)
{
ChangeState<ProcedureClearCache>(procedureOwner);
}
else
{
ChangeState<ProcedurePreload>(procedureOwner);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b3998cede0874b49afc170062e554c34
timeCreated: 1679635148

View File

@@ -0,0 +1,123 @@
using System;
using Cysharp.Threading.Tasks;
using Launcher;
using TEngine;
using UnityEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
using Utility = TEngine.Utility;
namespace Procedure
{
/// <summary>
/// 流程 => 初始化Package。
/// </summary>
public class ProcedureInitPackage : ProcedureBase
{
public override bool UseNativeDialog { get; }
private ProcedureOwner _procedureOwner;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
base.OnEnter(procedureOwner);
_procedureOwner = procedureOwner;
//Fire Forget立刻触发UniTask初始化Package
InitPackage(procedureOwner).Forget();
}
private async UniTaskVoid InitPackage(ProcedureOwner procedureOwner)
{
try
{
var initializationOperation = await _resourceModule.InitPackage(_resourceModule.DefaultPackageName);
if (initializationOperation.Status == EOperationStatus.Succeed)
{
//热更新阶段文本初始化
LoadText.Instance.InitConfigData(null);
EPlayMode playMode = _resourceModule.PlayMode;
// 编辑器模式。
if (playMode == EPlayMode.EditorSimulateMode)
{
Log.Info("Editor resource mode detected.");
ChangeState<ProcedureInitResources>(procedureOwner);
}
// 单机模式。
else if (playMode == EPlayMode.OfflinePlayMode)
{
Log.Info("Package resource mode detected.");
ChangeState<ProcedureInitResources>(procedureOwner);
}
// 可更新模式。
else if (playMode == EPlayMode.HostPlayMode ||
playMode == EPlayMode.WebPlayMode)
{
// 打开启动UI。
LauncherMgr.Show(UIDefine.UILoadUpdate);
Log.Info("Updatable resource mode detected.");
ChangeState<ProcedureInitResources>(procedureOwner);
}
else
{
Log.Error("UnKnow resource mode detected Please check???");
}
}
else
{
// 打开启动UI。
LauncherMgr.Show(UIDefine.UILoadUpdate);
Log.Error($"{initializationOperation.Error}");
// 打开启动UI。
LauncherMgr.Show(UIDefine.UILoadUpdate, $"资源初始化失败!");
LauncherMgr.ShowMessageBox(
$"资源初始化失败!点击确认重试 \n \n <color=#FF0000>原因{initializationOperation.Error}</color>",
MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry
, () => { Retry(procedureOwner); }, UnityEngine.Application.Quit);
}
}
catch (Exception e)
{
OnInitPackageFailed(procedureOwner, e.Message);
}
}
private void OnInitPackageFailed(ProcedureOwner procedureOwner, string message)
{
// 打开启动UI。
LauncherMgr.Show(UIDefine.UILoadUpdate);
Log.Error($"{message}");
// 打开启动UI。
LauncherMgr.Show(UIDefine.UILoadUpdate, $"资源初始化失败!");
if (message.Contains("PackageManifest_DefaultPackage.version Error : HTTP/1.1 404 Not Found"))
{
message = "请检查StreamingAssets/package/DefaultPackage/PackageManifest_DefaultPackage.version是否存在";
}
LauncherMgr.ShowMessageBox($"资源初始化失败!点击确认重试 \n \n <color=#FF0000>原因{message}</color>", MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry
, () => { Retry(procedureOwner); },
Application.Quit);
}
private void Retry(ProcedureOwner procedureOwner)
{
// 打开启动UI。
LauncherMgr.Show(UIDefine.UILoadUpdate, $"重新初始化资源中...");
InitPackage(procedureOwner).Forget();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0fc41b358bdc498681a63f72ad8942c8
timeCreated: 1679639147

View File

@@ -0,0 +1,174 @@
using System.Collections;
using Launcher;
using TEngine;
using UnityEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
namespace Procedure
{
public class ProcedureInitResources : ProcedureBase
{
private bool _initResourcesComplete = false;
public override bool UseNativeDialog => true;
private ProcedureOwner _procedureOwner;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
_procedureOwner = procedureOwner;
base.OnEnter(procedureOwner);
_initResourcesComplete = false;
LauncherMgr.Show(UIDefine.UILoadUpdate, "初始化资源中...");
// 注意:使用单机模式并初始化资源前,需要先构建 AssetBundle 并复制到 StreamingAssets 中,否则会产生 HTTP 404 错误
Utility.Unity.StartCoroutine(InitResources(procedureOwner));
}
private void ChangeToCreateDownloaderState(ProcedureOwner procedureOwner)
{
ChangeState<ProcedureCreateDownloader>(procedureOwner);
}
protected override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);
if (!_initResourcesComplete)
{
// 初始化资源未完成则继续等待
return;
}
if (_resourceModule.PlayMode == EPlayMode.HostPlayMode || _resourceModule.PlayMode == EPlayMode.WebPlayMode)
{
//线上最新版本operation.PackageVersion
Log.Debug($"Updated package Version : from {_resourceModule.GetPackageVersion()} to {_resourceModule.PackageVersion}");
//注意:保存资源版本号作为下次默认启动的版本!
// 如果当前是WebGL或者是边玩边下载直接进入预加载阶段。
if (_resourceModule.PlayMode == EPlayMode.WebPlayMode ||
_resourceModule.UpdatableWhilePlaying)
{
// 边玩边下载还可以拓展首包支持。
ChangeToPreloadState(procedureOwner);
return;
}
ChangeToCreateDownloaderState(procedureOwner);
return;
}
ChangeToPreloadState(procedureOwner);
}
//// <summary>
/// 初始化资源流程。
/// <remarks>YooAsset 需要保持编辑器、单机、联机模式流程一致。</remarks>
private IEnumerator InitResources(ProcedureOwner procedureOwner)
{
Log.Info("更新资源清单!!!");
LauncherMgr.Show(UIDefine.UILoadUpdate, $"更新清单文件...");
// 1. 获取资源清单的版本信息
var operation1 = _resourceModule.RequestPackageVersionAsync();
yield return operation1;
if (operation1.Status != EOperationStatus.Succeed)
{
OnInitResourcesError(procedureOwner, operation1.Error);
yield break;
}
var packageVersion = operation1.PackageVersion;
_resourceModule.PackageVersion = packageVersion;
if (Utility.PlayerPrefs.HasKey("GAME_VERSION"))
{
Utility.PlayerPrefs.SetString("GAME_VERSION", _resourceModule.PackageVersion);
}
Log.Info($"Init resource package version : {packageVersion}");
// 2. 传入的版本信息更新资源清单
var operation2 = _resourceModule.UpdatePackageManifestAsync(packageVersion);
yield return operation2;
if (operation2.Status != EOperationStatus.Succeed)
{
OnInitResourcesError(procedureOwner, operation2.Error);
yield break;
}
_initResourcesComplete = true;
}
private void ChangeToPreloadState(ProcedureOwner procedureOwner)
{
ChangeState<ProcedurePreload>(procedureOwner);
}
private void OnInitResourcesError(ProcedureOwner procedureOwner, string message)
{
// 检查设备网络连接状态。
if (_resourceModule.PlayMode == EPlayMode.HostPlayMode)
{
if (!IsNeedUpdate())
{
return;
}
else
{
Log.Error(message);
LauncherMgr.ShowMessageBox($"获取远程版本失败!点击确认重试\n <color=#FF0000>{message}</color>", MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry,
Application.Quit);
return;
}
}
Log.Error(message);
LauncherMgr.ShowMessageBox($"初始化资源失败!点击确认重试 \n <color=#FF0000>{message}</color>", MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry, () => { Utility.Unity.StartCoroutine(InitResources(procedureOwner)); }, Application.Quit);
}
private bool IsNeedUpdate()
{
// 如果不能联网且当前游戏非强制(不更新可以进入游戏。)
if (Settings.UpdateSetting.UpdateStyle == UpdateStyle.Optional && !_resourceModule.UpdatableWhilePlaying)
{
// 获取上次成功记录的版本
string packageVersion = Utility.PlayerPrefs.GetString("GAME_VERSION", string.Empty);
if (string.IsNullOrEmpty(packageVersion))
{
LauncherMgr.Show(UIDefine.UILoadUpdate, LoadText.Instance.Label_Net_UnReachable);
LauncherMgr.ShowMessageBox("没有找到本地版本记录,需要更新资源!", MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry,
() => { Utility.Unity.StartCoroutine(InitResources(_procedureOwner)); },
Application.Quit);
return false;
}
_resourceModule.PackageVersion = packageVersion;
if (Settings.UpdateSetting.UpdateNotice == UpdateNotice.Notice)
{
LauncherMgr.Show(UIDefine.UILoadUpdate, LoadText.Instance.Label_Load_Notice);
LauncherMgr.ShowMessageBox($"更新失败,检测到可选资源更新,推荐完成更新提升游戏体验! \\n \\n 确定再试一次,取消进入游戏", MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry,
() => { Utility.Unity.StartCoroutine(InitResources(_procedureOwner)); },
() => { ChangeState<ProcedurePreload>(_procedureOwner); });
}
else
{
ChangeState<ProcedurePreload>(_procedureOwner);
}
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 49285f361d08436aab23bdf729c10ea0
timeCreated: 1679635748

View File

@@ -0,0 +1,94 @@
using Launcher;
using TEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
namespace Procedure
{
/// <summary>
/// 流程 => 启动器。
/// </summary>
public class ProcedureLaunch : ProcedureBase
{
public override bool UseNativeDialog => true;
private IAudioModule _audioModule;
protected override void OnInit(ProcedureOwner procedureOwner)
{
_audioModule = ModuleSystem.GetModule<IAudioModule>();
base.OnInit(procedureOwner);
}
protected override void OnEnter(ProcedureOwner procedureOwner)
{
base.OnEnter(procedureOwner);
//热更新UI初始化
LauncherMgr.Initialize();
// 语言配置:设置当前使用的语言,如果不设置,则默认使用操作系统语言
InitLanguageSettings();
// 声音配置:根据用户配置数据,设置即将使用的声音选项
InitSoundSettings();
}
protected override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);
// 运行一帧即切换到 Splash 展示流程
ChangeState<ProcedureSplash>(procedureOwner);
}
private void InitLanguageSettings()
{
if (_resourceModule.PlayMode == EPlayMode.EditorSimulateMode && RootModule.Instance.EditorLanguage == Language.Unspecified)
{
// 编辑器资源模式直接使用 Inspector 上设置的语言
return;
}
ILocalizationModule localizationModule = ModuleSystem.GetModule<ILocalizationModule>();
Language language = localizationModule.Language;
if (Utility.PlayerPrefs.HasSetting(Constant.Setting.Language))
{
try
{
string languageString = Utility.PlayerPrefs.GetString(Constant.Setting.Language);
language = (Language)System.Enum.Parse(typeof(Language), languageString);
}
catch(System.Exception exception)
{
Log.Error("Init language error, reason {0}",exception.ToString());
}
}
if (language != Language.English
&& language != Language.ChineseSimplified
&& language != Language.ChineseTraditional)
{
// 若是暂不支持的语言,则使用英语
language = Language.English;
Utility.PlayerPrefs.SetString(Constant.Setting.Language, language.ToString());
Utility.PlayerPrefs.Save();
}
localizationModule.Language = language;
Log.Info("Init language settings complete, current language is '{0}'.", language.ToString());
}
private void InitSoundSettings()
{
_audioModule.MusicEnable = !Utility.PlayerPrefs.GetBool(Constant.Setting.MusicMuted, false);
_audioModule.MusicVolume = Utility.PlayerPrefs.GetFloat(Constant.Setting.MusicVolume, 1f);
_audioModule.SoundEnable = !Utility.PlayerPrefs.GetBool(Constant.Setting.SoundMuted, false);
_audioModule.SoundVolume = Utility.PlayerPrefs.GetFloat(Constant.Setting.SoundVolume, 1f);
_audioModule.UISoundEnable = !Utility.PlayerPrefs.GetBool(Constant.Setting.UISoundMuted, false);
_audioModule.UISoundVolume = Utility.PlayerPrefs.GetFloat(Constant.Setting.UISoundVolume, 1f);
Log.Info("Init sound settings complete.");
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d6c3d65d0c3a2104383e82700116b3d1
timeCreated: 1528026159
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,294 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
#if ENABLE_HYBRIDCLR
using HybridCLR;
#endif
using UnityEngine;
using TEngine;
using System.Reflection;
using YooAsset;
using Cysharp.Threading.Tasks;
namespace Procedure
{
/// <summary>
/// 流程加载器 - 代码初始化
/// </summary>
public class ProcedureLoadAssembly : ProcedureBase
{
private bool _enableAddressable = true;
public override bool UseNativeDialog => true;
private int _loadAssetCount;
private int _loadMetadataAssetCount;
private int _failureAssetCount;
private int _failureMetadataAssetCount;
private bool _loadAssemblyComplete;
private bool _loadMetadataAssemblyComplete;
private bool _loadAssemblyWait;
private bool _loadMetadataAssemblyWait;
private Assembly _mainLogicAssembly;
private List<Assembly> _hotfixAssemblyList;
private IFsm<IProcedureModule> _procedureOwner;
private UpdateSetting _setting;
protected override void OnInit(IFsm<IProcedureModule> procedureOwner)
{
base.OnInit(procedureOwner);
_setting = Settings.UpdateSetting;
}
protected override void OnEnter(IFsm<IProcedureModule> procedureOwner)
{
base.OnEnter(procedureOwner);
Log.Debug("HybridCLR ProcedureLoadAssembly OnEnter");
_procedureOwner = procedureOwner;
LoadAssembly().Forget();
}
private async UniTaskVoid LoadAssembly()
{
_loadAssemblyComplete = false;
_hotfixAssemblyList = new List<Assembly>();
//AOT Assembly加载原始metadata
if (_setting.Enable)
{
#if !UNITY_EDITOR
_loadMetadataAssemblyComplete = false;
LoadMetadataForAOTAssembly();
#else
_loadMetadataAssemblyComplete = true;
#endif
}
else
{
_loadMetadataAssemblyComplete = true;
}
if (!_setting.Enable || _resourceModule.PlayMode == EPlayMode.EditorSimulateMode)
{
_mainLogicAssembly = GetMainLogicAssembly();
}
else
{
if (_setting.Enable)
{
foreach (string hotUpdateDllName in _setting.HotUpdateAssemblies)
{
var assetLocation = hotUpdateDllName;
if (!_enableAddressable)
{
assetLocation = Utility.Path.GetRegularPath(
Path.Combine(
"Assets",
_setting.AssemblyTextAssetPath,
$"{hotUpdateDllName}{_setting.AssemblyTextAssetExtension}"));
}
Log.Debug($"LoadAsset: [ {assetLocation} ]");
_loadAssetCount++;
var result = await _resourceModule.LoadAssetAsync<TextAsset>(assetLocation);
LoadAssetSuccess(result);
}
_loadAssemblyWait = true;
}
else
{
_mainLogicAssembly = GetMainLogicAssembly();
}
}
if (_loadAssetCount == 0)
{
_loadAssemblyComplete = true;
}
}
protected override void OnUpdate(IFsm<IProcedureModule> procedureOwner, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);
if (!_loadAssemblyComplete)
{
return;
}
if (!_loadMetadataAssemblyComplete)
{
return;
}
AllAssemblyLoadComplete();
}
private void AllAssemblyLoadComplete()
{
ChangeState<ProcedureStartGame>(_procedureOwner);
#if UNITY_EDITOR
_mainLogicAssembly = GetMainLogicAssembly();
#endif
if (_mainLogicAssembly == null)
{
Log.Fatal($"Main logic assembly missing. Please check \'ENABLE_HYBRIDCLR\' is defined in Player Settings And check the file of {_setting.LogicMainDllName}.bytes is exits.");
return;
}
var appType = _mainLogicAssembly.GetType("GameApp");
if (appType == null)
{
Log.Fatal($"Main logic type 'GameMain' missing.");
return;
}
var entryMethod = appType.GetMethod("Entrance");
if (entryMethod == null)
{
Log.Fatal($"Main logic entry method 'Entrance' missing.");
return;
}
object[] objects = new object[] { new object[] { _hotfixAssemblyList } };
entryMethod.Invoke(appType, objects);
}
private Assembly GetMainLogicAssembly()
{
_hotfixAssemblyList.Clear();
Assembly mainLogicAssembly = null;
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (string.Compare(_setting.LogicMainDllName, $"{assembly.GetName().Name}.dll",
StringComparison.Ordinal) == 0)
{
mainLogicAssembly = assembly;
}
foreach (var hotUpdateDllName in _setting.HotUpdateAssemblies)
{
if (hotUpdateDllName == $"{assembly.GetName().Name}.dll")
{
_hotfixAssemblyList.Add(assembly);
}
}
if (mainLogicAssembly != null && _hotfixAssemblyList.Count == _setting.HotUpdateAssemblies.Count)
{
break;
}
}
return mainLogicAssembly;
}
/// <summary>
/// 加载代码资源成功回调。
/// </summary>
/// <param name="textAsset">代码资产。</param>
private void LoadAssetSuccess(TextAsset textAsset)
{
_loadAssetCount--;
if (textAsset == null)
{
Log.Warning($"Load Assembly failed.");
return;
}
var assetName = textAsset.name;
Log.Debug($"LoadAssetSuccess, assetName: [ {assetName} ]");
try
{
var assembly = Assembly.Load(textAsset.bytes);
if (string.Compare(_setting.LogicMainDllName, assetName, StringComparison.Ordinal) == 0)
{
_mainLogicAssembly = assembly;
}
_hotfixAssemblyList.Add(assembly);
Log.Debug($"Assembly [ {assembly.GetName().Name} ] loaded");
}
catch (Exception e)
{
_failureAssetCount++;
Log.Fatal(e);
throw;
}
finally
{
_loadAssemblyComplete = _loadAssemblyWait && 0 == _loadAssetCount;
}
_resourceModule.UnloadAsset(textAsset);
}
/// <summary>
/// 为Aot Assembly加载原始metadata 这个代码放Aot或者热更新都行。
/// 一旦加载后如果AOT泛型函数对应native实现不存在则自动替换为解释模式执行。
/// </summary>
public void LoadMetadataForAOTAssembly()
{
// 可以加载任意aot assembly的对应的dll。但要求dll必须与unity build过程中生成的裁剪后的dll一致而不能直接使用原始dll。
// 我们在BuildProcessor_xxx里添加了处理代码这些裁剪后的dll在打包时自动被复制到 {项目目录}/HybridCLRData/AssembliesPostIl2CppStrip/{Target} 目录。
// 注意补充元数据是给AOT dll补充元数据而不是给热更新dll补充元数据。
// 热更新dll不缺元数据不需要补充如果调用LoadMetadataForAOTAssembly会返回错误
if (_setting.AOTMetaAssemblies.Count == 0)
{
_loadMetadataAssemblyComplete = true;
return;
}
foreach (string aotDllName in _setting.AOTMetaAssemblies)
{
var assetLocation = aotDllName;
if (!_enableAddressable)
{
assetLocation = Utility.Path.GetRegularPath(
Path.Combine(
"Assets",
_setting.AssemblyTextAssetPath,
$"{aotDllName}{_setting.AssemblyTextAssetExtension}"));
}
Log.Debug($"LoadMetadataAsset: [ {assetLocation} ]");
_loadMetadataAssetCount++;
_resourceModule.LoadAsset<TextAsset>(assetLocation, LoadMetadataAssetSuccess);
}
_loadMetadataAssemblyWait = true;
}
/// <summary>
/// 加载元数据资源成功回调。
/// </summary>
/// <param name="textAsset">代码资产。</param>
private void LoadMetadataAssetSuccess(TextAsset textAsset)
{
_loadMetadataAssetCount--;
if (null == textAsset)
{
Log.Debug($"LoadMetadataAssetSuccess:Load Metadata failed.");
return;
}
string assetName = textAsset.name;
Log.Debug($"LoadMetadataAssetSuccess, assetName: [ {assetName} ]");
try
{
byte[] dllBytes = textAsset.bytes;
#if ENABLE_HYBRIDCLR
// 加载assembly对应的dll会自动为它hook。一旦Aot泛型函数的native函数不存在用解释器版本代码
HomologousImageMode mode = HomologousImageMode.SuperSet;
LoadImageErrorCode err = (LoadImageErrorCode)HybridCLR.RuntimeApi.LoadMetadataForAOTAssembly(dllBytes,mode);
Log.Warning($"LoadMetadataForAOTAssembly:{assetName}. mode:{mode} ret:{err}");
#endif
}
catch (Exception e)
{
_failureMetadataAssetCount++;
Log.Fatal(e.Message);
throw;
}
finally
{
_loadMetadataAssemblyComplete = _loadMetadataAssemblyWait && 0 == _loadMetadataAssetCount;
}
_resourceModule.UnloadAsset(textAsset);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 38e1de092fdb401c955859d7d60d1b5a
timeCreated: 1675668724

View File

@@ -0,0 +1,175 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using Launcher;
using TEngine;
using UnityEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
namespace Procedure
{
/// <summary>
/// 预加载流程
/// </summary>
public class ProcedurePreload : ProcedureBase
{
private float _progress = 0f;
private readonly Dictionary<string, bool> _loadedFlag = new Dictionary<string, bool>();
public override bool UseNativeDialog => true;
private readonly bool _needProLoadConfig = true;
private ProcedureOwner _procedureOwner;
/// <summary>
/// 预加载回调。
/// </summary>
private LoadAssetCallbacks m_PreLoadAssetCallbacks;
protected override void OnInit(ProcedureOwner procedureOwner)
{
base.OnInit(procedureOwner);
_procedureOwner = procedureOwner;
m_PreLoadAssetCallbacks = new LoadAssetCallbacks(OnPreLoadAssetSuccess, OnPreLoadAssetFailure);
}
protected override void OnEnter(ProcedureOwner procedureOwner)
{
base.OnEnter(procedureOwner);
_loadedFlag.Clear();
LauncherMgr.Show(UIDefine.UILoadUpdate, Utility.Text.Format(LoadText.Instance.Label_Load_Load_Progress, 0));
GameEvent.Send("UILoadUpdate.RefreshVersion");
PreloadResources();
}
protected override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);
var totalCount = _loadedFlag.Count <= 0 ? 1 : _loadedFlag.Count;
var loadCount = _loadedFlag.Count <= 0 ? 1 : 0;
foreach (KeyValuePair<string, bool> loadedFlag in _loadedFlag)
{
if (!loadedFlag.Value)
{
break;
}
else
{
loadCount++;
}
}
if (_loadedFlag.Count != 0)
{
LauncherMgr.Show(UIDefine.UILoadUpdate, Utility.Text.Format(LoadText.Instance.Label_Load_Load_Progress, (float)loadCount / totalCount * 100));
}
else
{
LauncherMgr.UpdateUIProgress(_progress);
string progressStr = $"{_progress * 100:f1}";
if (Math.Abs(_progress - 1f) < 0.001f)
{
LauncherMgr.Show(UIDefine.UILoadUpdate, LoadText.Instance.Label_Load_Load_Complete);
}
else
{
LauncherMgr.Show(UIDefine.UILoadUpdate, Utility.Text.Format(LoadText.Instance.Label_Load_Load_Progress, progressStr));
}
}
if (loadCount < totalCount)
{
return;
}
ChangeProcedureToLoadAssembly();
}
private async UniTaskVoid SmoothValue(float value, float duration, Action callback = null)
{
float time = 0f;
while (time < duration)
{
time += Time.deltaTime;
var result = Mathf.Lerp(0, value, time / duration);
_progress = result;
await UniTask.Yield();
}
_progress = value;
callback?.Invoke();
}
private void PreloadResources()
{
if (_needProLoadConfig)
{
LoadAllConfig();
}
}
private void LoadAllConfig()
{
if (_resourceModule.PlayMode == EPlayMode.EditorSimulateMode)
{
return;
}
AssetInfo[] assetInfos = _resourceModule.GetAssetInfos("PRELOAD");
foreach (var assetInfo in assetInfos)
{
PreLoad(assetInfo.Address);
}
#if UNITY_WEBGL
AssetInfo[] webAssetInfos = _resourceModule.GetAssetInfos("WEBGL_PRELOAD");
foreach (var assetInfo in webAssetInfos)
{
PreLoad(assetInfo.Address);
}
#endif
if (_loadedFlag.Count <= 0)
{
// SmoothValue(1, 1f, ChangeProcedureToLoadAssembly).Forget();
return;
}
}
private void PreLoad(string location)
{
_loadedFlag.Add(location, false);
_resourceModule.LoadAssetAsync(location, 100, m_PreLoadAssetCallbacks, null);
}
private void OnPreLoadAssetFailure(string assetName, LoadResourceStatus status, string errormessage, object userdata)
{
Log.Warning("Can not preload asset from '{0}' with error message '{1}'.", assetName, errormessage);
_loadedFlag[assetName] = true;
}
private void OnPreLoadAssetSuccess(string assetName, object asset, float duration, object userdata)
{
Log.Debug("Success preload asset from '{0}' duration '{1}'.", assetName, duration);
_loadedFlag[assetName] = true;
}
private void ChangeProcedureToLoadAssembly()
{
ChangeState<ProcedureLoadAssembly>(_procedureOwner);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3510902f458c4bd48422c40d10138549
timeCreated: 1665578910

View File

@@ -0,0 +1,22 @@
using UnityEngine;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
namespace Procedure
{
/// <summary>
/// 流程 => 闪屏。
/// </summary>
public class ProcedureSplash : ProcedureBase
{
public override bool UseNativeDialog => true;
protected override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);
// 播放 Splash 动画
//Splash.Active(splashTime:3f);
//初始化资源包
ChangeState<ProcedureInitPackage>(procedureOwner);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 139ec26f4972a8147a39d4bb34d5fcaa
timeCreated: 1528026159
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
using System;
using Cysharp.Threading.Tasks;
using Launcher;
using TEngine;
namespace Procedure
{
public class ProcedureStartGame : ProcedureBase
{
public override bool UseNativeDialog { get; }
protected override void OnEnter(IFsm<IProcedureModule> procedureOwner)
{
base.OnEnter(procedureOwner);
StartGame().Forget();
}
private async UniTaskVoid StartGame()
{
await UniTask.Yield();
LauncherMgr.HideAll();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: db61ddc0f5f34d639fae224363a3ac49
timeCreated: 1665628052