Compare commits

...

113 Commits

Author SHA1 Message Date
ALEX
b6fbdf1d22 Merge pull request #171 from Alex-Rachel/obfuz
support Obfuz
2025-07-30 23:14:10 +08:00
Alex-Rachel
e2de2ba994 Define Symbols for obfuz 2025-07-30 23:11:58 +08:00
Alex-Rachel
3b68cb1519 Update BattleMainUI.cs 2025-07-30 23:09:13 +08:00
Alex-Rachel
be12d1024a Update ScriptGenerator.cs 2025-07-30 23:09:03 +08:00
Alex-Rachel
55ffdf4edb Update GameApp.cs 2025-07-30 23:08:45 +08:00
Alex-Rachel
511619cb71 Update obfuz version 2025-07-30 23:00:47 +08:00
ALEX
5b8793de40 Merge pull request #169 from tpxxn/main
fix 打图集工具对有子图的图片只会打第一张子图的问题
2025-07-28 12:18:52 +08:00
tpxxn
9c9079c55b fix 打图集工具对有子图的图片只会打第一张子图的问题 2025-07-28 12:13:25 +08:00
Alex-Rachel
c8482799c5 接入obfuz->3.0 2025-07-26 18:16:16 +08:00
Alex-Rachel
cb86d8868e 接入obfuz->2.0 2025-07-26 08:10:41 +08:00
ALEX
f2c7ff4336 Merge pull request #165 from KMUS1997/patch-1
Update LauncherMgr.cs
2025-07-10 13:09:51 +08:00
KMUS1997
c18cdacdbb Update LauncherMgr.cs
// 解决秘之Bug,如果你旋转了UI相机会导致 界面也跟着旋转
2025-07-09 23:56:58 +08:00
Alex-Rachel
b18298cbd9 Update Hybridclr 7.6.0->8.3.0 2025-07-08 23:04:21 +08:00
Alex-Rachel
86b1ff5d7b Update YooAsset 2.3.8 -> 2.3.12 2025-07-08 23:02:24 +08:00
Alex-Rachel
71eb698038 Debugger Add ObjectPoolInfomationWindow 2025-07-07 19:33:52 +08:00
Alex-Rachel
57c08d4e92 Update ReleaseTools.cs 2025-07-07 12:05:07 +08:00
ALEX
45f5bf3dc7 Merge pull request #164 from Eatjune/Local
Update SceneModule.cs
2025-07-02 23:22:03 +08:00
Eatjune
7a86b80c2b Update SceneModule.cs
修复无法异步加载 挂起的场景
2025-07-02 20:12:26 +08:00
ALEX
07fb31cc1c Merge pull request #163 from XiaoBojun/TE_EGamePlay
Tween:新增MaterialColor
2025-06-28 00:17:09 +08:00
xiaobo
4a1f2b52f6 Tween:新增MaterialColor 2025-06-26 14:23:14 +08:00
Alex-Rachel
aee8a4a3dc Update GameEvent.cs 2025-06-18 23:20:04 +08:00
ALEX
7621ba4b4a Merge pull request #162 from XiaoBojun/main
一键打包ab包新增,UpdateSetting新增
2025-06-16 13:02:05 +08:00
xiaobo
53e16dc1ff Aot补充UnityEngine.CoreModule.dll 2025-06-16 12:47:48 +08:00
xiaobo
e26a2937a7 1:一键打包ab包:新增快捷键,暂定f8。自动生成dll,打包资源,自动将项目的StreamingAssets复制到打包后的StreamingAssets文件夹下
2:UpdateSetting
(1)新增webgl设置,选择访问远程还是StreamingAssets
(2)构建资源设置:是否自动复制资源包到打包地址;打包地址如:../../Builds/Unity_Data/StreamingAssets,支持绝对路径和相对路径
2025-06-16 11:39:50 +08:00
ALEX
e7344db57d Merge pull request #160 from SaberArtoriaFan/main
[Fix] 修复namespace 缺失
2025-06-11 18:46:20 +08:00
Saber
8ab792b778 [Fix] 修复namespace 缺失 2025-06-11 17:35:15 +08:00
ALEX
d6917f28b7 Merge pull request #159 from tpxxn/main
SetSpriteExtension 回调
2025-06-11 16:29:16 +08:00
tpxxn
9aef4d4081 SetSpriteExtension 回调 2025-06-11 11:16:25 +08:00
Alex-Rachel
7d1de6e9a3 Update AssetsReference.cs 2025-06-05 16:07:38 +08:00
ALEX
9e0e762a39 Merge pull request #158 from liang530/main
AssetsReference的自动销毁计数处理完善,兼容unity Instantiate和不可见对象
2025-06-05 16:06:11 +08:00
liang
ad2bde9259 AssetsReference的自动销毁计数处理完善,兼容unity Instantiate和不可见对象 2025-06-05 15:30:44 +08:00
Alex-Rachel
cb8f15ad88 #157 AssetsReference的自动销毁计数处理
#157 AssetsReference的自动销毁计数处理
2025-06-05 11:13:21 +08:00
ALEX
b4639fcbdc Update README.md 2025-06-03 11:11:08 +08:00
Alex-Rachel
cc9002fe9a F10快捷键 TEngine/查找资产引用
F10快捷键 TEngine/查找资产引用
2025-05-26 19:24:33 +08:00
ALEX
e64926a66a Merge pull request #155 from Molth/main
添加引用分析
2025-05-26 19:16:57 +08:00
Molth Nevin
32c1edd0a5 添加引用分析 2025-05-26 15:07:50 +08:00
ALEX
c6f1508e84 Merge pull request #154 from XiaoBojun/main
toolbar拓展布局自适应优化,以及场景切换分组:初始场景、默认包、其他场景。
2025-05-26 14:10:33 +08:00
XiaoBojun
1f129118a7 toolbar拓展布局自适应优化,以及场景切换分组:初始场景、默认包、其他场景。 2025-05-26 12:16:46 +08:00
ALEX
6dac7b44a6 Update README.md 2025-05-25 23:37:25 +08:00
ALEX
2e8d181bed Update README.md 2025-05-25 23:36:56 +08:00
Alex-Rachel
b42daa035e Update AssetsReference.cs 2025-05-20 18:00:49 +08:00
Alex-Rachel
e7b07dfea2 AssetsReference支持Unity实例化接口GameObject.Instantiate
AssetsReference支持Unity实例化接口GameObject.Instantiate
2025-05-20 12:54:15 +08:00
ALEX
62c05e7c2b Merge pull request #149 from 6ag/main
移除luban生成脚本在Mac环境中的无效命令
2025-05-19 22:46:52 +08:00
feng
4c4a882b21 移除luban生成脚本在Mac环境中的无效命令 2025-05-19 22:40:28 +08:00
Alex-Rachel
58d7847079 Update TEngine.Runtime.asmdef 2025-05-16 23:33:32 +08:00
Alex-Rachel
724f52bbab Update TEngine.Runtime.asmdef 2025-05-16 23:24:36 +08:00
Alex-Rachel
0b409436a0 一键打包需要支持SBP构建内置着色器资源包
一键打包需要支持SBP构建内置着色器资源包
2025-05-13 12:36:58 +08:00
Alex-Rachel
cc799ce426 Update YooAsset
Update YooAsset
2025-05-10 20:58:24 +08:00
Alex-Rachel
35a9729930 Update SceneSwitcher.cs.meta 2025-05-10 20:55:29 +08:00
Alex-Rachel
0e34c99398 Remove
Remove
2025-05-10 19:25:59 +08:00
Alex-Rachel
240da47fc3 移除LoadSubAssetsSync 2025-05-10 19:25:26 +08:00
ALEX
04109a43b6 Merge pull request #148 from tpxxn/main
fix ExternalTypeUtil namespace
2025-05-09 14:15:44 +08:00
tpxxn
bc82685ec7 fix ExternalTypeUtil namespace 2025-05-09 13:51:47 +08:00
Alex-Rachel
1894cd4d72 Extension for LoadSubSprite
Extension for LoadSubSprite
2025-05-07 12:53:27 +08:00
ALEX
4c903e8da5 Merge pull request #147 from tpxxn/main
refactor AtlasMakerEditor & luban gen code code.lineEnding=crlf &  luban Vector ExternalTypeUtil
2025-05-06 17:22:30 +08:00
tpxxn
03aabf6b08 luban Vector ExternalTypeUtil 2025-05-06 14:15:20 +08:00
tpxxn
c1d9fe5ca7 luban gen code code.lineEnding=crlf 2025-05-06 14:02:24 +08:00
tpxxn
122b5b27ad refactor AtlasMakerEditor 2025-05-06 13:58:02 +08:00
ALEX
9a8de43e95 Merge pull request #145 from lvshiling/main
Fix currentPath DirectorySeparatorChar
2025-05-03 12:13:31 +08:00
lvshiling
06d826d4b2 Fix currentPath DirectorySeparatorChar 2025-04-30 11:42:38 +08:00
ALEX
afd0c6a24e Merge pull request #143 from 6ag/main
修正UIWindow中用于记录UI隐藏的IsHide字段在HideUI时赋值又被覆盖的问题
2025-04-28 18:39:21 +08:00
ALEX
7f9dddaec0 Merge pull request #144 from tpxxn/main
fix GenerateAtlas -> LoadValidSprites
2025-04-28 18:39:09 +08:00
tpxxn
d97538481e fix GenerateAtlas -> LoadValidSprites 2025-04-28 16:14:05 +08:00
feng
c72ef8e197 修正UIWindow中用于记录UI隐藏的IsHide字段在HideUI时赋值又被覆盖的问题 2025-04-28 09:14:14 +08:00
ALEX
7c9393051c Merge pull request #142 from Vorik-S/main
UIModule关闭的倒计时应该先进行判断原计时器是否销毁,避免多次Hide导致计时器多次添加
2025-04-27 17:29:09 +08:00
沈挺
e4c3247546 UIModule关闭的倒计时应该先进行判断原计时器是否销毁,避免多次Hide导致计时器多次添加 2025-04-27 14:08:15 +08:00
Alex-Rachel
6e9d23b2dc WEBGL下的文件系统初始化修正 2025-04-22 16:43:06 +08:00
Alex-Rachel
eae827a600 WEBGL下的remoteServices初始化修正(资源路径走HostServer)
WEBGL下的remoteServices初始化修正(资源路径走HostServer)
2025-04-22 14:25:31 +08:00
Alex-Rachel
f916adb71b 更新YooAsset 2.3.7 -> 2.3.8
更新YooAsset 2.3.7 -> 2.3.8
2025-04-18 00:16:29 +08:00
Alex-Rachel
227283864f 更新YooAsset 2.3.3 -> 2.3.7 优化YooAsset.RuntimeExtension以及YooAsset.EditorExtension目录结构
更新YooAsset 2.3.3 -> 2.3.7 优化YooAsset.RuntimeExtension以及YooAsset.EditorExtension目录结构
2025-04-17 12:59:23 +08:00
Alex-Rachel
32418326b1 Update ProcedureInitResources.cs 2025-03-28 17:43:17 +08:00
Alex-Rachel
6570f87074 Create SceneSwitcher.cs.meta 2025-03-26 21:10:45 +08:00
ALEX
81b00ae913 Merge pull request #135 from XiaoBojun/main
修改工具栏切换场景布局
2025-03-26 20:58:56 +08:00
XiaoBojun
322aaa0a46 Merge branch 'main' of https://github.com/XiaoBojun/TEngine
# Conflicts:
#	UnityProject/Assets/Editor/ToolbarExtender/Custom/SceneSwitcher/Editor/SceneSwitcher.cs   resolved by main version
2025-03-26 00:30:17 +08:00
XiaoBojun
8f5b1b2117 修改工具栏切换场景布局 2025-03-26 00:23:18 +08:00
Alex-Rachel
54a4c55f3c Remove SceneSwitcher 2025-03-25 23:40:10 +08:00
Alex-Rachel
e37c066f9f 简化流程,支持非强制更新,移除冗余UpdateMainFest与UpdateVersion的流程,因为Yoo统一了编辑器单机和联网的流程了。
简化流程,支持非强制更新,移除冗余UpdateMainFest与UpdateVersion的流程,因为Yoo统一了编辑器单机和联网的流程了。
2025-03-25 19:10:39 +08:00
Alex-Rachel
b1d06ecef5 Update Singleton.cs 2025-03-25 19:09:01 +08:00
Alex-Rachel
27ce7e8ecc Update UpdateSettingEditor.cs 2025-03-25 16:39:14 +08:00
Alex-Rachel
8b715ffc8e 新增Setting面板设置
新增Setting面板设置
2025-03-24 19:11:51 +08:00
Alex-Rachel
e8fa910f1f Main logic assembly missing Tips. 2025-03-24 18:49:42 +08:00
Alex-Rachel
3b3bc7822a Update SceneModule.cs 2025-03-24 17:28:22 +08:00
Alex-Rachel
3c662e489e UpdateSetting Editor不限定路径
UpdateSetting Editor不限定路径
2025-03-24 09:26:26 +08:00
Alex-Rachel
5cebc82ba1 TweenHelper
TweenHelper
2025-03-23 21:06:05 +08:00
Alex-Rachel
5cb72e0b08 Update ResourceExtComponent.Resource.cs 2025-03-23 12:36:41 +08:00
Alex-Rachel
500e012f2e Update TEngine.Runtime.asmdef 2025-03-23 01:08:39 +08:00
Alex-Rachel
82332ba5b4 Utility.Unity.FindObjectOfType 2025-03-23 00:26:33 +08:00
Alex-Rachel
55a6ab1ffb Update Utility.Tween.cs 2025-03-23 00:18:04 +08:00
Alex-Rachel
24313b61ab Utility.Unity Unity6下过时API修正 2025-03-21 20:26:24 +08:00
Alex-Rachel
876b36b3b9 LocalizationTools支持Unity6
LocalizationTools支持Unity6
2025-03-21 20:26:03 +08:00
Alex-Rachel
2a62a1f320 AOT补充元数据默认增加UniTask与YooAsset 2025-03-21 13:20:04 +08:00
Alex-Rachel
b6e6c1c423 AOT补充元数据默认增加UniTask与YooAsset
AOT补充元数据默认增加UniTask与YooAsset
2025-03-21 13:19:27 +08:00
Alex-Rachel
230f6a0be5 Update UpdateSettingEditor.cs 2025-03-21 13:18:56 +08:00
Alex-Rachel
25ca5f3f2b Update BuildDLLCommand.cs 2025-03-21 13:18:51 +08:00
Alex-Rachel
66c5a41be5 增加ENABLE_HYBRIDCLR的宏支持
增加ENABLE_HYBRIDCLR的宏支持
2025-03-21 10:26:12 +08:00
ALEX
61ba472346 Merge pull request #130 from Wkeasy/main
添加重启流程接口
2025-03-21 00:07:49 +08:00
Alex-Rachel
9ce4fb9d88 默认不校验CRC,CRC影响AB加载的速度 2025-03-20 15:12:19 +08:00
Wkeasy
e405236d4d 添加重启流程接口 2025-03-20 14:54:15 +08:00
Alex-Rachel
2e27941755 同步GameApp、Procedure里简化IUpdateDriver的调用 2025-03-20 11:53:35 +08:00
Alex-Rachel
14bbb993f8 YooAsset Extension程序集 因为MIniGame的FileSystem是写死了这个程序集
YooAsset Extension程序集 因为MIniGame的FileSystem是写死了这个程序集
2025-03-20 10:29:49 +08:00
ALEX
0ba9d1a8b7 Merge pull request #129 from 6ag/main
UI脚本生成,区分UIWidget和UIWindow
2025-03-20 00:06:31 +08:00
feng
2818edb575 UI脚本生成,区分UIWidget和UIWindow 2025-03-19 22:04:10 +08:00
Alex-Rachel
5210c1d36b ToolbarExtender
ToolbarExtender
2025-03-19 21:12:28 +08:00
Alex-Rachel
cdc7a8f688 Update SourceGenerator 忽略除了默认程序集的生成
Update SourceGenerator 忽略除了默认程序集的生成
2025-03-19 20:18:09 +08:00
Alex-Rachel
c20f8285d8 Update ResourceModule.Services.cs 2025-03-19 12:55:52 +08:00
Alex-Rachel
f2a63978da 增加MiniGameFileSystem的支持,增加EncryptType的解密枚举类型,以及ResourceModule解密方法实现
增加MiniGameFileSystem的支持,增加EncryptType的解密枚举类型,以及ResourceModule解密方法实现
2025-03-19 12:40:13 +08:00
Alex-Rachel
36db2f3aba 增加UnLoadAsync, 调整LoadSceneAsync的命名,增加_handlingScene当前正在处理的Scene保护
增加UnLoadAsync, 调整LoadSceneAsync的命名,增加_handlingScene当前正在处理的Scene保护
2025-03-19 09:30:27 +08:00
Alex-Rachel
61b2baeaf7 LocalizationUtility 命名规范 2025-03-18 22:34:43 +08:00
ALEX
7aab91215c Merge pull request #128 from 6ag/main
脚本自动生成增加Toggle
2025-03-18 19:31:55 +08:00
feng
4ae417337b 脚本自动生成增加Toggle 2025-03-18 18:56:37 +08:00
Alex-Rachel
52ac77d5e4 SceneModule增加UniTask的LoadScene方法 2025-03-18 14:39:08 +08:00
Alex-Rachel
515d55e19a Update UpdateSettingEditor.cs 2025-03-18 14:37:24 +08:00
Alex-Rachel
a4221e36f8 HotUpdateAssemblies 剔除.dll后缀
HotUpdateAssemblies 剔除.dll后缀
2025-03-18 13:46:32 +08:00
721 changed files with 39559 additions and 2472 deletions

View File

@@ -0,0 +1,29 @@
using UnityEngine;
public static class ExternalTypeUtil
{
public static Vector2 NewVector2(GameConfig.vector2 v)
{
return new Vector2(v.X, v.Y);
}
public static Vector3 NewVector3(GameConfig.vector3 v)
{
return new Vector3(v.X, v.Y, v.Z);
}
public static Vector4 NewVector4(GameConfig.vector4 v)
{
return new Vector4(v.X, v.Y, v.Z, v.W);
}
public static Vector2Int NewVector2Int(GameConfig.vector2int v)
{
return new Vector2Int(v.X, v.Y);
}
public static Vector3Int NewVector3Int(GameConfig.vector3int v)
{
return new Vector3Int(v.X, v.Y, v.Z);
}
}

View File

@@ -1,17 +1,46 @@
<module name="">
<bean name="vector2" valueType="1" sep=",">
<var name="x" type="float"/>
<var name="y" type="float"/>
<var name="x" type="float" />
<var name="y" type="float" />
<mapper target="client" codeTarget="cs-bin,cs-simple-json,cs-newtonsoft-json">
<option name="type" value="UnityEngine.Vector2" />
<option name="constructor" value="ExternalTypeUtil.NewVector2" />
</mapper>
</bean>
<bean name="vector3" valueType="1" sep=",">
<var name="x" type="float"/>
<var name="y" type="float"/>
<var name="z" type="float"/>
<var name="x" type="float" />
<var name="y" type="float" />
<var name="z" type="float" />
<mapper target="client" codeTarget="cs-bin,cs-simple-json,cs-newtonsoft-json">
<option name="type" value="UnityEngine.Vector3" />
<option name="constructor" value="ExternalTypeUtil.NewVector3" />
</mapper>
</bean>
<bean name="vector4" valueType="1" sep=",">
<var name="x" type="float"/>
<var name="y" type="float"/>
<var name="z" type="float"/>
<var name="w" type="float"/>
<var name="x" type="float" />
<var name="y" type="float" />
<var name="z" type="float" />
<var name="w" type="float" />
<mapper target="client" codeTarget="cs-bin,cs-simple-json,cs-newtonsoft-json">
<option name="type" value="UnityEngine.Vector4" />
<option name="constructor" value="ExternalTypeUtil.NewVector4" />
</mapper>
</bean>
<bean name="vector2int" valueType="1" sep=",">
<var name="x" type="int" />
<var name="y" type="int" />
<mapper target="client" codeTarget="cs-bin,cs-simple-json,cs-newtonsoft-json">
<option name="type" value="UnityEngine.Vector2Int" />
<option name="constructor" value="ExternalTypeUtil.NewVector2Int" />
</mapper>
</bean>
<bean name="vector3int" valueType="1" sep=",">
<var name="x" type="int" />
<var name="y" type="int" />
<var name="z" type="int" />
<mapper target="client" codeTarget="cs-bin,cs-simple-json,cs-newtonsoft-json">
<option name="type" value="UnityEngine.Vector3" />
<option name="constructor" value="ExternalTypeUtil.NewVector3" />
</mapper>
</bean>
</module>

View File

@@ -8,12 +8,14 @@ set DATA_OUTPATH=%WORKSPACE%/UnityProject/Assets/AssetRaw/Configs/bytes/
set CODE_OUTPATH=%WORKSPACE%/UnityProject/Assets/GameScripts/HotFix/GameProto/GameConfig/
xcopy /s /e /i /y "%CONF_ROOT%\CustomTemplate\ConfigSystem.cs" "%WORKSPACE%\UnityProject\Assets\GameScripts\HotFix\GameProto\ConfigSystem.cs"
xcopy /s /e /i /y "%CONF_ROOT%\CustomTemplate\ExternalTypeUtil.cs" "%WORKSPACE%\UnityProject\Assets\GameScripts\HotFix\GameProto\ExternalTypeUtil.cs"
dotnet %LUBAN_DLL% ^
-t client ^
-c cs-bin ^
-d bin^
--conf %CONF_ROOT%\luban.conf ^
-x code.lineEnding=crlf ^
-x outputCodeDir=%CODE_OUTPATH% ^
-x outputDataDir=%DATA_OUTPATH%
pause

View File

@@ -11,14 +11,14 @@ export CODE_OUTPATH="${WORKSPACE}/UnityProject/Assets/GameScripts/HotFix/GamePro
cp -R "${CONF_ROOT}/CustomTemplate/ConfigSystem.cs" \
"${WORKSPACE}/UnityProject/Assets/GameScripts/HotFix/GameProto/ConfigSystem.cs"
cp -R "${CONF_ROOT}/CustomTemplate/ExternalTypeUtil.cs" \
"${WORKSPACE}/UnityProject/Assets/GameScripts/HotFix/GameProto/ExternalTypeUtil.cs"
dotnet "${LUBAN_DLL}" \
-t client \
-c cs-bin \
-d bin \
--conf "${CONF_ROOT}/luban.conf" \
-x code.lineEnding=crlf \
-x outputCodeDir="${CODE_OUTPATH}" \
-x outputDataDir="${DATA_OUTPATH}"
echo "操作完成,按任意键退出..."
read -k1

View File

@@ -8,6 +8,7 @@ set DATA_OUTPATH=%WORKSPACE%/UnityProject/Assets/AssetRaw/Configs/bytes/
set CODE_OUTPATH=%WORKSPACE%/UnityProject/Assets/GameScripts/HotFix/GameProto/GameConfig/
xcopy /s /e /i /y "%CONF_ROOT%\CustomTemplate\ConfigSystem.cs" "%WORKSPACE%\UnityProject\Assets\GameScripts\HotFix\GameProto\ConfigSystem.cs"
xcopy /s /e /i /y "%CONF_ROOT%\CustomTemplate\ExternalTypeUtil.cs" "%WORKSPACE%\UnityProject\Assets\GameScripts\HotFix\GameProto\ExternalTypeUtil.cs"
dotnet %LUBAN_DLL% ^
-t client ^
@@ -15,6 +16,7 @@ dotnet %LUBAN_DLL% ^
-d bin^
--conf %CONF_ROOT%\luban.conf ^
--customTemplateDir %CONF_ROOT%\CustomTemplate\CustomTemplate_Client_LazyLoad ^
-x code.lineEnding=crlf ^
-x outputCodeDir=%CODE_OUTPATH% ^
-x outputDataDir=%DATA_OUTPATH%
pause

View File

@@ -11,6 +11,8 @@ export CODE_OUTPATH="${WORKSPACE}/UnityProject/Assets/GameScripts/HotFix/GamePro
cp -R "${CONF_ROOT}/CustomTemplate/ConfigSystem.cs" \
"${WORKSPACE}/UnityProject/Assets/GameScripts/HotFix/GameProto/ConfigSystem.cs"
cp -R "${CONF_ROOT}/CustomTemplate/ExternalTypeUtil.cs" \
"${WORKSPACE}/UnityProject/Assets/GameScripts/HotFix/GameProto/ExternalTypeUtil.cs"
dotnet "${LUBAN_DLL}" \
-t client \
@@ -18,8 +20,6 @@ dotnet "${LUBAN_DLL}" \
-d bin \
--conf "${CONF_ROOT}/luban.conf" \
--customTemplateDir "${CONF_ROOT}/CustomTemplate/CustomTemplate_Client_LazyLoad" \
-x code.lineEnding=crlf \
-x outputCodeDir="${CODE_OUTPATH}" \
-x outputDataDir="${DATA_OUTPATH}"
echo "操作完成,按任意键退出..."
read -k1

View File

@@ -12,6 +12,7 @@ dotnet %LUBAN_DLL% ^
-c cs-bin ^
-d bin^
--conf %CONF_ROOT%\luban.conf ^
-x code.lineEnding=crlf ^
-x outputCodeDir=%CODE_OUTPATH% ^
-x outputDataDir=%DATA_OUTPATH%
pause

View File

@@ -14,8 +14,6 @@ dotnet "${LUBAN_DLL}" \
-c cs-bin \
-d bin \
--conf "${CONF_ROOT}/luban.conf" \
-x code.lineEnding=crlf \
-x outputCodeDir="${CODE_OUTPATH}" \
-x outputDataDir="${DATA_OUTPATH}"
echo "操作完成,按任意键退出..."
read -k1

View File

@@ -23,12 +23,14 @@
<a style="text-decoration:none">
<img src="https://img.shields.io/github/languages/top/ALEXTANGXIAO/TEngine" alt="topLanguage" />
</a>
<a href="https://deepwiki.com/Alex-Rachel/TEngine" style="text-decoration:none">
<img src="https://deepwiki.com/badge.svg" alt="DeepWiki" />
</a>
<br>
<br>
</p>
# <strong>TEngine
#### TEngine是一个简单(新手友好开箱即用)且强大的Unity框架全平台解决方案,对于需要一套上手快、文档清晰、高性能且可拓展性极强的商业级解决方案的开发者或者团队来说是一个很好的选择。
@@ -100,6 +102,8 @@ TEngine
#### <a href="https://github.com/ALEXTANGXIAO/GameNetty"><strong>GameNetty</strong></a> - GameNetty是一套源于ETServer首次拆分最新的ET8.1的前后端解决方案客户端最精简大约750k完美做成包的形式几乎零成本 无侵入的嵌入进你的框架。
#### <a href="https://github.com/Herta-Space-Station/FixedPoint"><strong>FixedPoint</strong></a> - 性能极限的定点数学库FixedPoint。
## <strong>Buy me a 奶茶.

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 075130c21aee41b49b3977adc2bfa288
guid: e11095ce128a7214a88c291ea368df9a
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -0,0 +1,174 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
namespace TEngine.Editor
{
internal sealed class AssetTreeView : TreeView
{
private const float K_ICON_WIDTH = 18f;
private const float K_ROW_HEIGHTS = 20f;
private readonly GUIStyle _stateGuiStyle = new GUIStyle { richText = true, alignment = TextAnchor.MiddleCenter };
public AssetViewItem assetRoot;
public AssetTreeView(TreeViewState state, MultiColumnHeader multicolumnHeader) : base(state, multicolumnHeader)
{
rowHeight = K_ROW_HEIGHTS;
columnIndexForTreeFoldouts = 0;
showAlternatingRowBackgrounds = true;
showBorder = false;
customFoldoutYOffset = (K_ROW_HEIGHTS - EditorGUIUtility.singleLineHeight) * 0.5f;
extraSpaceBeforeIconAndLabel = K_ICON_WIDTH;
}
protected override void DoubleClickedItem(int id)
{
AssetViewItem item = (AssetViewItem)FindItem(id, rootItem);
if (item != null)
{
Object assetObject = AssetDatabase.LoadAssetAtPath(item.data.path, typeof(Object));
EditorUtility.FocusProjectWindow();
Selection.activeObject = assetObject;
EditorGUIUtility.PingObject(assetObject);
}
}
protected override void ExpandedStateChanged() => SortExpandItem();
public void SortExpandItem()
{
if (SortHelper.CurSortType == SortType.None) return;
IList<int> expandItemList = GetExpanded();
foreach (int i in expandItemList)
{
AssetViewItem item = (AssetViewItem)FindItem(i, rootItem);
SortHelper.SortChild(item.data);
}
ResourceReferenceInfo curWindow = EditorWindow.GetWindow<ResourceReferenceInfo>();
curWindow.needUpdateAssetTree = true;
}
public static MultiColumnHeaderState CreateDefaultMultiColumnHeaderState(float treeViewWidth, bool isDepend)
{
List<MultiColumnHeaderState.Column> columns = new List<MultiColumnHeaderState.Column>
{
new MultiColumnHeaderState.Column
{
headerContent = new GUIContent("名称"),
headerTextAlignment = TextAlignment.Center,
sortedAscending = false,
width = 200,
minWidth = 60,
autoResize = false,
allowToggleVisibility = false,
canSort = true,
sortingArrowAlignment = TextAlignment.Center
},
new MultiColumnHeaderState.Column
{
headerContent = new GUIContent("路径"),
headerTextAlignment = TextAlignment.Center,
sortedAscending = false,
width = 360,
minWidth = 60,
autoResize = false,
allowToggleVisibility = false,
canSort = true,
sortingArrowAlignment = TextAlignment.Center
},
new MultiColumnHeaderState.Column
{
headerContent = new GUIContent("状态"),
headerTextAlignment = TextAlignment.Center,
sortedAscending = false,
width = 60,
minWidth = 60,
autoResize = false,
allowToggleVisibility = true,
canSort = false
}
};
if (!isDepend)
{
columns.Add(new MultiColumnHeaderState.Column
{
headerContent = new GUIContent("引用数量"),
headerTextAlignment = TextAlignment.Center,
sortedAscending = false,
width = 60,
minWidth = 60,
autoResize = true,
allowToggleVisibility = true,
canSort = false
});
}
MultiColumnHeaderState state = new MultiColumnHeaderState(columns.ToArray());
return state;
}
protected override TreeViewItem BuildRoot() => assetRoot;
protected override void RowGUI(RowGUIArgs args)
{
AssetViewItem item = (AssetViewItem)args.item;
for (int i = 0; i < args.GetNumVisibleColumns(); ++i)
CellGUI(args.GetCellRect(i), item, (MyColumns)args.GetColumn(i), ref args);
}
private void CellGUI(Rect cellRect, AssetViewItem item, MyColumns column, ref RowGUIArgs args)
{
CenterRectUsingSingleLineHeight(ref cellRect);
switch (column)
{
case MyColumns.Name:
Rect iconRect = cellRect;
iconRect.x += GetContentIndent(item);
iconRect.width = K_ICON_WIDTH;
if (iconRect.x < cellRect.xMax)
{
Texture2D icon = GetIcon(item.data.path);
if (icon != null)
GUI.DrawTexture(iconRect, icon, ScaleMode.ScaleToFit);
}
args.rowRect = cellRect;
base.RowGUI(args);
break;
case MyColumns.Path:
GUI.Label(cellRect, item.data.path);
break;
case MyColumns.State:
GUI.Label(cellRect, ReferenceFinderData.GetInfoByState(item.data.state), _stateGuiStyle);
break;
case MyColumns.RefCount:
GUI.Label(cellRect, ResourceReferenceInfo.Data.GetRefCount(item.data, (item.parent as AssetViewItem)?.data), _stateGuiStyle);
break;
}
}
private Texture2D GetIcon(string path)
{
Object obj = AssetDatabase.LoadAssetAtPath(path, typeof(Object));
if (obj)
{
Texture2D icon = AssetPreview.GetMiniThumbnail(obj);
if (!icon)
icon = AssetPreview.GetMiniTypeThumbnail(obj.GetType());
return icon;
}
return null;
}
private enum MyColumns
{
Name,
Path,
State,
RefCount
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 56ea224b45d314e4a86b558404e9b6c8
guid: dd079ed03e211ca4698770fc925dcf0a
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,9 @@
using UnityEditor.IMGUI.Controls;
namespace TEngine.Editor
{
internal sealed class AssetViewItem : TreeViewItem
{
public ReferenceFinderData.AssetDescription data;
}
}

View File

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

View File

@@ -0,0 +1,34 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
namespace TEngine.Editor
{
internal sealed class ClickColumn : MultiColumnHeader
{
public delegate void SortInColumn();
public static Dictionary<int, SortInColumn> SortWithIndex = new Dictionary<int, SortInColumn>
{
{ 0, SortByName },
{ 1, SortByPath }
};
public ClickColumn(MultiColumnHeaderState state) : base(state) => canSort = true;
protected override void ColumnHeaderClicked(MultiColumnHeaderState.Column column, int columnIndex)
{
base.ColumnHeaderClicked(column, columnIndex);
if (SortWithIndex.ContainsKey(columnIndex))
{
SortWithIndex[columnIndex].Invoke();
ResourceReferenceInfo curWindow = EditorWindow.GetWindow<ResourceReferenceInfo>();
curWindow.mAssetTreeView.SortExpandItem();
}
}
public static void SortByName() => SortHelper.SortByName();
public static void SortByPath() => SortHelper.SortByPath();
}
}

View File

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

View File

@@ -0,0 +1,29 @@
using UnityEditor;
using UnityEngine;
namespace TEngine.Editor
{
internal sealed class DragAreaGetObject
{
public static Object[] GetObjects(string meg = null)
{
Event aEvent = Event.current;
GUI.contentColor = Color.white;
if (aEvent.type is EventType.DragUpdated or EventType.DragPerform)
{
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
bool needReturn = false;
if (aEvent.type == EventType.DragPerform)
{
DragAndDrop.AcceptDrag();
needReturn = true;
}
Event.current.Use();
if (needReturn) return DragAndDrop.objectReferences;
}
return null;
}
}
}

View File

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

View File

@@ -0,0 +1,9 @@
namespace TEngine.Editor
{
internal sealed class ListInfo
{
public int Count;
public string Name;
public string Type;
}
}

View File

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

View File

@@ -0,0 +1,417 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using UnityEditor;
using UnityEngine;
// ReSharper disable InconsistentNaming
namespace TEngine.Editor
{
internal sealed class ReferenceFinderData
{
public enum AssetState : byte
{
Normal,
Changed,
Missing,
Invalid
}
private const string CachePath = "Library/ReferenceFinderCache";
public const int MinThreadCount = 8;
private const int SingleThreadReadCount = 100;
private static readonly int ThreadCount = Math.Max(MinThreadCount, Environment.ProcessorCount);
private static string _basePath;
private static readonly HashSet<string> FileExtension = new HashSet<string>
{
".prefab",
".unity",
".mat",
".asset",
".anim",
".controller"
};
private static readonly Regex GuidRegex = new Regex("guid: ([a-z0-9]{32})", RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Dictionary<(AssetDescription, AssetDescription), int> _dictCache = new Dictionary<(AssetDescription, AssetDescription), int>();
private readonly List<Dictionary<string, AssetDescription>> _threadAssetDict = new List<Dictionary<string, AssetDescription>>();
private readonly List<Thread> _threadList = new List<Thread>();
private int _curReadAssetCount;
private int _totalCount;
public string[] allAssets;
public Dictionary<string, AssetDescription> assetDict = new Dictionary<string, AssetDescription>();
public void CollectDependenciesInfo()
{
try
{
_basePath = Application.dataPath.Replace("/Assets", "");
ReadFromCache();
allAssets = AssetDatabase.GetAllAssetPaths();
_totalCount = allAssets.Length;
_threadList.Clear();
_curReadAssetCount = 0;
foreach (Dictionary<string, AssetDescription> i in _threadAssetDict)
i.Clear();
_threadAssetDict.Clear();
for (int i = 0; i < ThreadCount; i++) _threadAssetDict.Add(new Dictionary<string, AssetDescription>());
bool allThreadFinish = false;
for (int i = 0; i < ThreadCount; i++)
{
ThreadStart method = ReadAssetInfo;
Thread readThread = new Thread(method);
_threadList.Add(readThread);
readThread.Start();
}
while (!allThreadFinish)
{
if (_curReadAssetCount % 500 == 0 &&
EditorUtility.DisplayCancelableProgressBar("Updating", $"Handle {_curReadAssetCount}", (float)_curReadAssetCount / _totalCount))
{
EditorUtility.ClearProgressBar();
foreach (Thread i in _threadList)
i.Abort();
return;
}
allThreadFinish = true;
foreach (Thread i in _threadList)
{
if (i.IsAlive)
{
allThreadFinish = false;
break;
}
}
}
foreach (Dictionary<string, AssetDescription> dict in _threadAssetDict)
{
foreach (KeyValuePair<string, AssetDescription> j in dict)
assetDict[j.Key] = j.Value;
}
EditorUtility.DisplayCancelableProgressBar("Updating", "Write cache", 1f);
WriteToChache();
EditorUtility.DisplayCancelableProgressBar("Updating", "Generate reference data", 1f);
UpdateResourceReferenceInfo();
EditorUtility.ClearProgressBar();
}
catch (Exception e)
{
Debug.LogError(e);
EditorUtility.ClearProgressBar();
}
}
public void ReadAssetInfo()
{
int index = Thread.CurrentThread.ManagedThreadId % ThreadCount;
int intervalLength = _totalCount / ThreadCount;
int start = intervalLength * index;
int end = start + intervalLength;
if (_totalCount - end < intervalLength)
end = _totalCount;
int readAssetCount = 0;
for (int i = start; i < end; i++)
{
if (readAssetCount % SingleThreadReadCount == 0)
{
_curReadAssetCount += readAssetCount;
readAssetCount = 0;
}
GetAsset(_basePath, allAssets[i]);
readAssetCount++;
}
}
public void GetAsset(string dataPath, string assetPath)
{
string extLowerStr = Path.GetExtension(assetPath).ToLower();
bool needReadFile = FileExtension.Contains(extLowerStr);
string fileName = $"{dataPath}/{assetPath}";
string metaFile = $"{dataPath}/{assetPath}.meta";
if (File.Exists(fileName) && File.Exists(metaFile))
{
string metaText = File.ReadAllText(metaFile, Encoding.UTF8);
MatchCollection matchRs = GuidRegex.Matches(metaText);
string selfGuid = matchRs[0].Groups[1].Value.ToLower();
string lastModifyTime = File.GetLastWriteTime(fileName).ToString(CultureInfo.InvariantCulture);
MatchCollection guids = null;
List<string> depend = new List<string>();
if (needReadFile)
{
string fileStr = File.ReadAllText(fileName, Encoding.UTF8);
guids = GuidRegex.Matches(fileStr);
}
int curListIndex = Thread.CurrentThread.ManagedThreadId % ThreadCount;
Dictionary<string, AssetDescription> curDict = _threadAssetDict[curListIndex];
if (!curDict.ContainsKey(selfGuid) || curDict[selfGuid].assetDependencyHashString != lastModifyTime)
{
if (guids != null)
{
for (int index = 0; index < guids.Count; ++index)
{
Match i = guids[index];
depend.Add(i.Groups[1].Value.ToLower());
}
}
AssetDescription ad = new AssetDescription
{
name = Path.GetFileNameWithoutExtension(assetPath),
path = assetPath,
assetDependencyHashString = lastModifyTime,
dependencies = depend
};
if (_threadAssetDict[curListIndex].ContainsKey(selfGuid))
_threadAssetDict[curListIndex][selfGuid] = ad;
else
_threadAssetDict[curListIndex].Add(selfGuid, ad);
}
}
}
private void UpdateResourceReferenceInfo()
{
foreach (KeyValuePair<string, AssetDescription> asset in assetDict)
{
foreach (string assetGuid in asset.Value.dependencies)
{
if (assetDict.ContainsKey(assetGuid))
assetDict[assetGuid].references.Add(asset.Key);
}
}
}
public bool ReadFromCache()
{
assetDict.Clear();
ClearCache();
if (File.Exists(CachePath))
{
List<string> serializedGuid;
List<string> serializedDependencyHash;
List<int[]> serializedDenpendencies;
using (FileStream fs = File.OpenRead(CachePath))
{
BinaryFormatter bf = new BinaryFormatter();
if (EditorUtility.DisplayCancelableProgressBar("Import Cache", "Reading Cache", 0))
{
EditorUtility.ClearProgressBar();
return false;
}
serializedGuid = (List<string>)bf.Deserialize(fs);
serializedDependencyHash = (List<string>)bf.Deserialize(fs);
serializedDenpendencies = (List<int[]>)bf.Deserialize(fs);
EditorUtility.ClearProgressBar();
}
for (int i = 0; i < serializedGuid.Count; ++i)
{
string path = AssetDatabase.GUIDToAssetPath(serializedGuid[i]);
if (string.IsNullOrEmpty(path))
{
AssetDescription ad = new AssetDescription
{
name = Path.GetFileNameWithoutExtension(path),
path = path,
assetDependencyHashString = serializedDependencyHash[i]
};
assetDict.Add(serializedGuid[i], ad);
}
}
for (int i = 0; i < serializedGuid.Count; ++i)
{
string guid = serializedGuid[i];
if (assetDict.ContainsKey(guid))
{
List<string> guids = new List<string>();
foreach (int index in serializedDenpendencies[i])
{
string g = serializedGuid[index];
if (assetDict.ContainsKey(g))
guids.Add(g);
}
assetDict[guid].dependencies = guids;
}
}
UpdateResourceReferenceInfo();
return true;
}
return false;
}
private void WriteToChache()
{
if (File.Exists(CachePath))
File.Delete(CachePath);
List<string> serializedGuid = new List<string>();
List<string> serializedDependencyHash = new List<string>();
List<int[]> serializedDenpendencies = new List<int[]>();
Dictionary<string, int> guidIndex = new Dictionary<string, int>();
using FileStream fs = File.OpenWrite(CachePath);
foreach (KeyValuePair<string, AssetDescription> pair in assetDict)
{
guidIndex.Add(pair.Key, guidIndex.Count);
serializedGuid.Add(pair.Key);
serializedDependencyHash.Add(pair.Value.assetDependencyHashString);
}
foreach (string guid in serializedGuid)
{
List<int> res = new List<int>();
foreach (string i in assetDict[guid].dependencies)
{
if (guidIndex.TryGetValue(i, out var value))
res.Add(value);
}
int[] indexes = res.ToArray();
serializedDenpendencies.Add(indexes);
}
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, serializedGuid);
bf.Serialize(fs, serializedDependencyHash);
bf.Serialize(fs, serializedDenpendencies);
}
public void UpdateAssetState(string guid)
{
if (assetDict.TryGetValue(guid, out AssetDescription ad) && ad.state != AssetState.Invalid)
{
if (File.Exists(ad.path))
ad.state = ad.assetDependencyHashString != File.GetLastWriteTime(ad.path).ToString(CultureInfo.InvariantCulture) ? AssetState.Changed : AssetState.Normal;
else
ad.state = AssetState.Missing;
}
else if (!assetDict.TryGetValue(guid, out ad))
{
string path = AssetDatabase.GUIDToAssetPath(guid);
ad = new AssetDescription
{
name = Path.GetFileNameWithoutExtension(path),
path = path,
state = AssetState.Invalid
};
assetDict.Add(guid, ad);
}
}
public static string GetInfoByState(AssetState state)
{
if (state == AssetState.Changed)
return "<color=red>缓存不匹配</color>";
if (state == AssetState.Missing)
return "<color=red>缓存丢失</color>";
if (state == AssetState.Invalid)
return "<color=yellow>缓存无效</color>";
return "<color=green>缓存正常</color>";
}
private int GetRefCount(string assetGUID, AssetDescription desc, List<string> guidStack)
{
if (guidStack.Contains(assetGUID))
{
Debug.Log("有循环引用, 计数可能不准确");
return 0;
}
guidStack.Add(assetGUID);
int total = 0;
if (assetDict.TryGetValue(assetGUID, out AssetDescription value))
{
if (value.references.Count > 0)
{
Dictionary<string, int> cachedRefCount = new Dictionary<string, int>();
foreach (string refs in value.references)
{
if (!cachedRefCount.ContainsKey(refs))
{
int refCount = GetRefCount(refs, value, guidStack);
cachedRefCount[refs] = refCount;
total += refCount;
}
}
}
else
{
total = 0;
if (desc != null)
{
string guid = AssetDatabase.AssetPathToGUID(desc.path);
foreach (string deps in value.dependencies)
{
if (guid == deps)
total++;
}
}
}
}
guidStack.RemoveAt(guidStack.Count - 1);
return total;
}
public void ClearCache() => _dictCache.Clear();
public string GetRefCount(AssetDescription desc, AssetDescription parentDesc)
{
if (_dictCache.TryGetValue((desc, parentDesc), out int total))
return total.ToString();
string rootGUID = AssetDatabase.AssetPathToGUID(desc.path);
List<string> guidInStack = new List<string> { rootGUID };
Dictionary<string, int> cachedRefCount = new Dictionary<string, int>();
foreach (string refs in desc.references)
{
if (!cachedRefCount.ContainsKey(refs))
{
int refCount = GetRefCount(refs, desc, guidInStack);
cachedRefCount[refs] = refCount;
total += refCount;
}
}
if (desc.references.Count == 0 && parentDesc != null)
{
string guid = AssetDatabase.AssetPathToGUID(desc.path);
foreach (string refs in parentDesc.references)
{
if (refs == guid)
total++;
}
}
guidInStack.RemoveAt(guidInStack.Count - 1);
_dictCache.Add((desc, parentDesc), total);
return total.ToString();
}
internal sealed class AssetDescription
{
public string assetDependencyHashString;
public List<string> dependencies = new List<string>();
public string name = "";
public string path = "";
public List<string> references = new List<string>();
public AssetState state = AssetState.Normal;
}
}
}

View File

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

View File

@@ -0,0 +1,307 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
namespace TEngine.Editor
{
internal sealed class ResourceReferenceInfo : EditorWindow
{
private const string IS_DEPEND_PREF_KEY = "ReferenceFinderData_IsDepend";
public static readonly ReferenceFinderData Data = new ReferenceFinderData();
private static bool _initializedData;
[SerializeField]
private TreeViewState _treeViewState;
public bool needUpdateAssetTree;
public bool needUpdateState = true;
public List<string> selectedAssetGuid = new List<string>();
private readonly HashSet<string> _brotherAssetIsAdd = new HashSet<string>();
private readonly HashSet<string> _parentAssetIsAdd = new HashSet<string>();
private readonly HashSet<string> _updatedAssetSet = new HashSet<string>();
private Dictionary<string, ListInfo> _artInfo = new Dictionary<string, ListInfo>();
private bool _initializedGUIStyle;
private bool _isDepend;
private GUIStyle _toolbarButtonGUIStyle;
private GUIStyle _toolbarGUIStyle;
public AssetTreeView mAssetTreeView;
private void OnEnable() => _isDepend = PlayerPrefs.GetInt(IS_DEPEND_PREF_KEY, 0) == 1;
private void OnGUI()
{
UpdateDragAssets();
InitGUIStyleIfNeeded();
DrawOptionBar();
UpdateAssetTree();
mAssetTreeView?.OnGUI(new Rect(0, _toolbarGUIStyle.fixedHeight, position.width, position.height - _toolbarGUIStyle.fixedHeight));
}
[MenuItem("TEngine/查找资产引用 _F10", false, 100)]
public static void FindRef()
{
InitDataIfNeeded();
OpenWindow();
ResourceReferenceInfo window = GetWindow<ResourceReferenceInfo>();
window.UpdateSelectedAssets();
}
private static void OpenWindow()
{
ResourceReferenceInfo window = GetWindow<ResourceReferenceInfo>();
window.wantsMouseMove = false;
window.titleContent = new GUIContent("查找资产引用");
window.Show();
window.Focus();
SortHelper.Init();
}
private static void InitDataIfNeeded()
{
if (!_initializedData)
{
if (!Data.ReadFromCache())
Data.CollectDependenciesInfo();
_initializedData = true;
}
}
private void InitGUIStyleIfNeeded()
{
if (!_initializedGUIStyle)
{
_toolbarButtonGUIStyle = new GUIStyle("ToolbarButton");
_toolbarGUIStyle = new GUIStyle("Toolbar");
_initializedGUIStyle = true;
}
}
private void UpdateSelectedAssets()
{
_artInfo = new Dictionary<string, ListInfo>();
selectedAssetGuid.Clear();
foreach (Object obj in Selection.objects)
{
string path = AssetDatabase.GetAssetPath(obj);
if (Directory.Exists(path))
{
string[] folder = { path };
string[] guids = AssetDatabase.FindAssets(null, folder);
foreach (string guid in guids)
{
if (!selectedAssetGuid.Contains(guid) && !Directory.Exists(AssetDatabase.GUIDToAssetPath(guid)))
selectedAssetGuid.Add(guid);
}
}
else
{
string guid = AssetDatabase.AssetPathToGUID(path);
selectedAssetGuid.Add(guid);
}
}
needUpdateAssetTree = true;
}
private void UpdateDragAssets()
{
if (mouseOverWindow)
{
Object[] tempObj = DragAreaGetObject.GetObjects();
if (tempObj != null)
{
InitDataIfNeeded();
selectedAssetGuid.Clear();
foreach (Object obj in tempObj)
{
string path = AssetDatabase.GetAssetPath(obj);
if (Directory.Exists(path))
{
string[] folder = { path };
string[] guids = AssetDatabase.FindAssets(null, folder);
foreach (string guid in guids)
{
if (!selectedAssetGuid.Contains(guid) && !Directory.Exists(AssetDatabase.GUIDToAssetPath(guid)))
selectedAssetGuid.Add(guid);
}
}
else
{
string guid = AssetDatabase.AssetPathToGUID(path);
selectedAssetGuid.Add(guid);
}
}
needUpdateAssetTree = true;
}
}
}
private void UpdateAssetTree()
{
if (needUpdateAssetTree && selectedAssetGuid.Count != 0)
{
AssetViewItem root = SelectedAssetGuidToRootItem(selectedAssetGuid);
if (mAssetTreeView == null)
{
if (_treeViewState == null)
_treeViewState = new TreeViewState();
MultiColumnHeaderState headerState = AssetTreeView.CreateDefaultMultiColumnHeaderState(position.width, _isDepend);
ClickColumn multiColumnHeader = new ClickColumn(headerState);
mAssetTreeView = new AssetTreeView(_treeViewState, multiColumnHeader);
}
else
{
MultiColumnHeaderState headerState = AssetTreeView.CreateDefaultMultiColumnHeaderState(position.width, _isDepend);
ClickColumn multiColumnHeader = new ClickColumn(headerState);
mAssetTreeView.multiColumnHeader = multiColumnHeader;
}
mAssetTreeView.assetRoot = root;
mAssetTreeView.Reload();
needUpdateAssetTree = false;
int totalPrefab = 0;
int totalMat = 0;
string prefabName = "";
string matName = "";
StringBuilder sb = new StringBuilder();
if (_artInfo.Count > 0)
{
foreach (KeyValuePair<string, ListInfo> kv in _artInfo)
{
if (kv.Value.Type == "prefab")
{
totalPrefab += kv.Value.Count;
prefabName += kv.Value.Name + "<--->";
}
if (kv.Value.Type == "mat")
{
totalMat += kv.Value.Count;
matName += kv.Value.Name + "<--->";
}
string tempInfo = $"name <color=green>[{kv.Key}]</color>, type: <color=orange>[{kv.Value.Type}]</color>, count: <color=red>[{kv.Value.Count}]</color>";
sb.AppendLine(tempInfo);
}
}
if (totalPrefab > 0)
sb.Insert(0, $"预制体总数 <color=red>[{totalPrefab}]</color> 预制体详情 <color=green>[{prefabName}]</color> \r\n");
if (totalMat > 0)
sb.Insert(0, $"材质总数 <color=red>[{totalMat}]</color> 材质详情 <color=green>[{matName}]</color> \r\n");
string str = sb.ToString();
if (!string.IsNullOrEmpty(str))
Debug.Log(str);
}
}
public void DrawOptionBar()
{
EditorGUILayout.BeginHorizontal(_toolbarGUIStyle);
if (GUILayout.Button("点击更新本地缓存", _toolbarButtonGUIStyle))
{
Data.CollectDependenciesInfo();
needUpdateAssetTree = true;
GUIUtility.ExitGUI();
}
bool preIsDepend = _isDepend;
_isDepend = GUILayout.Toggle(_isDepend, _isDepend ? "依赖模式" : "引用模式", _toolbarButtonGUIStyle, GUILayout.Width(100));
if (preIsDepend != _isDepend)
OnModelSelect();
if (GUILayout.Button("展开", _toolbarButtonGUIStyle))
mAssetTreeView?.ExpandAll();
if (GUILayout.Button("折叠", _toolbarButtonGUIStyle))
mAssetTreeView?.CollapseAll();
EditorGUILayout.EndHorizontal();
}
private void OnModelSelect()
{
needUpdateAssetTree = true;
PlayerPrefs.SetInt(IS_DEPEND_PREF_KEY, _isDepend ? 1 : 0);
UpdateAssetTree();
}
private AssetViewItem SelectedAssetGuidToRootItem(List<string> inputSelectedAssetGuid)
{
_updatedAssetSet.Clear();
_parentAssetIsAdd.Clear();
_brotherAssetIsAdd.Clear();
int elementCount = 0;
AssetViewItem root = new AssetViewItem { id = elementCount, depth = -1, displayName = "Root", data = null };
const int depth = 0;
foreach (string childGuid in inputSelectedAssetGuid)
{
AssetViewItem rs = CreateTree(childGuid, ref elementCount, depth);
root.AddChild(rs);
}
_updatedAssetSet.Clear();
return root;
}
private AssetViewItem CreateTree(string guid, ref int elementCount, int depth)
{
if (_parentAssetIsAdd.Contains(guid))
return null;
if (needUpdateState && !_updatedAssetSet.Contains(guid))
{
Data.UpdateAssetState(guid);
_updatedAssetSet.Add(guid);
}
++elementCount;
ReferenceFinderData.AssetDescription referenceData = Data.assetDict[guid];
AssetViewItem root = new AssetViewItem { id = elementCount, displayName = referenceData.name, data = referenceData, depth = depth };
List<string> childGuids = _isDepend ? referenceData.dependencies : referenceData.references;
_parentAssetIsAdd.Add(guid);
foreach (string childGuid in childGuids)
{
if (_brotherAssetIsAdd.Contains(childGuid)) continue;
ListInfo listInfo = new ListInfo();
if (AssetDatabase.GUIDToAssetPath(childGuid).EndsWith(".mat") && depth < 2)
{
listInfo.Type = "mat";
listInfo.Count = 1;
listInfo.Name = Path.GetFileName(AssetDatabase.GUIDToAssetPath(childGuid));
if (!_artInfo.TryAdd(root.displayName, listInfo))
{
_artInfo[root.displayName].Count += 1;
_artInfo[root.displayName].Name += "<<==>>" + listInfo.Name;
}
}
if (AssetDatabase.GUIDToAssetPath(childGuid).EndsWith(".prefab") && !AssetDatabase.GUIDToAssetPath(childGuid).Contains("_gen_render") && depth < 2)
{
listInfo.Type = "prefab";
listInfo.Count = 1;
listInfo.Name = Path.GetFileName(AssetDatabase.GUIDToAssetPath(childGuid));
if (!_artInfo.TryAdd(root.displayName, listInfo))
{
_artInfo[root.displayName].Count += 1;
_artInfo[root.displayName].Name += "<<==>>" + listInfo.Name;
}
}
_brotherAssetIsAdd.Add(childGuid);
AssetViewItem rs = CreateTree(childGuid, ref elementCount, depth + 1);
if (rs != null)
root.AddChild(rs);
}
foreach (string childGuid in childGuids)
{
if (_brotherAssetIsAdd.Contains(childGuid))
_brotherAssetIsAdd.Remove(childGuid);
}
_parentAssetIsAdd.Remove(guid);
return root;
}
}
}

View File

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

View File

@@ -0,0 +1,33 @@
using System.Collections.Generic;
namespace TEngine.Editor
{
internal sealed class SortConfig
{
public static readonly Dictionary<SortType, SortType> SortTypeChangeByNameHandler = new Dictionary<SortType, SortType>
{
{ SortType.None, SortType.AscByName },
{ SortType.AscByName, SortType.DescByName },
{ SortType.DescByName, SortType.AscByName }
};
public static readonly Dictionary<SortType, SortType> SortTypeChangeByPathHandler = new Dictionary<SortType, SortType>
{
{ SortType.None, SortType.AscByPath },
{ SortType.AscByPath, SortType.DescByPath },
{ SortType.DescByPath, SortType.AscByPath }
};
public static readonly Dictionary<SortType, short> SortTypeGroup = new Dictionary<SortType, short>
{
{ SortType.None, 0 },
{ SortType.AscByPath, 1 },
{ SortType.DescByPath, 1 },
{ SortType.AscByName, 2 },
{ SortType.DescByName, 2 }
};
public const short TYPE_BY_NAME_GROUP = 2;
public const short TYPE_BY_PATH_GROUP = 1;
}
}

View File

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

View File

@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
namespace TEngine.Editor
{
internal sealed class SortHelper
{
public delegate int SortCompare(string lString, string rString);
public static readonly HashSet<string> SortedGuid = new HashSet<string>();
public static readonly Dictionary<string, SortType> SortedAsset = new Dictionary<string, SortType>();
public static SortType CurSortType = SortType.None;
public static SortType PathType = SortType.None;
public static SortType NameType = SortType.None;
public static readonly Dictionary<SortType, SortCompare> CompareFunction = new Dictionary<SortType, SortCompare>
{
{ SortType.AscByPath, CompareWithPath },
{ SortType.DescByPath, CompareWithPathDesc },
{ SortType.AscByName, CompareWithName },
{ SortType.DescByName, CompareWithNameDesc }
};
public static void Init()
{
SortedGuid.Clear();
SortedAsset.Clear();
}
public static void ChangeSortType(short sortGroup, Dictionary<SortType, SortType> handler, ref SortType recoverType)
{
if (SortConfig.SortTypeGroup[CurSortType] == sortGroup)
{
CurSortType = handler[CurSortType];
}
else
{
CurSortType = recoverType;
if (CurSortType == SortType.None) CurSortType = handler[CurSortType];
}
recoverType = CurSortType;
}
public static void SortByName() => ChangeSortType(SortConfig.TYPE_BY_NAME_GROUP, SortConfig.SortTypeChangeByNameHandler, ref NameType);
public static void SortByPath() => ChangeSortType(SortConfig.TYPE_BY_PATH_GROUP, SortConfig.SortTypeChangeByPathHandler, ref PathType);
public static void SortChild(ReferenceFinderData.AssetDescription data)
{
if (data == null) return;
if (SortedAsset.ContainsKey(data.path))
{
if (SortedAsset[data.path] == CurSortType) return;
SortType oldSortType = SortedAsset[data.path];
if (SortConfig.SortTypeGroup[oldSortType] == SortConfig.SortTypeGroup[CurSortType])
{
FastSort(data.dependencies);
FastSort(data.references);
}
else
{
NormalSort(data.dependencies);
NormalSort(data.references);
}
SortedAsset[data.path] = CurSortType;
}
else
{
NormalSort(data.dependencies);
NormalSort(data.references);
SortedAsset.Add(data.path, CurSortType);
}
}
public static void NormalSort(List<string> strList)
{
SortCompare curCompare = CompareFunction[CurSortType];
strList.Sort((l, r) => curCompare(l, r));
}
public static void FastSort(List<string> strList)
{
int i = 0;
int j = strList.Count - 1;
while (i < j)
{
(strList[i], strList[j]) = (strList[j], strList[i]);
i++;
j--;
}
}
public static int CompareWithName(string lString, string rString)
{
Dictionary<string, ReferenceFinderData.AssetDescription> asset = ResourceReferenceInfo.Data.assetDict;
return string.Compare(asset[lString].name, asset[rString].name, StringComparison.Ordinal);
}
public static int CompareWithNameDesc(string lString, string rString) => 0 - CompareWithName(lString, rString);
public static int CompareWithPath(string lString, string rString)
{
Dictionary<string, ReferenceFinderData.AssetDescription> asset = ResourceReferenceInfo.Data.assetDict;
return string.Compare(asset[lString].path, asset[rString].path, StringComparison.Ordinal);
}
public static int CompareWithPathDesc(string lString, string rString) => 0 - CompareWithPath(lString, rString);
}
}

View File

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

View File

@@ -0,0 +1,11 @@
namespace TEngine.Editor
{
public enum SortType
{
None,
AscByName,
DescByName,
AscByPath,
DescByPath
}
}

View File

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

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 20dbf6614352aaf488de29690deeab68
guid: cace8d5078f400048a8afde10e2bf41d
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -0,0 +1,36 @@
using TEngine;
using UnityEditor;
public static class TEngineSettingsProvider
{
[MenuItem("TEngine/Settings/TEngine UpdateSettings", priority = -1)]
public static void OpenSettings() => SettingsService.OpenProjectSettings("Project/TEngine/UpdateSettings");
private const string SettingsPath = "Project/TEngine/UpdateSettings";
[SettingsProvider]
public static SettingsProvider CreateMySettingsProvider()
{
return new SettingsProvider(SettingsPath, SettingsScope.Project)
{
label = "TEngine/UpdateSettings",
guiHandler = (searchContext) =>
{
var settings = Settings.UpdateSetting;
var serializedObject = new SerializedObject(settings);
EditorGUILayout.PropertyField(serializedObject.FindProperty("projectName"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("HotUpdateAssemblies"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("AOTMetaAssemblies"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("LogicMainDllName"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("AssemblyTextAssetExtension"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("AssemblyTextAssetPath"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("UpdateStyle"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("ResDownLoadPath"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("FallbackResDownLoadPath"));
serializedObject.ApplyModifiedProperties();
},
keywords = new[] { "TEngine", "Settings", "Custom" }
};
}
}

View File

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

View File

@@ -0,0 +1,32 @@
using TEngine.Editor.UI;
using UnityEditor;
public static class TEngineUISettingsProvider
{
[MenuItem("TEngine/Settings/TEngineUISettings", priority = -1)]
public static void OpenSettings() => SettingsService.OpenProjectSettings("Project/TEngine/UISettings");
private const string SettingsPath = "Project/TEngine/UISettings";
[SettingsProvider]
public static SettingsProvider CreateMySettingsProvider()
{
return new SettingsProvider(SettingsPath, SettingsScope.Project)
{
label = "TEngine/UISettings",
guiHandler = (searchContext) =>
{
var scriptGeneratorSetting = ScriptGeneratorSetting.Instance;
var scriptGenerator = new SerializedObject(scriptGeneratorSetting);
EditorGUILayout.PropertyField(scriptGenerator.FindProperty("_codePath"));
EditorGUILayout.PropertyField(scriptGenerator.FindProperty("_namespace"));
EditorGUILayout.PropertyField(scriptGenerator.FindProperty("_widgetName"));
EditorGUILayout.PropertyField(scriptGenerator.FindProperty("CodeStyle"));
EditorGUILayout.PropertyField(scriptGenerator.FindProperty("scriptGenerateRule"));
scriptGenerator.ApplyModifiedProperties();
},
keywords = new[] { "TEngine", "Settings", "Custom" }
};
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2672376ad13242bcb6869e90c4def052
timeCreated: 1742814408

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: b826e89349869884994512ad083e53cc
guid: 2982073ed28ef744e96be44c7f55f5f7
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: caf4d6b8aba38514fa2d00c93b086073
guid: 443dd03b960c78d449f988aa5f00322a
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -0,0 +1,118 @@
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityToolbarExtender;
namespace TEngine
{
public partial class UnityToolbarExtenderLeft
{
private const string PreviousSceneKey = "TEngine_PreviousScenePath"; // 用于存储之前场景路径的键
private const string IsLauncherBtn = "TEngine_IsLauncher"; // 用于存储之前是否按下launcher
private static readonly string SceneMain = "main";
private static readonly string ButtonStyleName = "Tab middle";
private static GUIStyle _buttonGuiStyle;
private static void OnToolbarGUI_SceneLauncher()
{
_buttonGuiStyle ??= new GUIStyle(ButtonStyleName)
{
padding = new RectOffset(2, 8, 2, 2),
alignment = TextAnchor.MiddleCenter,
fontStyle = FontStyle.Bold
};
GUILayout.FlexibleSpace();
if (GUILayout.Button(
new GUIContent("Launcher", EditorGUIUtility.FindTexture("PlayButton"), "Start Scene Launcher"),
_buttonGuiStyle))
SceneHelper.StartScene(SceneMain);
}
private static void OnPlayModeStateChanged(PlayModeStateChange state)
{
if (state == PlayModeStateChange.EnteredEditMode)
{
// 从 EditorPrefs 读取之前的场景路径
var previousScenePath = EditorPrefs.GetString(PreviousSceneKey, string.Empty);
if (!string.IsNullOrEmpty(previousScenePath) && EditorPrefs.GetBool(IsLauncherBtn))
{
EditorApplication.delayCall += () =>
{
if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
EditorSceneManager.OpenScene(previousScenePath);
};
}
EditorPrefs.SetBool(IsLauncherBtn, false);
}
}
private static void OnEditorQuit()
{
EditorPrefs.SetString(PreviousSceneKey, "");
EditorPrefs.SetBool(IsLauncherBtn, false);
}
private static class SceneHelper
{
private static string _sceneToOpen;
public static void StartScene(string sceneName)
{
if (EditorApplication.isPlaying) EditorApplication.isPlaying = false;
// 记录当前场景路径到 EditorPrefs
var activeScene = SceneManager.GetActiveScene();
if (activeScene.isLoaded && activeScene.name != SceneMain)
{
EditorPrefs.SetString(PreviousSceneKey, activeScene.path);
EditorPrefs.SetBool(IsLauncherBtn, true);
}
_sceneToOpen = sceneName;
EditorApplication.update += OnUpdate;
}
private static void OnUpdate()
{
if (_sceneToOpen == null ||
EditorApplication.isPlaying || EditorApplication.isPaused ||
EditorApplication.isCompiling || EditorApplication.isPlayingOrWillChangePlaymode)
return;
EditorApplication.update -= OnUpdate;
if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
{
string[] guids = AssetDatabase.FindAssets("t:scene " + _sceneToOpen, null);
if (guids.Length == 0)
{
Debug.LogWarning("Couldn't find scene file");
}
else
{
string scenePath = null;
// 优先打开完全匹配_sceneToOpen的场景
for (var i = 0; i < guids.Length; i++)
{
scenePath = AssetDatabase.GUIDToAssetPath(guids[i]);
if (scenePath.EndsWith("/" + _sceneToOpen + ".unity")) break;
}
// 如果没有完全匹配的场景,默认显示找到的第一个场景
if (string.IsNullOrEmpty(scenePath)) scenePath = AssetDatabase.GUIDToAssetPath(guids[0]);
EditorSceneManager.OpenScene(scenePath);
EditorApplication.isPlaying = true;
}
}
_sceneToOpen = null;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2f6d2ca3d3b34950a79294e49dd9d016
timeCreated: 1742389721

View File

@@ -0,0 +1,16 @@
using UnityEditor;
using UnityToolbarExtender;
namespace TEngine
{
[InitializeOnLoad]
public partial class UnityToolbarExtenderLeft
{
static UnityToolbarExtenderLeft()
{
ToolbarExtender.LeftToolbarGUI.Add(OnToolbarGUI_SceneLauncher);
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
EditorApplication.quitting += OnEditorQuit;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c4485948c7a944af943177214f7f5033
timeCreated: 1748228552

View File

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

View File

@@ -0,0 +1,79 @@
using UnityEditor;
using UnityEngine;
using UnityToolbarExtender;
namespace TEngine
{
/// <summary>
/// EditorPlayMode 控件(资源模式选择器)
/// </summary>
public partial class UnityToolbarExtenderRight
{
private const string BUTTON_STYLE_NAME = "Tab middle";
private static readonly string[] _resourceModeNames =
{
"EditorMode (编辑器下的模拟模式)",
"OfflinePlayMode (单机模式)",
"HostPlayMode (联机运行模式)",
"WebPlayMode (WebGL运行模式)"
};
private static int _resourceModeIndex = 0;
public static int ResourceModeIndex => _resourceModeIndex;
static class ToolbarStyles
{
public static readonly GUIStyle PopupStyle;
static ToolbarStyles()
{
PopupStyle = new GUIStyle(EditorStyles.popup)
{
//fontSize = 11,
alignment = TextAnchor.MiddleLeft,
padding = new RectOffset(6, 6, 0, 0)
};
}
}
static void OnToolbarGUI_EditorPlayMode()
{
EditorGUI.BeginDisabledGroup(EditorApplication.isPlayingOrWillChangePlaymode);
{
GUILayout.Space(8);
// 自动计算最长文本宽度
float maxWidth = 0;
foreach (string mode in _resourceModeNames)
{
Vector2 size = ToolbarStyles.PopupStyle.CalcSize(new GUIContent(mode));
if (size.x > maxWidth)
maxWidth = size.x;
}
// 加点缓冲宽度最多不超过220像素
float popupWidth = Mathf.Clamp(maxWidth + 20, 100, 220);
GUILayout.BeginHorizontal();
//GUILayout.Label("资源模式:", GUILayout.Width(65));
int selectedIndex = EditorGUILayout.Popup(
_resourceModeIndex,
_resourceModeNames,
ToolbarStyles.PopupStyle,
GUILayout.Width(popupWidth)
);
if (selectedIndex != _resourceModeIndex)
{
Debug.Log($"更改编辑器资源运行模式:{_resourceModeNames[selectedIndex]}");
_resourceModeIndex = selectedIndex;
EditorPrefs.SetInt("EditorPlayMode", selectedIndex);
}
GUILayout.EndHorizontal();
}
EditorGUI.EndDisabledGroup();
}
}
}

View File

@@ -0,0 +1,170 @@
#if UNITY_EDITOR
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEditor;
using UnityEngine.SceneManagement;
using UnityEditor.SceneManagement;
namespace TEngine
{
/// <summary>
/// SceneSwitcher
/// </summary>
public partial class UnityToolbarExtenderRight
{
private static List<(string sceneName, string scenePath)> m_InitScenes;
private static List<(string sceneName, string scenePath)> m_DefaultScenes;
private static List<(string sceneName, string scenePath)> m_OtherScenes;
private static string initScenePath = "Assets/Scenes";
private static string defaultScenePath = "Assets/AssetRaw/Scenes";
static void UpdateScenes()
{
// 获取初始化场景和默认场景
m_InitScenes = SceneSwitcher.GetScenesInPath(initScenePath);
m_DefaultScenes = SceneSwitcher.GetScenesInPath(defaultScenePath);
// 获取所有场景路径
List<(string sceneName, string scenePath)> allScenes = SceneSwitcher.GetAllScenes();
// 排除初始化场景和默认场景,获得其他场景
m_OtherScenes = new List<(string sceneName, string scenePath)>(allScenes);
m_OtherScenes.RemoveAll(scene =>
m_InitScenes.Exists(init => init.scenePath == scene.scenePath) ||
m_DefaultScenes.Exists(defaultScene => defaultScene.scenePath == scene.scenePath)
);
}
static void OnToolbarGUI_SceneSwitch()
{
// 如果没有场景,直接返回
if (m_InitScenes.Count == 0 && m_DefaultScenes.Count == 0 && m_OtherScenes.Count == 0)
return;
// 获取当前场景名称
string currentSceneName = SceneManager.GetActiveScene().name;
EditorGUILayout.LabelField("当前场景:", GUILayout.Width(52));
// 使用 GUI.skin.button.CalcSize 计算文本的精确宽度
GUIContent content = new GUIContent(currentSceneName);
Vector2 textSize = GUI.skin.button.CalcSize(content);
// 设置按钮宽度为文本的宽度,并限制最大值
float buttonWidth = textSize.x;
// 创建弹出菜单
var menu = new GenericMenu();
// 添加 "初始化路径" 下的场景按钮
AddScenesToMenu(m_InitScenes, "初始化场景", menu);
// 添加 "默认路径" 下的场景按钮
AddScenesToMenu(m_DefaultScenes, "默认场景", menu);
// 添加 "其他路径" 下的场景按钮
AddScenesToMenu(m_OtherScenes, "其他场景", menu);
// 自定义GUIStyle
GUIStyle buttonStyle = new GUIStyle(GUI.skin.button)
{
alignment = TextAnchor.MiddleLeft // 左对齐
};
// 在工具栏中显示菜单
if (GUILayout.Button(currentSceneName, buttonStyle, GUILayout.Width(buttonWidth)))
{
menu.ShowAsContext();
}
}
private static void AddScenesToMenu(List<(string sceneName, string scenePath)> scenes, string category, GenericMenu menu)
{
if (scenes.Count > 0)
{
foreach (var scene in scenes)
{
menu.AddItem(new GUIContent($"{category}/{scene.sceneName}"), false, () => SwitchScene(scene.scenePath));
}
}
}
static void SwitchScene(string scenePath)
{
// 保证场景是否保存
if (SceneSwitcher.PromptSaveCurrentScene())
{
// 保存场景后切换到新场景
EditorSceneManager.OpenScene(scenePath);
}
}
}
static class SceneSwitcher
{
public static bool PromptSaveCurrentScene()
{
// 检查当前场景是否有未保存的更改
if (SceneManager.GetActiveScene().isDirty)
{
// 弹出保存对话框
bool saveScene = EditorUtility.DisplayDialog(
"是否保存当前场景",
"当前场景有未保存的更改. 你是否想保存?",
"保存",
"取消"
);
// 如果选择保存,保存场景
if (saveScene)
{
EditorSceneManager.SaveScene(SceneManager.GetActiveScene());
}
else
{
// 如果选择取消,跳转到目标场景
return false; // 表示取消
}
return true;
}
return true; // 如果场景没有更改,直接返回 true
}
public static List<(string sceneName, string scenePath)> GetScenesInPath(string path)
{
var scenes = new List<(string sceneName, string scenePath)>();
// 查找指定路径下的所有场景文件
string[] guids = AssetDatabase.FindAssets("t:Scene", new[] { path });
foreach (var guid in guids)
{
var scenePath = AssetDatabase.GUIDToAssetPath(guid);
var sceneName = Path.GetFileNameWithoutExtension(scenePath);
scenes.Add((sceneName, scenePath));
}
return scenes;
}
// 获取项目中所有场景
public static List<(string sceneName, string scenePath)> GetAllScenes()
{
var allScenes = new List<(string sceneName, string scenePath)>();
// 查找项目中所有场景文件
string[] guids = AssetDatabase.FindAssets("t:Scene");
foreach (var guid in guids)
{
var scenePath = AssetDatabase.GUIDToAssetPath(guid);
var sceneName = Path.GetFileNameWithoutExtension(scenePath);
allScenes.Add((sceneName, scenePath));
}
return allScenes;
}
}
}
#endif

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 45017e4df12424c4fb16db4708d239f0
guid: 1fb6885a187ff8449a9cc083b7fbee28
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,21 @@
using UnityEditor;
using UnityToolbarExtender;
namespace TEngine
{
[InitializeOnLoad]
public partial class UnityToolbarExtenderRight
{
static UnityToolbarExtenderRight()
{
// 添加自定义按钮到右上工具栏
ToolbarExtender.RightToolbarGUI.Add(OnToolbarGUI_SceneSwitch);
// 订阅项目变化事件
EditorApplication.projectChanged += UpdateScenes;
UpdateScenes();
ToolbarExtender.RightToolbarGUI.Add(OnToolbarGUI_EditorPlayMode);
_resourceModeIndex = EditorPrefs.GetInt("EditorPlayMode", 0);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6240714bc0004010a4d6ca42a4f4305e
timeCreated: 1748227857

View File

@@ -60,8 +60,18 @@ namespace TEngine.Editor.UI
strFile.Append("using TEngine;\n\n");
strFile.Append($"namespace {ScriptGeneratorSetting.GetUINameSpace()}\n");
strFile.Append("{\n");
strFile.Append("\t[Window(UILayer.UI)]\n");
strFile.Append("\tclass " + root.name + " : UIWindow\n");
var widgetPrefix = $"{(ScriptGeneratorSetting.GetCodeStyle() == UIFieldCodeStyle.MPrefix ? "m_" : "_")}{ScriptGeneratorSetting.GetWidgetName()}";
if (root.name.StartsWith(widgetPrefix))
{
strFile.Append("\tclass " + root.name.Replace(widgetPrefix, "") + " : UIWidget\n");
}
else
{
strFile.Append($"\t[Window(UILayer.UI,location:\"{root.name}\")]\n");
strFile.Append("\tclass " + root.name + " : UIWindow\n");
}
strFile.Append("\t{\n");
}

View File

@@ -65,6 +65,9 @@ MonoBehaviour:
- uiElementRegex: m_slider
componentName: Slider
isUIWidget: 0
- uiElementRegex: m_toggle
componentName: Toggle
isUIWidget: 0
- uiElementRegex: m_group
componentName: ToggleGroup
isUIWidget: 0

View File

@@ -181,5 +181,15 @@ namespace TEngine.Editor.UI
return Instance.CodePath;
}
public static string GetWidgetName()
{
if (Instance == null)
{
return string.Empty;
}
return Instance.WidgetName;
}
}
}

View File

@@ -1,6 +1,9 @@
using System.Collections.Generic;
using System.Reflection;
using GameLogic;
#if ENABLE_OBFUZ
using Obfuz;
#endif
using TEngine;
#pragma warning disable CS0436
@@ -8,6 +11,9 @@ using TEngine;
/// <summary>
/// 游戏App。
/// </summary>
#if ENABLE_OBFUZ
[ObfuzIgnore(ObfuzScope.TypeName | ObfuzScope.MethodName)]
#endif
public partial class GameApp
{
private static List<Assembly> _hotfixAssembly;
@@ -22,13 +28,14 @@ public partial class GameApp
_hotfixAssembly = (List<Assembly>)objects[0];
Log.Warning("======= 看到此条日志代表你成功运行了热更新代码 =======");
Log.Warning("======= Entrance GameApp =======");
ModuleSystem.GetModule<IUpdateDriver>().AddDestroyListener(Release);
Utility.Unity.AddDestroyListener(Release);
Log.Warning("======= StartGameLogic =======");
StartGameLogic();
}
private static void StartGameLogic()
{
GameEvent.Get<ILoginUI>().ShowLoginUI();
// GameEvent.Get<ILoginUI>().ShowLoginUI();
GameModule.UI.ShowUIAsync<BattleMainUI>();
}

View File

@@ -10,7 +10,8 @@
"GUID:47f9fc774596be54ebfed7739cd70c86",
"GUID:1aa3e8589868c80499255710874679c0",
"GUID:d8b63aba1907145bea998dd612889d6b",
"GUID:756335c0388f7114790e504ed368ae1d"
"GUID:756335c0388f7114790e504ed368ae1d",
"GUID:4140bd2e2764f1f47ab93125ecb61942"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -405,6 +405,7 @@ namespace GameLogic
return;
}
window.CancelHideToCloseTimer();
window.Visible = false;
window.IsHide = true;
window.HideTimerId = GameModule.Timer.AddTimer((arg) =>

View File

@@ -57,6 +57,8 @@ namespace GameLogic
}
}
protected abstract void OnRelease();
protected virtual void OnRelease()
{
}
}
}

View File

@@ -4,7 +4,7 @@ using TEngine;
namespace GameLogic
{
[Window(UILayer.UI)]
[Window(UILayer.UI,location:"BattleMainUI")]
class BattleMainUI : UIWindow
{
#region

View File

@@ -18,7 +18,7 @@ namespace Procedure
LauncherMgr.Show(UIDefine.UILoadUpdate, $"下载完成...");
// 下载完成之后再保存本地版本。
PlayerPrefs.SetString("GAME_VERSION", _resourceModule.PackageVersion);
Utility.PlayerPrefs.SetString("GAME_VERSION", _resourceModule.PackageVersion);
}
protected override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds)

View File

@@ -1,8 +1,7 @@
using System;
using System.Collections;
using Cysharp.Threading.Tasks;
using System.Collections;
using Launcher;
using TEngine;
using UnityEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
@@ -14,8 +13,12 @@ namespace Procedure
public override bool UseNativeDialog => true;
private ProcedureOwner _procedureOwner;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
_procedureOwner = procedureOwner;
base.OnEnter(procedureOwner);
_initResourcesComplete = false;
@@ -23,7 +26,12 @@ namespace Procedure
LauncherMgr.Show(UIDefine.UILoadUpdate, "初始化资源中...");
// 注意:使用单机模式并初始化资源前,需要先构建 AssetBundle 并复制到 StreamingAssets 中,否则会产生 HTTP 404 错误
ModuleSystem.GetModule<IUpdateDriver>().StartCoroutine(InitResources(procedureOwner));
Utility.Unity.StartCoroutine(InitResources(procedureOwner));
}
private void ChangeToCreateDownloaderState(ProcedureOwner procedureOwner)
{
ChangeState<ProcedureCreateDownloader>(procedureOwner);
}
protected override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds)
@@ -36,25 +44,35 @@ namespace Procedure
return;
}
if ((_resourceModule.PlayMode == EPlayMode.HostPlayMode || _resourceModule.PlayMode == EPlayMode.WebPlayMode))
if (_resourceModule.PlayMode == EPlayMode.HostPlayMode || _resourceModule.PlayMode == EPlayMode.WebPlayMode)
{
//线上最新版本operation.PackageVersion
Log.Debug($"Updated package Version : from {_resourceModule.GetPackageVersion()} to {_resourceModule.PackageVersion}");
ChangeState<ProcedureUpdateManifest>(procedureOwner);
//注意:保存资源版本号作为下次默认启动的版本!
// 如果当前是WebGL或者是边玩边下载直接进入预加载阶段。
if (_resourceModule.PlayMode == EPlayMode.WebPlayMode ||
_resourceModule.UpdatableWhilePlaying)
{
// 边玩边下载还可以拓展首包支持。
ChangeToPreloadState(procedureOwner);
return;
}
ChangeToCreateDownloaderState(procedureOwner);
return;
}
ChangeState<ProcedurePreload>(procedureOwner);
ChangeToPreloadState(procedureOwner);
}
/// <summary>
/// 编辑器与单机保持相同流程。
/// </summary>
/// <returns></returns>
//// <summary>
/// 初始化资源流程。
/// <remarks>YooAsset 需要保持编辑器、单机、联机模式流程一致。</remarks>
private IEnumerator InitResources(ProcedureOwner procedureOwner)
{
string packageVersion;
Log.Info("更新资源清单!!!");
LauncherMgr.Show(UIDefine.UILoadUpdate, $"更新清单文件...");
// 1. 获取资源清单的版本信息
var operation1 = _resourceModule.RequestPackageVersionAsync();
yield return operation1;
@@ -64,11 +82,16 @@ namespace Procedure
yield break;
}
packageVersion = operation1.PackageVersion;
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;
@@ -77,19 +100,75 @@ namespace Procedure
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
, () =>
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))
{
ModuleSystem.GetModule<IUpdateDriver>().StartCoroutine(InitResources(procedureOwner));
}, UnityEngine.Application.Quit);
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

@@ -129,7 +129,7 @@ namespace Procedure
#endif
if (_mainLogicAssembly == null)
{
Log.Fatal($"Main logic assembly missing.");
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;
}

View File

@@ -1,58 +0,0 @@
using System;
using Cysharp.Threading.Tasks;
using Launcher;
using TEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
namespace Procedure
{
/// <summary>
/// 流程 => 用户尝试更新清单
/// </summary>
public class ProcedureUpdateManifest: ProcedureBase
{
public override bool UseNativeDialog { get; }
protected override void OnEnter(ProcedureOwner procedureOwner)
{
Log.Info("更新资源清单!!!");
LauncherMgr.Show(UIDefine.UILoadUpdate,$"更新清单文件...");
UpdateManifest(procedureOwner).Forget();
}
private async UniTaskVoid UpdateManifest(ProcedureOwner procedureOwner)
{
await UniTask.Delay(TimeSpan.FromSeconds(0.5f));
var operation = _resourceModule.UpdatePackageManifestAsync(_resourceModule.PackageVersion);
await operation.ToUniTask();
if(operation.Status == EOperationStatus.Succeed)
{
//更新成功
//注意:保存资源版本号作为下次默认启动的版本!
if (_resourceModule.PlayMode == EPlayMode.WebPlayMode ||
_resourceModule.UpdatableWhilePlaying)
{
// 边玩边下载还可以拓展首包支持。
ChangeState<ProcedurePreload>(procedureOwner);
return;
}
ChangeState<ProcedureCreateDownloader>(procedureOwner);
}
else
{
Log.Error(operation.Error);
LauncherMgr.ShowMessageBox($"用户尝试更新清单失败!点击确认重试 \n \n <color=#FF0000>原因{operation.Error}</color>", MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry
, () => { ChangeState<ProcedureUpdateManifest>(procedureOwner); }, UnityEngine.Application.Quit);
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 3f0ff55ed6ee4711a65cc396946bb43f
timeCreated: 1679634395

View File

@@ -1,127 +0,0 @@
using System;
using Cysharp.Threading.Tasks;
using Launcher;
using UnityEngine;
using TEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
namespace Procedure
{
/// <summary>
/// 流程 => 用户尝试更新静态版本
/// </summary>
public class ProcedureUpdateVersion : ProcedureBase
{
public override bool UseNativeDialog => true;
private ProcedureOwner _procedureOwner;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
_procedureOwner = procedureOwner;
base.OnEnter(procedureOwner);
LauncherMgr.Show(UIDefine.UILoadUpdate, $"更新静态版本文件...");
// 检查设备是否能够访问互联网。
if (Application.internetReachability == NetworkReachability.NotReachable)
{
if (!IsNeedUpdate())
{
return;
}
else
{
Log.Error("The device is not connected to the network");
LauncherMgr.ShowMessageBox(LoadText.Instance.Label_Net_UnReachable, MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry,
Application.Quit);
return;
}
}
LauncherMgr.Show(UIDefine.UILoadUpdate, LoadText.Instance.Label_RequestVersionIng);
// 用户尝试更新静态版本。
GetStaticVersion().Forget();
}
/// <summary>
/// 向用户尝试更新静态版本。
/// </summary>
private async UniTaskVoid GetStaticVersion()
{
await UniTask.Delay(TimeSpan.FromSeconds(0.5f));
var operation = _resourceModule.RequestPackageVersionAsync();
try
{
await operation.ToUniTask();
if (operation.Status == EOperationStatus.Succeed)
{
//线上最新版本operation.PackageVersion
_resourceModule.PackageVersion = operation.PackageVersion;
Log.Debug($"Updated package Version : from {_resourceModule.GetPackageVersion()} to {operation.PackageVersion}");
ChangeState<ProcedureUpdateManifest>(_procedureOwner);
}
else
{
OnGetStaticVersionError(operation.Error);
}
}
catch (Exception e)
{
OnGetStaticVersionError(e.Message);
}
}
private void OnGetStaticVersionError(string error)
{
Log.Error(error);
if (!IsNeedUpdate())
{
return;
}
LauncherMgr.ShowMessageBox($"用户尝试更新静态版本失败!点击确认重试 \n \n <color=#FF0000>原因{error}</color>", MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry
, () => { ChangeState<ProcedureUpdateVersion>(_procedureOwner); }, UnityEngine.Application.Quit);
}
private bool IsNeedUpdate()
{
// 如果不能联网且当前游戏非强制(不更新可以进入游戏。)
if (Settings.UpdateSetting.UpdateStyle == UpdateStyle.Optional)
{
Log.Warning("The device is not connected to the network");
// 获取上次成功记录的版本
string packageVersion = 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,
GetStaticVersion().Forget,
Application.Quit);
return false;
}
_resourceModule.PackageVersion = packageVersion;
LauncherMgr.Show(UIDefine.UILoadUpdate, LoadText.Instance.Label_Net_UnReachable);
LauncherMgr.ShowMessageBox(LoadText.Instance.Label_Net_UnReachable, MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry,
GetStaticVersion().Forget,
() => { ChangeState<ProcedureInitResources>(_procedureOwner); });
return false;
}
return true;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 6f0ff5df84e44adcb8064f46662a1b10
timeCreated: 1665651883

View File

@@ -2,7 +2,8 @@
"name": "Launcher",
"rootNamespace": "Launcher",
"references": [
"GUID:13ba8ce62aa80c74598530029cb2d649"
"GUID:13ba8ce62aa80c74598530029cb2d649",
"GUID:4140bd2e2764f1f47ab93125ecb61942"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -63,6 +63,7 @@ namespace Launcher
{
ui.transform.SetParent(_uiRoot.transform);
ui.transform.localScale = Vector3.one;
ui.transform.localRotation = Quaternion.identity;
ui.transform.localPosition = Vector3.zero;
RectTransform rect = ui.GetComponent<RectTransform>();
rect.sizeDelta = Vector2.zero;
@@ -221,4 +222,4 @@ namespace Launcher
#endregion
}
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -0,0 +1,24 @@
using Obfuz;
using Obfuz.EncryptionVM;
using UnityEngine;
namespace Launcher
{
public class ObfuzInitialize : MonoBehaviour
{
// 初始化EncryptionService后被混淆的代码才能正常运行
// 因此尽可能地早地初始化它。
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
private static void SetUpStaticSecretKey()
{
#if ENABLE_OBFUZ
Debug.Log("Enable Obfuz");
Debug.Log("SetUpStaticSecret begin");
EncryptionService<DefaultStaticEncryptionScope>.Encryptor = new GeneratedEncryptionVirtualMachine(Resources.Load<TextAsset>("Obfuz/defaultStaticSecretKey").bytes);
Debug.Log("SetUpStaticSecret end");
#else
Debug.Log("Disable Obfuz");
#endif
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 883bcac172734133a1972a05275df53b
timeCreated: 1753458333

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -38,7 +38,6 @@ RenderSettings:
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
m_UseRadianceAmbientProbe: 0
--- !u!157 &3
LightmapSettings:

View File

@@ -4,8 +4,8 @@
using UnityEngine;
using UnityEditor;
[HybridCLR.Editor.Settings.FilePath("ProjectSettings/AtlasConfiguration.asset")]
public class AtlasConfiguration : HybridCLR.Editor.Settings.ScriptableSingleton<AtlasConfiguration>
[FilePath("ProjectSettings/AtlasConfiguration.asset")]
public class AtlasConfiguration : EditorScriptableSingleton<AtlasConfiguration>
{
[Header("目录设置")]
[Tooltip("生成的图集输出目录")]

View File

@@ -88,9 +88,8 @@
public static void MarkParentAtlasesDirty(string assetPath)
{
var currentPath = Path.GetDirectoryName(assetPath);
var currentPath = Path.GetDirectoryName(assetPath).Replace("\\", "/");
var rootPath = Config.sourceAtlasRoot.Replace("\\", "/").TrimEnd('/');
while (currentPath != null && currentPath.StartsWith(rootPath))
{
var parentAtlasName = GetAtlasNameForDirectory(currentPath);
@@ -202,11 +201,24 @@
private static List<Sprite> LoadValidSprites(string atlasName)
{
return _atlasMap[atlasName]
.Where(File.Exists)
.Select(p => AssetDatabase.LoadAssetAtPath<Sprite>(p))
.Where(s => s != null)
.ToList();
if (_atlasMap.TryGetValue(atlasName, out List<string> spriteList))
{
var allSprites = new List<Sprite>();
foreach (var assetPath in spriteList.Where(File.Exists))
{
// 加载所有子图
var sprites = AssetDatabase.LoadAllAssetsAtPath(assetPath)
.OfType<Sprite>()
.Where(s => s != null)
.ToArray();
allSprites.AddRange(sprites);
}
return allSprites;
}
return new List<Sprite>();
}

View File

@@ -2,6 +2,10 @@
using HybridCLR.Editor;
using HybridCLR.Editor.Commands;
#endif
using System.IO;
using Obfuz.Settings;
using Obfuz4HybridCLR;
using System.Collections.Generic;
using TEngine.Editor;
using UnityEditor;
using UnityEngine;
@@ -9,29 +13,60 @@ using UnityEngine;
public static class BuildDLLCommand
{
private const string EnableHybridClrScriptingDefineSymbol = "ENABLE_HYBRIDCLR";
private const string EnableObfuzScriptingDefineSymbol = "ENABLE_OBFUZ";
#region HybridCLR/Define Symbols
/// <summary>
/// 禁用HybridCLR宏定义。
/// </summary>
[MenuItem("HybridCLR/Define Symbols/Disable HybridCLR", false, 30)]
public static void Disable()
public static void DisableHybridCLR()
{
ScriptingDefineSymbols.RemoveScriptingDefineSymbol(EnableHybridClrScriptingDefineSymbol);
#if ENABLE_HYBRIDCLR
HybridCLR.Editor.SettingsUtil.Enable = false;
// SyncAssemblyContent.RefreshAssembly();
UpdateSettingEditor.ForceUpdateAssemblies();
#endif
}
/// <summary>
/// 开启HybridCLR宏定义。
/// </summary>
[MenuItem("HybridCLR/Define Symbols/Enable HybridCLR", false, 31)]
public static void Enable()
public static void EnableHybridCLR()
{
ScriptingDefineSymbols.RemoveScriptingDefineSymbol(EnableHybridClrScriptingDefineSymbol);
ScriptingDefineSymbols.AddScriptingDefineSymbol(EnableHybridClrScriptingDefineSymbol);
#if ENABLE_HYBRIDCLR
HybridCLR.Editor.SettingsUtil.Enable = true;
// SyncAssemblyContent.RefreshAssembly();
UpdateSettingEditor.ForceUpdateAssemblies();
#endif
}
#endregion
#region Obfuz/Define Symbols
/// <summary>
/// 禁用Obfuz宏定义。
/// </summary>
[MenuItem("Obfuz/Define Symbols/Disable Obfuz", false, 30)]
public static void DisableObfuz()
{
ScriptingDefineSymbols.RemoveScriptingDefineSymbol(EnableObfuzScriptingDefineSymbol);
ObfuzSettings.Instance.buildPipelineSettings.enable = false;
}
/// <summary>
/// 开启Obfuz宏定义。
/// </summary>
[MenuItem("Obfuz/Define Symbols/Enable Obfuz", false, 31)]
public static void EnableObfuz()
{
ScriptingDefineSymbols.RemoveScriptingDefineSymbol(EnableObfuzScriptingDefineSymbol);
ScriptingDefineSymbols.AddScriptingDefineSymbol(EnableObfuzScriptingDefineSymbol);
ObfuzSettings.Instance.buildPipelineSettings.enable = true;
}
#endregion
[MenuItem("HybridCLR/Build/BuildAssets And CopyTo AssemblyTextAssetPath")]
public static void BuildAndCopyDlls()
@@ -55,6 +90,31 @@ public static class BuildDLLCommand
{
CopyAOTAssembliesToAssetPath();
CopyHotUpdateAssembliesToAssetPath();
#if ENABLE_HYBRIDCLR && ENABLE_OBFUZ
CompileDllCommand.CompileDll(target);
string obfuscatedHotUpdateDllPath = PrebuildCommandExt.GetObfuscatedHotUpdateAssemblyOutputPath(target);
ObfuscateUtil.ObfuscateHotUpdateAssemblies(target, obfuscatedHotUpdateDllPath);
Directory.CreateDirectory(Application.streamingAssetsPath);
string hotUpdateDllPath = $"{SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target)}";
List<string> obfuscationRelativeAssemblyNames = ObfuzSettings.Instance.assemblySettings.GetObfuscationRelativeAssemblyNames();
foreach (string assName in SettingsUtil.HotUpdateAssemblyNamesIncludePreserved)
{
string srcDir = obfuscationRelativeAssemblyNames.Contains(assName) ? obfuscatedHotUpdateDllPath : hotUpdateDllPath;
string srcFile = $"{srcDir}/{assName}.dll";
string dstFile = Application.dataPath +"/"+ TEngine.Settings.UpdateSetting.AssemblyTextAssetPath + $"/{assName}.dll.bytes";
if (File.Exists(srcFile))
{
File.Copy(srcFile, dstFile, true);
Debug.Log($"[CompileAndObfuscate] Copy {srcFile} to {dstFile}");
}
}
#endif
AssetDatabase.Refresh();
}

View File

@@ -17,6 +17,7 @@ namespace TEngine.Editor.Inspector
};
private SerializedProperty _playMode = null;
private SerializedProperty _encryptionType = null;
private SerializedProperty _updatableWhilePlaying = null;
private SerializedProperty _milliseconds = null;
private SerializedProperty _minUnloadUnusedAssetsInterval = null;
@@ -56,6 +57,8 @@ namespace TEngine.Editor.Inspector
_playMode.enumValueIndex = selectedIndex;
}
}
EditorGUILayout.PropertyField(_encryptionType);
}
EditorGUILayout.PropertyField(_updatableWhilePlaying);
@@ -220,6 +223,7 @@ namespace TEngine.Editor.Inspector
private void OnEnable()
{
_playMode = serializedObject.FindProperty("playMode");
_encryptionType = serializedObject.FindProperty("encryptionType");
_updatableWhilePlaying = serializedObject.FindProperty("updatableWhilePlaying");
_milliseconds = serializedObject.FindProperty("milliseconds");
_minUnloadUnusedAssetsInterval = serializedObject.FindProperty("minUnloadUnusedAssetsInterval");

View File

@@ -724,7 +724,12 @@ namespace TEngine.Localization
{
if (s_RecycledEditor==null)
{
FieldInfo info = typeof(EditorGUI).GetField("s_RecycledEditor", BindingFlags.NonPublic | BindingFlags.Static);
#if UNITY_6000_0_OR_NEWER
FieldInfo info = typeof(EditorGUI).GetField("s_RecycledEditorInternal", BindingFlags.NonPublic | BindingFlags.Static);
#else
FieldInfo info = typeof(EditorGUI).GetField("s_RecycledEditor", BindingFlags.NonPublic | BindingFlags.Static);
#endif
s_RecycledEditor = info.GetValue(null);
}

View File

@@ -57,14 +57,89 @@ namespace TEngine
Debug.LogWarning($"Start BuildPackage BuildTarget:{target} outputPath:{outputRoot}");
}
[MenuItem("TEngine/Build/一键打包AssetBundle")]
[MenuItem("TEngine/Build/一键打包AssetBundle _F8")]
public static void BuildCurrentPlatformAB()
{
{
BuildDLLCommand.BuildAndCopyDlls();
BuildTarget target = EditorUserBuildSettings.activeBuildTarget;
BuildInternal(target, Application.dataPath + "/../Builds/", packageVersion: GetBuildPackageVersion());
AssetDatabase.Refresh();
//复制到打包后的StreamingAssets
CopyStreamingAssetsFiles();
}
/// <summary>
/// 复制StreamingAssets文件去打包目录
/// </summary>
public static void CopyStreamingAssetsFiles()
{
if (!Settings.UpdateSetting.IsAutoAssetCopeToBuildAddress())
{
Debug.Log("UpdateSetting.IsAutoAssetCopeToBuildAddress关闭,并不会生产到打包目录中");
return;
}
// 获取StreamingAssets路径
string streamingAssetsPath = Application.streamingAssetsPath;
// 目标路径,可以是任何你想要的目录
string targetPath = Settings.UpdateSetting.GetBuildAddress();
// 判断目标路径是相对路径还是绝对路径
if (!System.IO.Path.IsPathRooted(targetPath))
{
// 如果是相对路径,结合 StreamingAssets 的路径进行合并
targetPath = System.IO.Path.Combine(streamingAssetsPath, targetPath);
}
// 如果目标目录不存在,创建它
if (!System.IO.Directory.Exists(targetPath))
{
Debug.LogError("打包目录不存在,检查UpdateSetting BuildAddress:"+targetPath);
return;
}
// 删除目标路径下的所有文件
string[] Deletefiles = System.IO.Directory.GetFiles(targetPath);
foreach (var file in Deletefiles)
{
System.IO.File.Delete(file);
Debug.Log($"删除文件: {file}");
}
// 删除目标路径下的所有子目录
string[] directories = System.IO.Directory.GetDirectories(targetPath);
foreach (var directory in directories)
{
System.IO.Directory.Delete(directory, true); // true 表示递归删除子目录及其中内容
Debug.Log($"删除目录: {directory}");
}
// 获取StreamingAssets中的所有文件排除.meta文件
string[] files = System.IO.Directory.GetFiles(streamingAssetsPath, "*", System.IO.SearchOption.AllDirectories);
// 遍历并复制文件到目标目录
foreach (var file in files)
{
// 排除.meta文件
if (file.EndsWith(".meta"))
continue;
// 获取相对路径,用于在目标目录中创建相同的文件结构
string relativePath = file.Substring(streamingAssetsPath.Length + 1);
string destinationFilePath = System.IO.Path.Combine(targetPath, relativePath);
// 确保目标文件夹存在
string destinationDir = System.IO.Path.GetDirectoryName(destinationFilePath);
if (!System.IO.Directory.Exists(destinationDir))
{
System.IO.Directory.CreateDirectory(destinationDir);
}
// 复制文件
System.IO.File.Copy(file, destinationFilePath, true); // true 表示覆盖已存在的文件
}
Debug.Log($"复制文件完成:{targetPath}");
}
private static BuildTarget GetBuildTarget(string platform)
{
BuildTarget target = BuildTarget.NoTarget;
@@ -130,6 +205,8 @@ namespace TEngine
buildParameters = scriptableBuildParameters;
scriptableBuildParameters.CompressOption = ECompressOption.LZ4;
scriptableBuildParameters.BuiltinShadersBundleName = GetBuiltinShaderBundleName("DefaultPackage");
}
buildParameters.BuildOutputRoot = AssetBundleBuilderHelper.GetDefaultBuildOutputRoot();
@@ -149,7 +226,6 @@ namespace TEngine
buildParameters.ClearBuildCacheFiles = false; //不清理构建缓存,启用增量构建,可以提高打包速度!
buildParameters.UseAssetDependencyDB = true; //使用资源依赖关系数据库,可以提高打包速度!
var buildResult = pipeline.Run(buildParameters, true);
if (buildResult.Success)
{
@@ -161,12 +237,23 @@ namespace TEngine
}
}
/// <summary>
/// 内置着色器资源包名称
/// 注意:和自动收集的着色器资源包名保持一致!
/// </summary>
private static string GetBuiltinShaderBundleName(string packageName)
{
var uniqueBundleName = AssetBundleCollectorSettingData.Setting.UniqueBundleName;
var packRuleResult = DefaultPackRule.CreateShadersPackRuleResult();
return packRuleResult.GetBundleName(packageName, uniqueBundleName);
}
/// <summary>
/// 创建加密类实例
/// </summary>
private static IEncryptionServices CreateEncryptionInstance(string packageName, EBuildPipeline buildPipeline)
{
var encryptionClassName = AssetBundleBuilderSetting.GetPackageEncyptionClassName(packageName, buildPipeline);
var encryptionClassName = AssetBundleBuilderSetting.GetPackageEncyptionServicesClassName(packageName, buildPipeline.ToString());
var encryptionClassTypes = EditorTools.GetAssignableTypes(typeof(IEncryptionServices));
var classType = encryptionClassTypes.Find(x => x.FullName != null && x.FullName.Equals(encryptionClassName));
if (classType != null)
@@ -223,7 +310,7 @@ namespace TEngine
public static void BuildImp(BuildTargetGroup buildTargetGroup, BuildTarget buildTarget, string locationPathName)
{
EditorUserBuildSettings.SwitchActiveBuildTarget(buildTargetGroup, BuildTarget.StandaloneWindows64);
EditorUserBuildSettings.SwitchActiveBuildTarget(buildTargetGroup, buildTarget);
AssetDatabase.Refresh();
BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions

View File

@@ -7,7 +7,9 @@
"GUID:4d1926c9df5b052469a1c63448b7609a",
"GUID:6e76b07590314a543b982daed6af2509",
"GUID:2373f786d14518f44b0f475db77ba4de",
"GUID:6055be8ebefd69e48b49212b09b47b2f"
"GUID:6055be8ebefd69e48b49212b09b47b2f",
"GUID:3743e71edcd5bd8499007797ef02cbfb",
"GUID:66e09fc524ec6594b8d6ca1d91aa1a41"
],
"includePlatforms": [
"Editor"

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: d4139bd1a1fd4d258b671e8e45fdd9f7
timeCreated: 1705986748

View File

@@ -1,92 +0,0 @@
using UnityEditor;
using UnityEngine;
using UnityToolbarExtender;
namespace TEngine
{
[InitializeOnLoad]
public class EditorPlayMode
{
static class ToolbarStyles
{
public static readonly GUIStyle ToolBarExtenderBtnStyle;
public static readonly GUIStyle ToolBarTextStyle;
public static readonly GUIStyle ToolBarButtonGuiStyle;
static ToolbarStyles()
{
ToolBarExtenderBtnStyle = new GUIStyle("Command")
{
fontSize = 12,
alignment = TextAnchor.MiddleCenter,
imagePosition = ImagePosition.ImageAbove,
fontStyle = FontStyle.Normal,
fixedWidth = 60
};
ToolBarTextStyle = new GUIStyle(BUTTON_STYLE_NAME)
{
padding = new RectOffset(2, 8, 2, 2),
alignment = TextAnchor.MiddleCenter,
fontStyle = FontStyle.Bold
};
ToolBarButtonGuiStyle = new GUIStyle(BUTTON_STYLE_NAME)
{
padding = new RectOffset(2, 8, 2, 2),
alignment = TextAnchor.MiddleCenter,
fontStyle = FontStyle.Bold
};
}
}
static EditorPlayMode()
{
ToolbarExtender.RightToolbarGUI.Add(OnToolbarGUI);
_resourceModeIndex = EditorPrefs.GetInt("EditorPlayMode", 0);
}
private const string BUTTON_STYLE_NAME = "Tab middle";
static GUIStyle _buttonGuiStyle;
private static readonly string[] _resourceModeNames =
{
"EditorMode (编辑器下的模拟模式)",
"OfflinePlayMode (单机模式)",
"HostPlayMode (联机运行模式)",
"WebPlayMode (WebGL运行模式)"
};
private static int _resourceModeIndex = 0;
public static int ResourceModeIndex => _resourceModeIndex;
static void OnToolbarGUI()
{
EditorGUI.BeginDisabledGroup(EditorApplication.isPlayingOrWillChangePlaymode);
{
// GUILayout.Label("资源加载模式:",ToolbarStyles.ToolBarTextStyle);
GUILayout.Space(10);
GUILayout.FlexibleSpace();
// 资源模式
int selectedIndex = EditorGUILayout.Popup("", _resourceModeIndex, _resourceModeNames, ToolbarStyles.ToolBarButtonGuiStyle);
// ReSharper disable once RedundantCheckBeforeAssignment
if (selectedIndex != _resourceModeIndex)
{
Debug.Log($"更改编辑器资源运行模式 : {_resourceModeNames[selectedIndex]}");
_resourceModeIndex = selectedIndex;
EditorPrefs.SetInt("EditorPlayMode", selectedIndex);
}
GUILayout.FlexibleSpace();
GUILayout.Space(400);
}
EditorGUI.EndDisabledGroup();
}
}
}

View File

@@ -1,84 +0,0 @@
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityToolbarExtender;
namespace TEngine
{
[InitializeOnLoad]
public class SceneSwitchLeftButton
{
private static readonly string SceneMain = "main";
static SceneSwitchLeftButton()
{
ToolbarExtender.LeftToolbarGUI.Add(OnToolbarGUI);
}
static readonly string ButtonStyleName = "Tab middle";
static GUIStyle _buttonGuiStyle;
static void OnToolbarGUI()
{
_buttonGuiStyle ??= new GUIStyle(ButtonStyleName)
{
padding = new RectOffset(2, 8, 2, 2),
alignment = TextAnchor.MiddleCenter,
fontStyle = FontStyle.Bold
};
GUILayout.FlexibleSpace();
if (GUILayout.Button(
new GUIContent("Launcher", EditorGUIUtility.FindTexture("PlayButton"), $"Start Scene Launcher"),
_buttonGuiStyle))
{
SceneHelper.StartScene(SceneMain);
}
}
}
static class SceneHelper
{
static string _sceneToOpen;
public static void StartScene(string sceneName)
{
if (EditorApplication.isPlaying)
{
EditorApplication.isPlaying = false;
}
_sceneToOpen = sceneName;
EditorApplication.update += OnUpdate;
}
static void OnUpdate()
{
if (_sceneToOpen == null ||
EditorApplication.isPlaying || EditorApplication.isPaused ||
EditorApplication.isCompiling || EditorApplication.isPlayingOrWillChangePlaymode)
{
return;
}
EditorApplication.update -= OnUpdate;
if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
{
string[] guids = AssetDatabase.FindAssets("t:scene " + _sceneToOpen, null);
if (guids.Length == 0)
{
Debug.LogWarning("Couldn't find scene file");
}
else
{
string scenePath = AssetDatabase.GUIDToAssetPath(guids[0]);
EditorSceneManager.OpenScene(scenePath);
EditorApplication.isPlaying = true;
}
}
_sceneToOpen = null;
}
}
}

View File

@@ -0,0 +1,101 @@
using System;
using System.IO;
using System.Linq;
using UnityEditorInternal;
using UnityEngine;
namespace TEngine.Editor
{
public class EditorScriptableSingleton<T> : ScriptableObject where T : ScriptableObject
{
private static T _instance;
public static T Instance
{
get
{
if (!_instance)
{
LoadOrCreate();
}
return _instance;
}
}
public static T LoadOrCreate()
{
string filePath = GetFilePath();
if (!string.IsNullOrEmpty(filePath))
{
var arr = InternalEditorUtility.LoadSerializedFileAndForget(filePath);
_instance = arr.Length > 0 ? arr[0] as T : _instance ?? CreateInstance<T>();
}
else
{
Debug.LogError($"save location of {nameof(EditorScriptableSingleton<T>)} is invalid");
}
return _instance;
}
public static void Save(bool saveAsText = true)
{
if (!_instance)
{
Debug.LogError("Cannot save ScriptableSingleton: no instance!");
return;
}
string filePath = GetFilePath();
if (!string.IsNullOrEmpty(filePath))
{
string directoryName = Path.GetDirectoryName(filePath);
if (!Directory.Exists(directoryName))
{
if (directoryName != null)
{
Directory.CreateDirectory(directoryName);
}
}
UnityEngine.Object[] obj = { _instance };
InternalEditorUtility.SaveToSerializedFileAndForget(obj, filePath, saveAsText);
}
}
protected static string GetFilePath()
{
return typeof(T).GetCustomAttributes(inherit: true)
.Where(v => v is FilePathAttribute)
.Cast<FilePathAttribute>()
.FirstOrDefault()
?.Filepath;
}
}
[AttributeUsage(AttributeTargets.Class)]
public class FilePathAttribute : Attribute
{
internal readonly string Filepath;
/// <summary>
/// 单例存放路径。
/// </summary>
/// <param name="path">相对 Project 路径。</param>
public FilePathAttribute(string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentException("Invalid relative path (it is empty)");
}
if (path[0] == '/')
{
path = path.Substring(1);
}
Filepath = path;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bec53acfb41d41a29b3664015d318e39
timeCreated: 1742523496

View File

@@ -1,8 +1,9 @@
#if ENABLE_HYBRIDCLR
using System.Collections.Generic;
using System.Linq;
using HybridCLR.Editor.Settings;
using UnityEditor;
using UnityEngine;
using HybridCLR.Editor.Settings;
namespace TEngine.Editor
{
@@ -40,11 +41,17 @@ namespace TEngine.Editor
// 标记对象为“已修改”,确保修改能被保存
EditorUtility.SetDirty(updateSetting);
bool isHotChanged = HotUpdateAssemblies.SequenceEqual(updateSetting.HotUpdateAssemblies);
bool isAOTChanged = AOTMetaAssemblies.SequenceEqual(updateSetting.AOTMetaAssemblies);
bool isHotChanged = !HotUpdateAssemblies.SequenceEqual(updateSetting.HotUpdateAssemblies);
bool isAOTChanged = !AOTMetaAssemblies.SequenceEqual(updateSetting.AOTMetaAssemblies);
if (isHotChanged)
{
HybridCLRSettings.Instance.hotUpdateAssemblies = updateSetting.HotUpdateAssemblies.ToArray();
for (int i = 0; i < updateSetting.HotUpdateAssemblies.Count; i++)
{
var assemblyName = updateSetting.HotUpdateAssemblies[i];
string assemblyNameWithoutExtension = assemblyName.Substring(0, assemblyName.LastIndexOf('.'));
HybridCLRSettings.Instance.hotUpdateAssemblies[i] = assemblyNameWithoutExtension;
}
Debug.Log("HotUpdateAssemblies changed");
}
if (isAOTChanged)
@@ -52,7 +59,45 @@ namespace TEngine.Editor
HybridCLRSettings.Instance.patchAOTAssemblies = updateSetting.AOTMetaAssemblies.ToArray();
Debug.Log("AOTMetaAssemblies changed");
}
if (isAOTChanged || isHotChanged)
{
// 在修改HybridCLRSettings后添加
EditorUtility.SetDirty(HybridCLRSettings.Instance);
AssetDatabase.SaveAssets();
}
}
}
public static void ForceUpdateAssemblies()
{
UpdateSetting updateSetting = null;
string[] guids = AssetDatabase.FindAssets("t:UpdateSetting");
if (guids.Length >= 1)
{
string path = AssetDatabase.GUIDToAssetPath(guids[0]);
updateSetting = AssetDatabase.LoadAssetAtPath<UpdateSetting>(path);
}
if (updateSetting == null)
{
Log.Error("Can not find UpdateSetting");
return;
}
HybridCLRSettings.Instance.hotUpdateAssemblies = updateSetting.HotUpdateAssemblies.ToArray();
for (int i = 0; i < updateSetting.HotUpdateAssemblies.Count; i++)
{
var assemblyName = updateSetting.HotUpdateAssemblies[i];
string assemblyNameWithoutExtension = assemblyName.Substring(0, assemblyName.LastIndexOf('.'));
HybridCLRSettings.Instance.hotUpdateAssemblies[i] = assemblyNameWithoutExtension;
}
HybridCLRSettings.Instance.patchAOTAssemblies = updateSetting.AOTMetaAssemblies.ToArray();
Debug.Log("HotUpdateAssemblies changed");
}
}
}
}
#endif

View File

@@ -1,638 +0,0 @@
using System;
using System.Collections.Generic;
#region Class Documentation
/************************************************************************************************************
Class Name: ActorEventDispatcher.cs 局部单位事件分发器。
Type: Actor, Event
Example:
private ActorEventDispatcher _event;
/// <summary>
/// 局部事件管理器。
/// <remark>只分发和监听这个Event内部的事件</remark>
/// </summary>
public ActorEventDispatcher Event => _event ??= MemoryPool.Acquire<ActorEventDispatcher>();
// owner局部发送事件。
owner.Event.Send(eventId,xxx);
// owner监听自身事件并绑定持有对象为owner。 比如是组件的情况下。移除时调用RemoveAllListenerByOwner(owner)会根据持有对象(组件)移除。
owner.Event.AddEventListener(eventId,xxx,owner);
************************************************************************************************************/
#endregion
namespace TEngine
{
/// <summary>
/// 局部单位事件分发器。
/// </summary>
public class ActorEventDispatcher : IMemory
{
/// <summary>
/// 所有事件。
/// </summary>
private readonly Dictionary<int, List<EventRegInfo>> _allEventListenerMap = new();
/// <summary>
/// 用于标记一个事件是不是正在处理。
/// </summary>
private readonly List<int> _processEventList = new();
/// <summary>
/// 用于标记一个事件是不是被移除。
/// </summary>
private readonly List<int> _delayDeleteEventList = new();
/// <summary>
/// 移除所有事件监听。
/// </summary>
public void DestroyAllEventListener()
{
var itr = _allEventListenerMap.GetEnumerator();
while (itr.MoveNext())
{
var kv = itr.Current;
List<EventRegInfo> list = kv.Value;
foreach (var eventRegInfo in list)
{
EventRegInfo.Release(eventRegInfo);
}
kv.Value.Clear();
}
_processEventList.Clear();
_delayDeleteEventList.Clear();
itr.Dispose();
}
/// <summary>
/// 延迟移除事件。
/// </summary>
/// <param name="eventId"></param>
private void AddDelayDelete(int eventId)
{
if (!_delayDeleteEventList.Contains(eventId))
{
_delayDeleteEventList.Add(eventId);
Log.Info("delay delete eventId[{0}]", eventId);
}
}
/// <summary>
/// 如果找到eventId对应的监听删除所有标记为delete的监听。
/// </summary>
/// <param name="eventId">事件Id。</param>
private void CheckDelayDelete(int eventId)
{
if (_delayDeleteEventList.Contains(eventId))
{
if (_allEventListenerMap.TryGetValue(eventId, out var listListener))
{
for (int i = listListener.Count - 1; i >= 0; i--)
{
if (listListener[i].IsDeleted)
{
Log.Info("remove delay delete eventId[{0}]", eventId);
EventRegInfo.Release(listListener[i]);
listListener.RemoveAt(i);
}
}
}
_delayDeleteEventList.Remove(eventId);
}
}
/// <summary>
/// 发送事件。
/// </summary>
/// <param name="eventId">事件Id。</param>
public void SendEvent(int eventId)
{
if (_allEventListenerMap.TryGetValue(eventId, out var listListener))
{
_processEventList.Add(eventId);
#if UNITY_EDITOR
int iEventCnt = _processEventList.Count;
#endif
var count = listListener.Count;
for (int i = 0; i < count; i++)
{
var node = listListener[i];
if (node.IsDeleted)
{
continue;
}
if (listListener[i].Callback is Action callBack)
{
callBack();
}
else
{
Log.Fatal("Invalid event data type: {0}", eventId);
}
}
#if UNITY_EDITOR
Log.Assert(iEventCnt == _processEventList.Count);
Log.Assert(eventId == _processEventList[^1]);
#endif
_processEventList.RemoveAt(_processEventList.Count - 1);
CheckDelayDelete(eventId);
}
}
/// <summary>
/// 发送事件。
/// </summary>
/// <param name="eventId">事件Id。</param>
/// <param name="arg1">事件参数。</param>
/// <typeparam name="TArg1">事件参数类型1。</typeparam>
public void SendEvent<TArg1>(int eventId, TArg1 arg1)
{
if (_allEventListenerMap.TryGetValue(eventId, out var listListener))
{
_processEventList.Add(eventId);
#if UNITY_EDITOR
int iEventCnt = _processEventList.Count;
#endif
var count = listListener.Count;
for (int i = 0; i < count; i++)
{
var node = listListener[i];
if (node.IsDeleted)
{
continue;
}
if (listListener[i].Callback is Action<TArg1> callBack)
{
callBack(arg1);
}
else
{
Log.Fatal("Invalid event data type: {0}", eventId);
}
}
#if UNITY_EDITOR
Log.Assert(iEventCnt == _processEventList.Count);
Log.Assert(eventId == _processEventList[^1]);
#endif
_processEventList.RemoveAt(_processEventList.Count - 1);
CheckDelayDelete(eventId);
}
}
/// <summary>
/// 发送事件。
/// </summary>
/// <param name="eventId">事件Id。</param>
/// <param name="arg1">事件参数1。</param>
/// <param name="arg2">事件参数2。</param>
/// <typeparam name="TArg1">事件参数类型1。</typeparam>
/// <typeparam name="TArg2">事件参数类型2。</typeparam>
public void SendEvent<TArg1, TArg2>(int eventId, TArg1 arg1, TArg2 arg2)
{
if (_allEventListenerMap.TryGetValue(eventId, out var listListener))
{
_processEventList.Add(eventId);
#if UNITY_EDITOR
int iEventCnt = _processEventList.Count;
#endif
var count = listListener.Count;
for (int i = 0; i < count; i++)
{
var node = listListener[i];
if (node.IsDeleted)
{
continue;
}
if (listListener[i].Callback is Action<TArg1, TArg2> callBack)
{
callBack(arg1, arg2);
}
else
{
Log.Fatal("Invalid event data type: {0}", eventId);
}
}
#if UNITY_EDITOR
Log.Assert(iEventCnt == _processEventList.Count);
Log.Assert(eventId == _processEventList[^1]);
#endif
_processEventList.RemoveAt(_processEventList.Count - 1);
CheckDelayDelete(eventId);
}
}
/// <summary>
/// 发送事件。
/// </summary>
/// <param name="eventId">事件Id。</param>
/// <param name="arg1">事件参数1。</param>
/// <param name="arg2">事件参数2。</param>
/// <param name="arg3">事件参数3。</param>
/// <typeparam name="TArg1">事件参数类型1。</typeparam>
/// <typeparam name="TArg2">事件参数类型2。</typeparam>
/// <typeparam name="TArg3">事件参数类型3。</typeparam>
public void SendEvent<TArg1, TArg2, TArg3>(int eventId, TArg1 arg1, TArg2 arg2, TArg3 arg3)
{
if (_allEventListenerMap.TryGetValue(eventId, out var listListener))
{
_processEventList.Add(eventId);
#if UNITY_EDITOR
int iEventCnt = _processEventList.Count;
#endif
var count = listListener.Count;
for (int i = 0; i < count; i++)
{
var node = listListener[i];
if (node.IsDeleted)
{
continue;
}
if (node.Callback is Action<TArg1, TArg2, TArg3> callBack)
{
callBack(arg1, arg2, arg3);
}
else
{
Log.Fatal("Invalid event data type: {0}", eventId);
}
}
#if UNITY_EDITOR
Log.Assert(iEventCnt == _processEventList.Count);
Log.Assert(eventId == _processEventList[^1]);
#endif
_processEventList.RemoveAt(_processEventList.Count - 1);
CheckDelayDelete(eventId);
}
}
/// <summary>
/// 发送事件。
/// </summary>
/// <param name="eventId">事件Id。</param>
/// <param name="arg1">事件参数1。</param>
/// <param name="arg2">事件参数2。</param>
/// <param name="arg3">事件参数3。</param>
/// <param name="arg4">事件参数4。</param>
/// <typeparam name="TArg1">事件参数类型1。</typeparam>
/// <typeparam name="TArg2">事件参数类型2。</typeparam>
/// <typeparam name="TArg3">事件参数类型3。</typeparam>
/// <typeparam name="TArg4">事件参数类型4。</typeparam>
public void SendEvent<TArg1, TArg2, TArg3, TArg4>(int eventId, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4)
{
if (_allEventListenerMap.TryGetValue(eventId, out var listListener))
{
_processEventList.Add(eventId);
#if UNITY_EDITOR
int iEventCnt = _processEventList.Count;
#endif
var count = listListener.Count;
for (int i = 0; i < count; i++)
{
var node = listListener[i];
if (node.IsDeleted)
{
continue;
}
if (listListener[i].Callback is Action<TArg1, TArg2, TArg3, TArg4> callBack)
{
callBack(arg1, arg2, arg3, arg4);
}
else
{
Log.Fatal("Invalid event data type: {0}", eventId);
}
}
#if UNITY_EDITOR
Log.Assert(iEventCnt == _processEventList.Count);
Log.Assert(eventId == _processEventList[^1]);
#endif
_processEventList.RemoveAt(_processEventList.Count - 1);
CheckDelayDelete(eventId);
}
}
/// <summary>
/// 增加事件监听。
/// </summary>
/// <param name="eventId">事件Id。</param>
/// <param name="eventCallback">事件回调。</param>
/// <param name="owner">持有者Tag。</param>
public void AddEventListener(int eventId, Action eventCallback, object owner)
{
AddEventListenerImp(eventId, eventCallback, owner);
}
/// <summary>
/// 增加事件监听。
/// </summary>
/// <param name="eventId">事件Id。</param>
/// <param name="eventCallback">事件回调。</param>
/// <param name="owner">持有者Tag。</param>
/// <typeparam name="TArg1">事件参数类型1。</typeparam>
public void AddEventListener<TArg1>(int eventId, Action<TArg1> eventCallback, object owner)
{
AddEventListenerImp(eventId, eventCallback, owner);
}
/// <summary>
/// 增加事件监听。
/// </summary>
/// <param name="eventId">事件Id。</param>
/// <param name="eventCallback">事件回调。</param>
/// <param name="owner">持有者Tag。</param>
/// <typeparam name="TArg1">事件参数类型1。</typeparam>
/// <typeparam name="TArg2">事件参数类型2。</typeparam>
public void AddEventListener<TArg1, TArg2>(int eventId, Action<TArg1, TArg2> eventCallback, object owner)
{
AddEventListenerImp(eventId, eventCallback, owner);
}
/// <summary>
/// 增加事件监听。
/// </summary>
/// <param name="eventId">事件Id。</param>
/// <param name="eventCallback">事件回调。</param>
/// <param name="owner">持有者Tag。</param>
/// <typeparam name="TArg1">事件参数类型1。</typeparam>
/// <typeparam name="TArg2">事件参数类型2。</typeparam>
/// <typeparam name="TArg3">事件参数类型3。</typeparam>
public void AddEventListener<TArg1, TArg2, TArg3>(int eventId, Action<TArg1, TArg2, TArg3> eventCallback, object owner)
{
AddEventListenerImp(eventId, eventCallback, owner);
}
/// <summary>
/// 增加事件监听。
/// </summary>
/// <param name="eventId">事件Id。</param>
/// <param name="eventCallback">事件回调。</param>
/// <param name="owner">持有者Tag。</param>
/// <typeparam name="TArg1">事件参数类型1。</typeparam>
/// <typeparam name="TArg2">事件参数类型2。</typeparam>
/// <typeparam name="TArg3">事件参数类型3。</typeparam>
/// <typeparam name="TArg4">事件参数类型4。</typeparam>
public void AddEventListener<TArg1, TArg2, TArg3, TArg4>(int eventId, Action<TArg1, TArg2, TArg3, TArg4> eventCallback, object owner)
{
AddEventListenerImp(eventId, eventCallback, owner);
}
/// <summary>
/// 增加事件监听具体实现。
/// </summary>
/// <param name="eventId">事件Id。</param>
/// <param name="listener">事件回调。</param>
/// <param name="owner">持有者Tag。</param>
private void AddEventListenerImp(int eventId, Delegate listener, object owner)
{
if (!_allEventListenerMap.TryGetValue(eventId, out var listListener))
{
listListener = new List<EventRegInfo>();
_allEventListenerMap.Add(eventId, listListener);
}
var existNode = listListener.Find((node) => node.Callback == listener);
if (existNode != null)
{
if (existNode.IsDeleted)
{
existNode.IsDeleted = false;
Log.Warning("AddEvent hashId deleted, repeat add: {0}", eventId);
return;
}
Log.Fatal("AddEvent hashId repeated: {0}", eventId);
return;
}
listListener.Add(EventRegInfo.Alloc(listener, owner));
}
/// <summary>
/// 通过持有者Tag移除监听。
/// </summary>
/// <param name="owner">持有者Tag。</param>
public void RemoveAllListenerByOwner(object owner)
{
var itr = _allEventListenerMap.GetEnumerator();
while (itr.MoveNext())
{
var kv = itr.Current;
var list = kv.Value;
int eventId = kv.Key;
bool isProcessing = _processEventList.Contains(eventId);
bool delayDeleted = false;
for (int i = list.Count - 1; i >= 0; i--)
{
var regInfo = list[i];
if (regInfo.Owner == owner)
{
if (isProcessing)
{
regInfo.IsDeleted = true;
delayDeleted = true;
}
else
{
EventRegInfo.Release(list[i]);
list.RemoveAt(i);
}
}
}
if (delayDeleted)
{
AddDelayDelete(eventId);
}
}
itr.Dispose();
}
/// <summary>
/// 移除事件监听。
/// </summary>
/// <param name="eventId">事件Id。</param>
/// <param name="eventCallback">消息回调。</param>
public void RemoveEventListener(int eventId, Action eventCallback)
{
RemoveEventListenerImp(eventId, eventCallback);
}
/// <summary>
/// 移除事件监听。
/// </summary>
/// <param name="eventId">事件Id。</param>
/// <param name="eventCallback">消息回调。</param>
/// <typeparam name="TArg1">参数类型1。</typeparam>
public void RemoveEventListener<TArg1>(int eventId, Action<TArg1> eventCallback)
{
RemoveEventListenerImp(eventId, eventCallback);
}
/// <summary>
/// 移除事件监听。
/// </summary>
/// <param name="eventId">事件Id。</param>
/// <param name="eventCallback">消息回调。</param>
/// <typeparam name="TArg1">参数类型1。</typeparam>
/// <typeparam name="TArg2">参数类型2。</typeparam>
public void RemoveEventListener<TArg1, TArg2>(int eventId, Action<TArg1, TArg2> eventCallback)
{
RemoveEventListenerImp(eventId, eventCallback);
}
/// <summary>
/// 移除事件监听。
/// </summary>
/// <param name="eventId">事件Id。</param>
/// <param name="eventCallback">消息回调。</param>
/// <typeparam name="TArg1">参数类型1。</typeparam>
/// <typeparam name="TArg2">参数类型2。</typeparam>
/// <typeparam name="TArg3">参数类型3。</typeparam>
public void RemoveEventListener<TArg1, TArg2, TArg3>(int eventId, Action<TArg1, TArg2, TArg3> eventCallback)
{
RemoveEventListenerImp(eventId, eventCallback);
}
/// <summary>
/// 移除事件监听。
/// </summary>
/// <param name="eventId">事件Id。</param>
/// <param name="eventCallback">消息回调。</param>
/// <typeparam name="TArg1">参数类型1。</typeparam>
/// <typeparam name="TArg2">参数类型2。</typeparam>
/// <typeparam name="TArg3">参数类型3。</typeparam>
/// <typeparam name="TArg4">参数类型4。</typeparam>
public void RemoveEventListener<TArg1, TArg2, TArg3, TArg4>(int eventId, Action<TArg1, TArg2, TArg3, TArg4> eventCallback)
{
RemoveEventListenerImp(eventId, eventCallback);
}
/// <summary>
/// 删除监听,如果是正在处理的监听则标记为删除
/// </summary>
/// <param name="eventId">事件Id。</param>
/// <param name="listener">事件监听。</param>
protected void RemoveEventListenerImp(int eventId, Delegate listener)
{
if (_allEventListenerMap.TryGetValue(eventId, out var listListener))
{
bool isProcessing = _processEventList.Contains(eventId);
if (!isProcessing)
{
listListener.RemoveAll(node => node.Callback == listener);
}
else
{
int listenCnt = listListener.Count;
for (int i = 0; i < listenCnt; i++)
{
var node = listListener[i];
if (node.Callback == listener)
{
node.IsDeleted = true;
AddDelayDelete(eventId);
break;
}
}
}
}
}
/// <summary>
/// 清除回收接口。
/// </summary>
public void Clear()
{
DestroyAllEventListener();
}
}
/// <summary>
/// 事件注册信息。
/// </summary>
public class EventRegInfo : IMemory
{
/// <summary>
/// 事件回调。
/// </summary>
public Delegate Callback;
/// <summary>
/// 事件持有者。
/// </summary>
public object Owner;
/// <summary>
/// 事件是否删除。
/// </summary>
public bool IsDeleted;
public EventRegInfo(Delegate callback, object owner)
{
this.Callback = callback;
Owner = owner;
IsDeleted = false;
}
public EventRegInfo()
{
}
public void Clear()
{
Callback = null;
Owner = null;
IsDeleted = false;
}
public static EventRegInfo Alloc(Delegate callback, object owner)
{
EventRegInfo ret = MemoryPool.Acquire<EventRegInfo>();
ret.Callback = callback;
ret.Owner = owner;
ret.IsDeleted = false;
return ret;
}
public static void Release(EventRegInfo eventRegInfo)
{
MemoryPool.Release(eventRegInfo);
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 222c2a26f3b14d42950d0e9a10b61b38
timeCreated: 1673511406

Some files were not shown because too many files have changed in this diff Show More