Compare commits
153 Commits
beforinput
...
master
Author | SHA1 | Date | |
---|---|---|---|
a1aa816426 | |||
ccf2279947 | |||
dd12a88d11 | |||
01da084b5a | |||
c09085354b | |||
b07972f66d | |||
51d638e116 | |||
224e0d1126 | |||
2356fd7448 | |||
78f56831ae | |||
ab4496ebef | |||
ca0880e256 | |||
5d44174d01 | |||
a5642aef54 | |||
f8b8eb6213 | |||
94808a8b84 | |||
31ca1b9571 | |||
aba9d4fa41 | |||
4beea769b5 | |||
f043a8323e | |||
15993b493f | |||
f03bb43bdf | |||
09407468c2 | |||
c864dceb5d | |||
9270852fe0 | |||
627faef387 | |||
8aa3fc2b55 | |||
f275fa79c7 | |||
1a526b10f6 | |||
a7807194ed | |||
cd4163d9fe | |||
c36adccbde | |||
e1d7ef2d13 | |||
0f27fa328c | |||
33283a9a34 | |||
0a45c4fbbd | |||
a1db47ab4b | |||
ebb4cb401c | |||
57c4947623 | |||
629cd16016 | |||
8afcbce6b1 | |||
15c64ae655 | |||
3c04d26c50 | |||
a1fe8dff21 | |||
fbcaecf596 | |||
c69340f7b9 | |||
f4af119cdb | |||
45d075722b | |||
a596d199c2 | |||
de7340ee39 | |||
d1c150e1be | |||
570419e2cc | |||
caaad7a6bf | |||
9995a784aa | |||
92169c2f87 | |||
59968bbd1f | |||
20b3d2e8ef | |||
7b40e190d0 | |||
fe53dc4021 | |||
631d4bb0bc | |||
6193e57a99 | |||
3cc79ca730 | |||
bd45706c00 | |||
7d2b80ab55 | |||
eaeb789513 | |||
0e5421d6bd | |||
7eed897474 | |||
3783868bee | |||
7c3c025b7c | |||
f192e748b4 | |||
235dc0ed39 | |||
c8f0d77b34 | |||
52e9f42184 | |||
2bc6972ebe | |||
e6ef077f16 | |||
f3d246ab43 | |||
0d3024521c | |||
ef419928ed | |||
6c23bdc1c2 | |||
eb6b25256b | |||
6be11bdf42 | |||
306199dba8 | |||
97c278b4ec | |||
cc57a1e0df | |||
ace77987e4 | |||
1ffa708e71 | |||
c7a4f1467a | |||
992adc115b | |||
902dc95d19 | |||
e5f497a163 | |||
8fc65c2f70 | |||
f0b139e692 | |||
fcd64a54f6 | |||
e8fc45b006 | |||
9cfee768c3 | |||
e8bac390af | |||
412c9759df | |||
74bde1d94a | |||
213ed7163c | |||
24036739a7 | |||
1f33d35394 | |||
e66740b8a9 | |||
36b614c4a7 | |||
d7fe849bb1 | |||
ee9d54386b | |||
eb2f5d268c | |||
13cddd8a3b | |||
16a7acee61 | |||
959956706f | |||
8e24f556af | |||
424e139692 | |||
49d0a9aa86 | |||
a079947bc4 | |||
a21be185ce | |||
3dc7e9acd1 | |||
f325e63e26 | |||
017026f706 | |||
4116a05b6c | |||
209c82da80 | |||
8bd3b2247b | |||
5aabda7c37 | |||
6111319ad4 | |||
279483c14f | |||
c89b6b1ab5 | |||
8b2b66a940 | |||
902a8f1360 | |||
569e4e44fe | |||
c77cac4812 | |||
5f9d67b36f | |||
5014c81539 | |||
4b0a781957 | |||
10ac17c18b | |||
591204e044 | |||
cfb3bc8b6a | |||
34cf5487e8 | |||
6cc7b2fb4b | |||
fbe47bce1b | |||
3797bba442 | |||
4e87cfe9ee | |||
e463c23c4c | |||
50e3a30f31 | |||
8007af0bc5 | |||
924727eb7c | |||
cf8127c553 | |||
49c893fdab | |||
667089891a | |||
811b0b2a48 | |||
a93f6449c8 | |||
3257e15dda | |||
1c20707751 | |||
4e1f8fd045 | |||
955a35659a | |||
3c10873180 |
.gitignore
AxibugEmuOnline.Client.Switch/Assets
AxiProjectTools.meta
AxiProjectTools
AxiNSPack.meta
Editors.metaAxiNSPack
Editors.meta
Editors.metaEditors
hacpack.metahacpack
switch_keys.metaswitch_keys
Editors
AxiAutoBuild.csAxiAutoBuild.cs.metaAxiPrefabCache.csAxiPrefabCache.cs.metaAxiProjectTools.AssetsAutoSetting.csAxiProjectTools.AssetsAutoSetting.cs.metaAxiProjectTools.Statistics.csAxiProjectTools.Statistics.cs.metaAxiProjectTools.csAxiProjectTools.cs.metaAxiStatisticsCache.csAxiStatisticsCache.cs.meta
Scene.metaScene
Editors
AxibugEmuOnline.Editor.asmdefAxibugEmuOnline.Editor.asmdef.metaCommandDispatcherEditor.csCommandDispatcherEditor.cs.meta
New Controls.inputactionsNew Controls.inputactions.metaPlugins.metaPlugins
AxiNSApi.meta
AxiNSApi
AxiNS.csAxiNS.cs.metaAxiNSApi.asmdefAxiNSApi.asmdef.metaAxiNSErrCode.csAxiNSErrCode.cs.metaAxiNSIO.csAxiNSIO.cs.metaAxiNSMono.csAxiNSMono.cs.metaAxiNSMount.csAxiNSMount.cs.metaAxiNSSDCard.csAxiNSSDCard.cs.metaAxiNSUser.csAxiNSUser.cs.metaAxiNSWaitHandle.meta
AxiReplay.metaAxiNSWaitHandle
AxiReplay
AxiReplay.asmdefAxiReplay.asmdef.metaFrameProfiler.csFrameProfiler.cs.metaIReplayReader.csIReplayReader.cs.metaIReplayWriter.csIReplayWriter.cs.metaInternalRingbuffer.csInternalRingbuffer.cs.metaNetReplay.csNetReplay.cs.metaReplayData.csReplayData.cs.metaReplayReader.csReplayReader.cs.metaReplayWriter.csReplayWriter.cs.meta
Demigiant.metaDemigiant
20
.gitignore
vendored
20
.gitignore
vendored
@ -9,13 +9,28 @@
|
||||
/AxibugEmuOnline.Client/obj/
|
||||
/AxibugEmuOnline.Client/log/
|
||||
/AxibugEmuOnline.Client/ProjectSettings/Packages/
|
||||
|
||||
/AxibugEmuOnline.Client.Switch/*.vsconfig
|
||||
/AxibugEmuOnline.Client.Switch/Library/
|
||||
/AxibugEmuOnline.Client.Switch/Temp/
|
||||
/AxibugEmuOnline.Client.Switch/UserSettings/
|
||||
/AxibugEmuOnline.Client.Switch/.vs/
|
||||
/AxibugEmuOnline.Client.Switch/*.csproj
|
||||
/AxibugEmuOnline.Client.Switch/*.sln
|
||||
/AxibugEmuOnline.Client.Switch/*.sln
|
||||
/AxibugEmuOnline.Client.Switch/obj/
|
||||
/AxibugEmuOnline.Client.Switch/Logs/
|
||||
/AxibugEmuOnline.Client.Switch/ProjectSettings/Packages/
|
||||
|
||||
/AxibugEmuOnline.Web/config.cfg
|
||||
/AxibugEmuOnline.Client/ProjectSettings/ProjectVersion.txt
|
||||
/AxibugEmuOnline.Client/ProjectSettings/AutoStreamingSettings.asset
|
||||
/AxibugEmuOnline.Client/Logs
|
||||
|
||||
/AxibugEmuOnline.Client/Assets/AxiProjectTools/AxiNSPack/switch_keys/*.keys
|
||||
/AxibugEmuOnline.Client/Assets/AxiProjectTools/AxiNSPack/switch_keys/*.keys.meta
|
||||
**/switch_keys/*.keys
|
||||
**/switch_keys/*.keys.meta
|
||||
**/NintendoSDKPlugin/
|
||||
**/NintendoSDKPlugin.meta
|
||||
|
||||
/virtuanessrc097-master
|
||||
/AxibugEmuOnline.Server/config.cfg
|
||||
@ -49,3 +64,4 @@ AxibugEmuOnline.GameScreenConvert/bin/
|
||||
AxibugEmuOnline.GameScreenConvert/obj/
|
||||
|
||||
/.vs/
|
||||
AxibugEmuOnline.Client/ProjectSettings/PackageManagerSettings.asset
|
||||
|
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91746af636f351140a4796dc4e98be6d
|
||||
guid: e745b95dbce117b48b4bd17df06ed302
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 04e926e140ae5bc4fa46bd64067261cf
|
||||
guid: 164952f99969ca942b4761b200d7e381
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5fe26f58ab822c44888b86305c5326e0
|
||||
guid: cbe37300d75dbd641be2e6dca83a913c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
@ -0,0 +1,369 @@
|
||||
#if UNITY_EDITOR_WIN
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build.Reporting;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AxibugEmuOnline.Editors
|
||||
{
|
||||
public class AxibugNSPTools : Editor
|
||||
{
|
||||
static string WorkRoot = Path.GetFullPath(Path.Combine(Application.dataPath, "AxiProjectTools/AxiNSPack"));
|
||||
static string switch_keys = Path.GetFullPath(Path.Combine(Application.dataPath, "AxiProjectTools/AxiNSPack/switch_keys"));
|
||||
static string hacpack_root = Path.GetFullPath(Path.Combine(Application.dataPath, "AxiProjectTools/AxiNSPack/hacpack"));
|
||||
static Dictionary<string, string> tools = new Dictionary<string, string>();
|
||||
static string prodKeysPath;
|
||||
|
||||
static void InitToolPath()
|
||||
{
|
||||
#region 初始化工具路径
|
||||
// 获取环境变量(需要添加环境变量检查)
|
||||
string sdkRoot = Environment.GetEnvironmentVariable("NINTENDO_SDK_ROOT");
|
||||
tools["authoringTool"] = Path.Combine(sdkRoot, "Tools/CommandLineTools/AuthoringTool/AuthoringTool.exe");
|
||||
tools["hacPack"] = Path.Combine(hacpack_root, "hacpack");
|
||||
#endregion
|
||||
}
|
||||
|
||||
[MenuItem("Axibug移植工具/Switch/AxibugNSPTools/RepackNSP(仅重新构建NSP)")]
|
||||
static void RepackNSP()
|
||||
{
|
||||
if (!CheckEnvironmentVariable())
|
||||
return;
|
||||
|
||||
string path = EditorUtility.OpenFilePanel(
|
||||
title: "选择 .nsp 文件",
|
||||
directory: Path.Combine(Application.dataPath, ".."), // 默认路径为项目 Assets 目录
|
||||
extension: "nsp" // 限制文件类型为 .nsp
|
||||
);
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return;
|
||||
|
||||
RepackNSP(path);
|
||||
}
|
||||
|
||||
|
||||
//[MenuItem("Axibug移植工具/Switch/AxibugNSPTools/UnpackNSP(解包工具)")]
|
||||
//static void UnpackNSP()
|
||||
//{
|
||||
// InitToolPath();
|
||||
// if (!CheckEnvironmentVariable())
|
||||
// return;
|
||||
|
||||
// string nspFilePath = EditorUtility.OpenFilePanel(
|
||||
// title: "选择 .nsp 文件",
|
||||
// directory: Path.Combine(Application.dataPath, ".."), // 默认路径为项目 Assets 目录
|
||||
// extension: "nsp" // 限制文件类型为 .nsp
|
||||
// );
|
||||
|
||||
// if (string.IsNullOrEmpty(nspFilePath))
|
||||
// return;
|
||||
|
||||
// string nspParentDir = Path.GetDirectoryName(nspFilePath);
|
||||
// string extractPath = Path.Combine(nspParentDir, "repacker_extract");
|
||||
// ExecuteCommand($"{tools["authoringTool"]} extract -o \"{extractPath}\" \"{nspFilePath}\"", nspParentDir);
|
||||
//}
|
||||
|
||||
[MenuItem("Axibug移植工具/Switch/AxibugNSPTools/Build With RepackNSP(打包NSP并重新构建NSP)")]
|
||||
public static void BuildWithRepackNSP()
|
||||
{
|
||||
if (!CheckEnvironmentVariable())
|
||||
return;
|
||||
|
||||
if (!EditorUtility.DisplayDialog("打包", $"确认打包NSP?", "继续", "取消"))
|
||||
return;
|
||||
|
||||
var levels = new List<string>();
|
||||
foreach (var scene in EditorBuildSettings.scenes)
|
||||
{
|
||||
if (scene.enabled)
|
||||
levels.Add(scene.path);
|
||||
}
|
||||
|
||||
var buildOpt = EditorUserBuildSettings.development ? BuildOptions.Development : BuildOptions.None;
|
||||
if (EditorUserBuildSettings.buildWithDeepProfilingSupport)
|
||||
buildOpt |= BuildOptions.EnableDeepProfilingSupport;
|
||||
if (EditorUserBuildSettings.allowDebugging)
|
||||
buildOpt |= BuildOptions.AllowDebugging;
|
||||
|
||||
//勾选构建NSP包
|
||||
EditorUserBuildSettings.switchCreateRomFile = true;
|
||||
|
||||
#if UNITY_2018_1_OR_NEWER && !UNITY_6000_0_OR_NEWER
|
||||
string titleid = PlayerSettings.Switch.applicationID;
|
||||
#else
|
||||
string titleid = "null";
|
||||
#endif
|
||||
string targetName = $"{Application.productName}_{titleid}.nsp";
|
||||
|
||||
string _locationPathName = $"Output/NSPBuild/{DateTime.Now.ToString("yyyyMMddHHmmss")}/{targetName}";
|
||||
var options = new BuildPlayerOptions
|
||||
{
|
||||
scenes = levels.ToArray(),
|
||||
locationPathName = _locationPathName,
|
||||
target = BuildTarget.Switch,
|
||||
options = buildOpt
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
BuildReport report = BuildPipeline.BuildPlayer(options);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"[AxibugNSPTools] Unity Build NSP 错误:{ex.ToString()}");
|
||||
return;
|
||||
}
|
||||
|
||||
string NSPFullPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", _locationPathName));
|
||||
RepackNSP(NSPFullPath);
|
||||
}
|
||||
|
||||
static bool CheckEnvironmentVariable()
|
||||
{
|
||||
// 获取环境变量(需要添加环境变量检查)
|
||||
string sdkRoot = Environment.GetEnvironmentVariable("NINTENDO_SDK_ROOT");
|
||||
if (string.IsNullOrEmpty(sdkRoot))
|
||||
{
|
||||
Debug.LogError($"[AxibugNSPTools]请先正确配置环境变量:NINTENDO_SDK_ROOT,(若已配置,则保证配置后彻底重启Unity Hub和Unity)");
|
||||
return false;
|
||||
}
|
||||
|
||||
#region 获取prod.keys文件路径
|
||||
prodKeysPath = Path.Combine(
|
||||
switch_keys,
|
||||
"prod.keys"
|
||||
);
|
||||
|
||||
if (!File.Exists(prodKeysPath))
|
||||
{
|
||||
Debug.LogError($"[AxibugNSPTools]未找到 prod.keys! 请先准备文件,path:{prodKeysPath}");
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void RepackNSP(string nspFile)
|
||||
{
|
||||
InitToolPath();
|
||||
|
||||
#region 处理NSP文件路径
|
||||
string nspFilePath = nspFile;
|
||||
string nspFileName = Path.GetFileName(nspFilePath);
|
||||
string nspParentDir = Path.GetDirectoryName(nspFilePath);
|
||||
#endregion
|
||||
|
||||
#region 提取Title ID
|
||||
string titleID = ExtractTitleID(nspFilePath);
|
||||
if (string.IsNullOrEmpty(titleID))
|
||||
{
|
||||
Debug.LogError($"[AxibugNSPTools]NSP文件名一部分,必须包含TitleID");
|
||||
return;
|
||||
}
|
||||
Debug.Log($"[AxibugNSPTools]Using Title ID: {titleID}");
|
||||
#endregion
|
||||
|
||||
EditorUtility.DisplayProgressBar("AxibugNSPTools", $"清理临时目录", 0);
|
||||
#region 清理临时目录
|
||||
CleanDirectory(Path.Combine(nspParentDir, "repacker_extract"));
|
||||
CleanDirectory(Path.Combine(Path.GetTempPath(), "NCA"));
|
||||
CleanDirectory(Path.Combine(nspParentDir, "hacpack_backup"));
|
||||
#endregion
|
||||
|
||||
EditorUtility.DisplayProgressBar("AxibugNSPTools", $"解包NSP文件", 0.2f);
|
||||
#region 解包NSP文件
|
||||
string extractPath = Path.Combine(nspParentDir, "repacker_extract");
|
||||
ExecuteCommand($"{tools["authoringTool"]} extract -o \"{extractPath}\" \"{nspFilePath}\"", nspParentDir);
|
||||
|
||||
string controlPath = null;
|
||||
string programPath = null;
|
||||
FindNACPAndNPDPaths(extractPath, ref controlPath, ref programPath);
|
||||
if (controlPath == null || programPath == null)
|
||||
{
|
||||
Debug.LogError("[AxibugNSPTools] Critical directory structure not found!");
|
||||
return;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 重建NCA/NSP
|
||||
string tmpPath = Path.Combine(Path.GetTempPath(), "NCA");
|
||||
EditorUtility.DisplayProgressBar("AxibugNSPTools", $"重建 Program NCA", 0.3f);
|
||||
string programNCA = BuildProgramNCA(tmpPath, titleID, programPath, nspParentDir);
|
||||
EditorUtility.DisplayProgressBar("AxibugNSPTools", $"重建 Control NCA", 0.4f);
|
||||
string controlNCA = BuildControlNCA(tmpPath, titleID, controlPath, nspParentDir);
|
||||
EditorUtility.DisplayProgressBar("AxibugNSPTools", $"重建 Meta NCA", 0.5f);
|
||||
BuildMetaNCA(tmpPath, titleID, programNCA, controlNCA, nspParentDir);
|
||||
EditorUtility.DisplayProgressBar("AxibugNSPTools", $"重建NSP", 0.6f);
|
||||
string outputNSP = BuildFinalNSP(nspFilePath, nspParentDir, tmpPath, titleID, nspParentDir);
|
||||
EditorUtility.DisplayProgressBar("AxibugNSPTools", $"重建NSP", 0.9f);
|
||||
Debug.Log($"[AxibugNSPTools]Repacking completed: {outputNSP}");
|
||||
|
||||
#endregion
|
||||
|
||||
EditorUtility.DisplayProgressBar("AxibugNSPTools", $"清理临时目录", 1);
|
||||
#region 清理临时目录
|
||||
CleanDirectory(Path.Combine(nspParentDir, "repacker_extract"));
|
||||
CleanDirectory(Path.Combine(Path.GetTempPath(), "NCA"));
|
||||
CleanDirectory(Path.Combine(nspParentDir, "hacpack_backup"));
|
||||
#endregion
|
||||
System.Diagnostics.Process.Start("explorer", "/select,\"" + outputNSP.Trim() + "\"");
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region 辅助方法
|
||||
static string GetUserInput()
|
||||
{
|
||||
Console.Write("Enter the NSP filepath: ");
|
||||
return Console.ReadLine();
|
||||
}
|
||||
static string ExtractTitleID(string path)
|
||||
{
|
||||
var match = Regex.Match(path, @"0100[\dA-Fa-f]{12}");
|
||||
return match.Success ? match.Value : null;
|
||||
}
|
||||
|
||||
static void CleanDirectory(string path)
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
Directory.Delete(path, true);
|
||||
while (Directory.Exists(path)) ; // 等待删除完成
|
||||
}
|
||||
}
|
||||
|
||||
static void FindNACPAndNPDPaths(string basePath, ref string controlPath, ref string programPath)
|
||||
{
|
||||
foreach (var dir in Directory.GetDirectories(basePath))
|
||||
{
|
||||
if (File.Exists(Path.Combine(dir, "fs0/control.nacp")))
|
||||
controlPath = dir;
|
||||
if (File.Exists(Path.Combine(dir, "fs0/main.npdm")))
|
||||
programPath = dir;
|
||||
}
|
||||
}
|
||||
|
||||
static string ExecuteCommand(string command, string workdir)
|
||||
{
|
||||
Debug.Log($"调用cmd=>{command}");
|
||||
var process = new System.Diagnostics.Process()
|
||||
{
|
||||
StartInfo = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = "cmd.exe",
|
||||
Arguments = $"/C {command}",
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true, // 增加错误流重定向
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
StandardOutputEncoding = Encoding.UTF8, // 明确指定编码
|
||||
StandardErrorEncoding = Encoding.UTF8,
|
||||
WorkingDirectory = workdir
|
||||
}
|
||||
};
|
||||
|
||||
var outputBuilder = new StringBuilder();
|
||||
var errorBuilder = new StringBuilder();
|
||||
|
||||
// 使用事件处理程序捕获实时输出
|
||||
process.OutputDataReceived += (sender, args) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(args.Data))
|
||||
{
|
||||
outputBuilder.AppendLine(args.Data);
|
||||
Debug.Log($"[AxibugNSPTools]{args.Data}");
|
||||
}
|
||||
};
|
||||
|
||||
process.ErrorDataReceived += (sender, args) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(args.Data))
|
||||
{
|
||||
errorBuilder.AppendLine(args.Data);
|
||||
Debug.LogError($"[AxibugNSPTools]{args.Data}");
|
||||
}
|
||||
};
|
||||
|
||||
process.Start();
|
||||
|
||||
// 开始异步读取输出
|
||||
process.BeginOutputReadLine();
|
||||
process.BeginErrorReadLine();
|
||||
|
||||
// 等待进程退出(此时流已关闭)
|
||||
process.WaitForExit();
|
||||
|
||||
// 将错误信息附加到主输出
|
||||
if (errorBuilder.Length > 0)
|
||||
{
|
||||
outputBuilder.AppendLine("\nError Output:");
|
||||
outputBuilder.Append(errorBuilder);
|
||||
}
|
||||
|
||||
return outputBuilder.ToString();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region NCA构建逻辑
|
||||
static string BuildProgramNCA(string tmpPath, string titleID, string programDir, string workdir)
|
||||
{
|
||||
string args = $"-k \"{prodKeysPath}\" -o \"{tmpPath}\" --titleid {titleID} " +
|
||||
$"--type nca --ncatype program --exefsdir \"{programDir}/fs0\" " +
|
||||
$"--romfsdir \"{programDir}/fs1\" --logodir \"{programDir}/fs2\"";
|
||||
|
||||
string output = ExecuteCommand($"{tools["hacPack"]} {args}", workdir);
|
||||
return ParseNCAOutput(output, "Program");
|
||||
}
|
||||
|
||||
static string BuildControlNCA(string tmpPath, string titleID, string controlDir, string workdir)
|
||||
{
|
||||
string args = $"-k \"{prodKeysPath}\" -o \"{tmpPath}\" --titleid {titleID} " +
|
||||
$"--type nca --ncatype control --romfsdir \"{controlDir}/fs0\"";
|
||||
|
||||
string output = ExecuteCommand($"{tools["hacPack"]} {args}", workdir);
|
||||
|
||||
return ParseNCAOutput(output, "Control");
|
||||
}
|
||||
|
||||
static void BuildMetaNCA(string tmpPath, string titleID, string programNCA, string controlNCA, string workdir)
|
||||
{
|
||||
string args = $"-k \"{prodKeysPath}\" -o \"{tmpPath}\" --titleid {titleID} " +
|
||||
$"--type nca --ncatype meta --titletype application " +
|
||||
$"--programnca \"{programNCA}\" --controlnca \"{controlNCA}\"";
|
||||
|
||||
ExecuteCommand($"{tools["hacPack"]} {args}", workdir);
|
||||
}
|
||||
|
||||
static string BuildFinalNSP(string origPath, string parentDir, string tmpPath, string titleID, string workdir)
|
||||
{
|
||||
string outputPath = origPath.Replace(".nsp", "_repacked.nsp");
|
||||
if (File.Exists(outputPath)) File.Delete(outputPath);
|
||||
|
||||
string args = $"-k \"{prodKeysPath}\" -o \"{parentDir}\" --titleid {titleID} " +
|
||||
$"--type nsp --ncadir \"{tmpPath}\"";
|
||||
|
||||
ExecuteCommand($"{tools["hacPack"]} {args}", workdir);
|
||||
File.Move(Path.Combine(parentDir, $"{titleID}.nsp"), outputPath);
|
||||
return outputPath;
|
||||
}
|
||||
|
||||
static string ParseNCAOutput(string output, string type)
|
||||
{
|
||||
var line = output.Split('\n')
|
||||
.FirstOrDefault(l => l.Contains($"Created {type} NCA:"));
|
||||
//return line?.Split(':').Last().Trim();
|
||||
return line?.Substring(line.IndexOf("NCA:") + "NCA:".Length).Trim();
|
||||
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55aa3f0466c30bc4683cdbdc4dd75940
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d90c85ddb14ad7e4e9a6242ba135da0b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b31e2ae7250c09548a777d4dcdfe2d1f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7be57cd4293e9dc4297ea9b83fe08b18
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e1252f6d74d67ee48af0a0342aecc981
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,152 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Application>
|
||||
<Title>
|
||||
<Language>AmericanEnglish</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>BritishEnglish</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>Japanese</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>French</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>German</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>LatinAmericanSpanish</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>Spanish</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>Italian</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>Dutch</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>CanadianFrench</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>Portuguese</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>Russian</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Isbn/>
|
||||
<StartupUserAccount>Required</StartupUserAccount>
|
||||
<UserAccountSwitchLock>Disable</UserAccountSwitchLock>
|
||||
<ParentalControl>None</ParentalControl>
|
||||
<SupportedLanguage>AmericanEnglish</SupportedLanguage>
|
||||
<SupportedLanguage>BritishEnglish</SupportedLanguage>
|
||||
<SupportedLanguage>Japanese</SupportedLanguage>
|
||||
<SupportedLanguage>French</SupportedLanguage>
|
||||
<SupportedLanguage>German</SupportedLanguage>
|
||||
<SupportedLanguage>LatinAmericanSpanish</SupportedLanguage>
|
||||
<SupportedLanguage>Spanish</SupportedLanguage>
|
||||
<SupportedLanguage>Italian</SupportedLanguage>
|
||||
<SupportedLanguage>Dutch</SupportedLanguage>
|
||||
<SupportedLanguage>CanadianFrench</SupportedLanguage>
|
||||
<SupportedLanguage>Russian</SupportedLanguage>
|
||||
<Screenshot>Allow</Screenshot>
|
||||
<VideoCapture>Disable</VideoCapture>
|
||||
<PresenceGroupId>0x0104444444441001</PresenceGroupId>
|
||||
<DisplayVersion>2.0</DisplayVersion>
|
||||
<Rating>
|
||||
<Organization>CERO</Organization>
|
||||
<Age>12</Age>
|
||||
</Rating>
|
||||
<Rating>
|
||||
<Organization>ESRB</Organization>
|
||||
<Age>10</Age>
|
||||
</Rating>
|
||||
<Rating>
|
||||
<Organization>USK</Organization>
|
||||
<Age>12</Age>
|
||||
</Rating>
|
||||
<Rating>
|
||||
<Organization>PEGI</Organization>
|
||||
<Age>12</Age>
|
||||
</Rating>
|
||||
<Rating>
|
||||
<Organization>PEGIPortugal</Organization>
|
||||
<Age>12</Age>
|
||||
</Rating>
|
||||
<Rating>
|
||||
<Organization>PEGIBBFC</Organization>
|
||||
<Age>12</Age>
|
||||
</Rating>
|
||||
<Rating>
|
||||
<Organization>Russian</Organization>
|
||||
<Age>12</Age>
|
||||
</Rating>
|
||||
<Rating>
|
||||
<Organization>ACB</Organization>
|
||||
<Age>13</Age>
|
||||
</Rating>
|
||||
<Rating>
|
||||
<Organization>OFLC</Organization>
|
||||
<Age>13</Age>
|
||||
</Rating>
|
||||
<DataLossConfirmation>Required</DataLossConfirmation>
|
||||
<PlayLogPolicy>All</PlayLogPolicy>
|
||||
<SaveDataOwnerId>0x0104444444441001</SaveDataOwnerId>
|
||||
<UserAccountSaveDataSize>0x0000000003e00000</UserAccountSaveDataSize>
|
||||
<UserAccountSaveDataJournalSize>0x0000000000180000</UserAccountSaveDataJournalSize>
|
||||
<DeviceSaveDataSize>0x0000000000000000</DeviceSaveDataSize>
|
||||
<DeviceSaveDataJournalSize>0x0000000000000000</DeviceSaveDataJournalSize>
|
||||
<BcatDeliveryCacheStorageSize>0x0000000000000000</BcatDeliveryCacheStorageSize>
|
||||
<ApplicationErrorCodeCategory/>
|
||||
<AddOnContentBaseId>0x0104444444442001</AddOnContentBaseId>
|
||||
<LogoType>Nintendo</LogoType>
|
||||
<LocalCommunicationId>0x0104444444441001</LocalCommunicationId>
|
||||
<LogoHandling>Auto</LogoHandling>
|
||||
<SeedForPseudoDeviceId>0x0000000000000000</SeedForPseudoDeviceId>
|
||||
<BcatPassphrase/>
|
||||
<AddOnContentRegistrationType>AllOnLaunch</AddOnContentRegistrationType>
|
||||
<UserAccountSaveDataSizeMax>0x0000000000000000</UserAccountSaveDataSizeMax>
|
||||
<UserAccountSaveDataJournalSizeMax>0x0000000000000000</UserAccountSaveDataJournalSizeMax>
|
||||
<DeviceSaveDataSizeMax>0x0000000000000000</DeviceSaveDataSizeMax>
|
||||
<DeviceSaveDataJournalSizeMax>0x0000000000000000</DeviceSaveDataJournalSizeMax>
|
||||
<TemporaryStorageSize>0x0000000000000000</TemporaryStorageSize>
|
||||
<CacheStorageSize>0x0000000000000000</CacheStorageSize>
|
||||
<CacheStorageJournalSize>0x0000000000000000</CacheStorageJournalSize>
|
||||
<CacheStorageDataAndJournalSizeMax>0x0000000000000000</CacheStorageDataAndJournalSizeMax>
|
||||
<CacheStorageIndexMax>0x0000000000000000</CacheStorageIndexMax>
|
||||
<Hdcp>None</Hdcp>
|
||||
<CrashReport>Deny</CrashReport>
|
||||
<RuntimeAddOnContentInstall>Deny</RuntimeAddOnContentInstall>
|
||||
<PlayLogQueryableApplicationId>0x0000000000000000</PlayLogQueryableApplicationId>
|
||||
<PlayLogQueryCapability>None</PlayLogQueryCapability>
|
||||
<Repair>None</Repair>
|
||||
<Attribute>None</Attribute>
|
||||
<ProgramIndex>0</ProgramIndex>
|
||||
<RequiredNetworkServiceLicenseOnLaunch>None</RequiredNetworkServiceLicenseOnLaunch>
|
||||
</Application>
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42c1295c31de3a948825b9e8e9e8184f
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b1b3ff7954facb409d3ba6f9840f762
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 409c6e8e5ead0ac4991ea6c243e407dd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08bd0c8a53daacb4ea23b14dde156354
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 292b282919a768c4fa6b8adb858daa95
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,118 @@
|
||||
#if UNITY_EDITOR
|
||||
using AxibugEmuOnline.Editors;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build.Reporting;
|
||||
using UnityEngine;
|
||||
|
||||
public static class AxiAutoBuild
|
||||
{
|
||||
[MenuItem("Axibug移植工具/AutoBuild/Build ALL")]
|
||||
public static void Build_Build_ALL()
|
||||
{
|
||||
Build_Global(BuildTarget.Android);
|
||||
Build_Global(BuildTarget.iOS);
|
||||
Build_Global(BuildTarget.StandaloneWindows);
|
||||
Build_Global(BuildTarget.StandaloneLinux64);
|
||||
Build_Global(BuildTarget.WSAPlayer);
|
||||
AxibugNSPTools.BuildWithRepackNSP();
|
||||
}
|
||||
|
||||
[MenuItem("Axibug移植工具/AutoBuild/Android")]
|
||||
public static void Build_Android()
|
||||
{
|
||||
Build_Global(BuildTarget.Android);
|
||||
}
|
||||
|
||||
[MenuItem("Axibug移植工具/AutoBuild/IOS")]
|
||||
public static void Build_IOS()
|
||||
{
|
||||
Build_Global(BuildTarget.iOS);
|
||||
}
|
||||
[MenuItem("Axibug移植工具/AutoBuild/PC")]
|
||||
public static void Build_PC()
|
||||
{
|
||||
Build_Global(BuildTarget.StandaloneWindows);
|
||||
}
|
||||
[MenuItem("Axibug移植工具/AutoBuild/Linux")]
|
||||
public static void Build_Linux64()
|
||||
{
|
||||
Build_Global(BuildTarget.StandaloneLinux64);
|
||||
}
|
||||
[MenuItem("Axibug移植工具/AutoBuild/UWP")]
|
||||
public static void Build_UWP()
|
||||
{
|
||||
Build_Global(BuildTarget.WSAPlayer);
|
||||
}
|
||||
|
||||
[MenuItem("Axibug移植工具/AutoBuild/EmbeddedLinux")]
|
||||
public static void Build_EmbeddedLinux()
|
||||
{
|
||||
Build_Global(BuildTarget.EmbeddedLinux);
|
||||
}
|
||||
|
||||
[MenuItem("Axibug移植工具/AutoBuild/Switch")]
|
||||
public static void Build_Switch()
|
||||
{
|
||||
AxibugNSPTools.BuildWithRepackNSP();
|
||||
}
|
||||
|
||||
|
||||
public static void Build_Global(BuildTarget target)
|
||||
{
|
||||
if (!EditorUtility.DisplayDialog("打包", $"确认打包{target}?", "继续", "取消"))
|
||||
return;
|
||||
|
||||
var levels = new List<string>();
|
||||
foreach (var scene in EditorBuildSettings.scenes)
|
||||
{
|
||||
if (scene.enabled)
|
||||
levels.Add(scene.path);
|
||||
}
|
||||
|
||||
var buildOpt = EditorUserBuildSettings.development ? BuildOptions.Development : BuildOptions.None;
|
||||
if (EditorUserBuildSettings.buildWithDeepProfilingSupport)
|
||||
buildOpt |= BuildOptions.EnableDeepProfilingSupport;
|
||||
if (EditorUserBuildSettings.allowDebugging)
|
||||
buildOpt |= BuildOptions.AllowDebugging;
|
||||
|
||||
string targetName = $"{Application.productName}_{DateTime.Now.ToString("yyyyMMddHHmmss")}";
|
||||
|
||||
targetName += target switch
|
||||
{
|
||||
BuildTarget.Android => ".apk",
|
||||
BuildTarget.iOS => ".ipa",
|
||||
BuildTarget.StandaloneWindows => ".exe",
|
||||
_ => "",
|
||||
};
|
||||
|
||||
|
||||
string _locationPathName = $"Output/{target}/{targetName}";
|
||||
string FullPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", _locationPathName));
|
||||
string dirPath = Path.GetDirectoryName(FullPath);
|
||||
if (!Directory.Exists(dirPath))
|
||||
Directory.CreateDirectory(dirPath);
|
||||
|
||||
var options = new BuildPlayerOptions
|
||||
{
|
||||
scenes = levels.ToArray(),
|
||||
locationPathName = _locationPathName,
|
||||
target = target,
|
||||
options = buildOpt
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
BuildReport report = BuildPipeline.BuildPlayer(options);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"[AutoBuild] Unity Build {target} 错误:{ex.ToString()}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3b8550d3919d1044b50eb20f0c50fb8
|
@ -0,0 +1,24 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
public class AxiPrefabCache : ScriptableObject
|
||||
{
|
||||
public List<AxiPrefabCache_Com2GUID> caches = new List<AxiPrefabCache_Com2GUID>();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class AxiPrefabCache_Com2GUID
|
||||
{
|
||||
public string SrcFullName;
|
||||
public string SrcName;
|
||||
public string GUID;
|
||||
public string ToName;
|
||||
public string ToPATH;
|
||||
public string ToGUID;
|
||||
public MonoScript monoScript;
|
||||
}
|
||||
#endif
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 47ae96bfd5393694da28c22ab2ccfb7c
|
@ -0,0 +1,42 @@
|
||||
#if UNITY_EDITOR
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
public class AxiProjectTools_AssetsAutoSetting : EditorWindow
|
||||
{
|
||||
[MenuItem("Axibug移植工具/AssetsAutoSetting/自动设置TextureMaxSize为图片大小1倍")]
|
||||
public static void AutoSettTextureSize_1x() { SetTextureSite(1f); }
|
||||
|
||||
[MenuItem("Axibug移植工具/AssetsAutoSetting/自动设置TextureMaxSize为图片大小2分之1倍")]
|
||||
public static void AutoSettTextureSize_1_2x() { SetTextureSite(1f / 2f); }
|
||||
[MenuItem("Axibug移植工具/AssetsAutoSetting/自动设置TextureMaxSize为图片大小4分之1倍")]
|
||||
public static void AutoSettTextureSize_1_4x() { SetTextureSite(1f / 4f); }
|
||||
|
||||
public static void SetTextureSite(float Scale)
|
||||
{
|
||||
Texture2D[] textures = Selection.GetFiltered<Texture2D>(SelectionMode.DeepAssets);
|
||||
if (textures.Length == 0)
|
||||
{
|
||||
Debug.LogWarning("请先选择目录,或者Texture资源");
|
||||
return;
|
||||
}
|
||||
|
||||
AssetDatabase.StartAssetEditing(); // 开启批量编辑模式
|
||||
foreach (var texture in textures)
|
||||
{
|
||||
string path = AssetDatabase.GetAssetPath(texture);
|
||||
TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter;
|
||||
if (importer != null)
|
||||
{
|
||||
int size = Mathf.Max(texture.width, texture.height);
|
||||
int maxsize = Mathf.ClosestPowerOfTwo((int)(size * Scale)); // Unity内置方法适配2的幂次方
|
||||
importer.maxTextureSize = maxsize;
|
||||
importer.SaveAndReimport();
|
||||
}
|
||||
}
|
||||
AssetDatabase.StopAssetEditing(); // 结束批量编辑
|
||||
Debug.Log($"Updated {textures.Length} textures.");
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc0741a79e74f96449a260406f239066
|
@ -0,0 +1,840 @@
|
||||
#if UNITY_EDITOR && UNITY_2020_1_OR_NEWER
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
public class AxiProjectToolsStatistics : EditorWindow
|
||||
{
|
||||
static string cachecfgPath = "Assets/AxiStatisticsDatas.asset";
|
||||
static Dictionary<string, AxiStatisticsDatas> dictTempData = new Dictionary<string, AxiStatisticsDatas>();
|
||||
|
||||
static void ClearTempData()
|
||||
{
|
||||
dictTempData.Clear();
|
||||
}
|
||||
static string GetRootTempKey(int type, string rootName)
|
||||
{
|
||||
return type + "_" + rootName;
|
||||
}
|
||||
|
||||
|
||||
// 添加Hierarchy右键菜单项
|
||||
[MenuItem("GameObject/AxiStatistics/GetAxiNodeHash", false, 10)]
|
||||
public static void GetAxiNodeHash()
|
||||
{
|
||||
// 获取当前右键选中的Transform
|
||||
Transform selectedTransform = Selection.activeTransform;
|
||||
if (selectedTransform != null)
|
||||
{
|
||||
Debug.Log("选中的对象"+selectedTransform.name+",Hash=>"+ GetNodeDataHash(selectedTransform,true));
|
||||
}
|
||||
}
|
||||
|
||||
static int GetNodeDataHash(Transform trans,bool bLog = false)
|
||||
{
|
||||
long hashplus = 0;
|
||||
hashplus += trans.position.GetHashCode();
|
||||
hashplus += trans.localPosition.GetHashCode();
|
||||
|
||||
|
||||
#if UNITY_2017_1_OR_NEWER
|
||||
int count = trans.childCount;
|
||||
#else
|
||||
int count = trans.GetChildCount();
|
||||
#endif
|
||||
hashplus += count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
hashplus += trans.GetChild(i).name.GetHashCode();
|
||||
}
|
||||
|
||||
|
||||
if (bLog)
|
||||
{
|
||||
//Debug.Log("trans.position.GetHashCode()=>" + trans.position.GetHashCode());
|
||||
//Debug.Log("trans.localPosition.GetHashCode()=>" + trans.localPosition.GetHashCode());
|
||||
//Debug.Log("childCount =>" + count);
|
||||
//Debug.Log("hashplus =>" + hashplus);
|
||||
//Debug.Log("hashplus.GetHashCode() =>" + hashplus.GetHashCode());
|
||||
}
|
||||
|
||||
return hashplus.GetHashCode();
|
||||
}
|
||||
static int GetNodeLinkListHash(List<AxiStatistics_Node_Link> nodes)
|
||||
{
|
||||
string hashplus = string.Empty;
|
||||
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
hashplus += node.Name;
|
||||
hashplus += node.Idx.ToString();
|
||||
}
|
||||
|
||||
return hashplus.GetHashCode();
|
||||
}
|
||||
|
||||
static string GetNodeLinkListStr(List<AxiStatistics_Node_Link> nodes)
|
||||
{
|
||||
string linkstr = string.Empty;
|
||||
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
linkstr += "/";
|
||||
linkstr += node.Name;
|
||||
linkstr += "[" + node.Idx + "]";
|
||||
}
|
||||
return linkstr;
|
||||
}
|
||||
|
||||
static void AddComponentData(int _type, string _rootPath, AxiStatistics_Node_Component _comdata, string _nodepath, Component lastcom)
|
||||
{
|
||||
string rootKey = GetRootTempKey(_type, _rootPath);
|
||||
|
||||
if (!dictTempData.ContainsKey(rootKey))
|
||||
{
|
||||
dictTempData[rootKey] = new AxiStatisticsDatas() { type = _type, FullPath = _rootPath, nodes = new List<AxiStatistics_Node>() };
|
||||
}
|
||||
AxiStatisticsDatas rootData = dictTempData[rootKey];
|
||||
|
||||
List<AxiStatistics_Node_Link> link = new List<AxiStatistics_Node_Link>();
|
||||
if (lastcom.transform.parent != null)
|
||||
{
|
||||
Transform currNode = lastcom.transform;
|
||||
while (currNode != null)
|
||||
{
|
||||
//最顶层了
|
||||
if (currNode.parent == null)
|
||||
{
|
||||
link.Insert(0, new AxiStatistics_Node_Link()
|
||||
{
|
||||
NodeHash = GetNodeDataHash(currNode),
|
||||
Idx = 0,
|
||||
OnlyOne = true,
|
||||
Name = currNode.gameObject.name
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
int thisNameAllCount = 0;
|
||||
int thisNodeIdx = -1;
|
||||
#if UNITY_2017_1_OR_NEWER
|
||||
int count = currNode.parent.childCount;
|
||||
#else
|
||||
int count = currNode.parent.GetChildCount();
|
||||
#endif
|
||||
bool bFind = false;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
GameObject checkGobj = currNode.parent.GetChild(i).gameObject;
|
||||
if (checkGobj.name == currNode.name)
|
||||
{
|
||||
thisNameAllCount++;
|
||||
if (checkGobj == currNode.gameObject)
|
||||
{
|
||||
thisNodeIdx = thisNameAllCount - 1;
|
||||
bFind = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bFind)
|
||||
{
|
||||
link.Insert(0, new AxiStatistics_Node_Link()
|
||||
{
|
||||
NodeHash = GetNodeDataHash(currNode),
|
||||
Idx = thisNodeIdx,
|
||||
OnlyOne = thisNameAllCount == 1,
|
||||
Name = currNode.gameObject.name
|
||||
});
|
||||
currNode = currNode.parent;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
link.Insert(0, new AxiStatistics_Node_Link()
|
||||
{
|
||||
NodeHash = GetNodeDataHash(lastcom.transform),
|
||||
Idx = 0,
|
||||
OnlyOne = true,
|
||||
Name = lastcom.gameObject.name
|
||||
});
|
||||
}
|
||||
|
||||
int linkhash = GetNodeLinkListHash(link);
|
||||
AxiStatistics_Node nodeData = rootData.nodes.Where(w => w.LinkHash == linkhash).FirstOrDefault();
|
||||
if (nodeData == null)
|
||||
{
|
||||
nodeData = new AxiStatistics_Node();
|
||||
nodeData.Name = Path.GetFileName(_nodepath);
|
||||
//nodeData.NodeFullPath = _nodepath;
|
||||
nodeData.components = new List<AxiStatistics_Node_Component>();
|
||||
//nodeData.NodeIdx = thisNodeIdx;
|
||||
//nodeData.NodeIdxOnlyOne = bNodeIdxOnlyOne;
|
||||
|
||||
nodeData.link = link;
|
||||
nodeData.LinkHash = linkhash;
|
||||
nodeData.LinkFullStr = GetNodeLinkListStr(link);
|
||||
|
||||
rootData.nodes.Add(nodeData);
|
||||
}
|
||||
|
||||
nodeData.components.Add(_comdata);
|
||||
}
|
||||
|
||||
static bool CheckCom(Component[] allcoms, int comRealIdx, int _type, string _rootPath, Component com, string nodepath)
|
||||
{
|
||||
if (com is BoxCollider2D)
|
||||
{
|
||||
BoxCollider2D bc = com as BoxCollider2D;
|
||||
#if UNITY_2017_1_OR_NEWER
|
||||
Debug.Log(nodepath + "BoxCollider2D->center=>(" + bc.offset.x + "," + bc.offset.y + ") size=>(" + bc.size.x + "," + bc.size.y + "");
|
||||
#else
|
||||
Debug.Log(nodepath +"BoxCollider2D->center=>("+ bc.center.x+","+bc.center.y+") size=>("+ bc.size.x+","+bc.size.y+"");
|
||||
#endif
|
||||
AxiStatistics_Node_Component _com = new AxiStatistics_Node_Component();
|
||||
_com.type = typeof(BoxCollider2D).ToString();
|
||||
#if UNITY_2017_1_OR_NEWER
|
||||
_com.center = bc.offset;
|
||||
#else
|
||||
_com.center = bc.center;
|
||||
#endif
|
||||
_com.size = bc.size;
|
||||
SetCompnentIdxNum<BoxCollider2D>(_com, allcoms, comRealIdx, bc);
|
||||
AddComponentData(_type, _rootPath, _com, nodepath, com);
|
||||
}
|
||||
if (com is Rigidbody2D)
|
||||
{
|
||||
Rigidbody2D rig2d = com as Rigidbody2D;
|
||||
Debug.Log(_rootPath + "Rigidbody2D->simulated=>(" + rig2d.simulated + ")");
|
||||
Debug.Log(_rootPath + "Rigidbody2D->IsSleeping=>(" + rig2d.isKinematic.ToString() + ")");
|
||||
|
||||
AxiStatistics_Node_Component _com = new AxiStatistics_Node_Component();
|
||||
_com.type = typeof(Rigidbody2D).ToString();
|
||||
_com.isKinematic = rig2d.isKinematic;
|
||||
_com.simulated = rig2d.simulated;
|
||||
_com.gravityScale = rig2d.gravityScale;
|
||||
SetCompnentIdxNum<Rigidbody2D>(_com, allcoms, comRealIdx, rig2d);
|
||||
AddComponentData(_type, _rootPath, _com, nodepath, com);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 找出同类Idx
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="allcoms"></param>
|
||||
/// <param name="comRealIdx"></param>
|
||||
/// <param name="com"></param>
|
||||
/// <returns></returns>
|
||||
static void SetCompnentIdxNum<T>(AxiStatistics_Node_Component _comData, Component[] allcoms, int comRealIdx, T com) where T : Component
|
||||
{
|
||||
int ComIdxNum;
|
||||
bool ComTypeIsOnlyOne = false;
|
||||
int TCount = com.transform.GetComponents<T>().Length;
|
||||
if (TCount == 1)
|
||||
{
|
||||
ComIdxNum = 0;
|
||||
ComTypeIsOnlyOne = true;
|
||||
}
|
||||
else if (TCount < 1)
|
||||
{
|
||||
Debug.LogError("找不到,不应该");
|
||||
ComIdxNum = -1;
|
||||
}
|
||||
|
||||
ComIdxNum = -1;
|
||||
for (int i = 0; i < allcoms.Length; i++)
|
||||
{
|
||||
//他自己自然是了
|
||||
if (i == comRealIdx)
|
||||
{
|
||||
ComIdxNum++;
|
||||
break;
|
||||
}
|
||||
if (allcoms[i] is T)
|
||||
ComIdxNum++;
|
||||
}
|
||||
|
||||
_comData.ComIdxNum = ComIdxNum;
|
||||
_comData.ComTypeOnlyOne = ComTypeIsOnlyOne;
|
||||
}
|
||||
|
||||
[MenuItem("Axibug移植工具/Statistics/[1]统计所有预制体和场景下的Collider和RigBody")]
|
||||
|
||||
public static void StatisticsCollider()
|
||||
{
|
||||
ClearTempData();
|
||||
StatisticsCollider<BoxCollider2D>();
|
||||
StatisticsCollider<Rigidbody2D>();
|
||||
|
||||
AxiStatisticsCache cache = ScriptableObject.CreateInstance<AxiStatisticsCache>();
|
||||
foreach (var data in dictTempData)
|
||||
cache.caches.Add(data.Value);
|
||||
AssetDatabase.CreateAsset(cache, cachecfgPath);
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
public static void StatisticsCollider<T>() where T : Component
|
||||
{
|
||||
AxiProjectTools.GoTAxiProjectToolsSence();
|
||||
|
||||
string[] sceneGuids = AssetDatabase.FindAssets("t:scene");
|
||||
foreach (string guid in sceneGuids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
if (path.Contains(AxiProjectTools.toolSenceName))
|
||||
continue;
|
||||
|
||||
#if UNITY_4_6
|
||||
EditorApplication.OpenScene(path);
|
||||
#else
|
||||
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(path);
|
||||
#endif
|
||||
|
||||
// 创建一个列表来存储根节点
|
||||
List<GameObject> rootNodes = new List<GameObject>();
|
||||
|
||||
// 遍历场景中的所有对象
|
||||
GameObject[] allObjects = FindObjectsOfType<GameObject>();
|
||||
foreach (GameObject obj in allObjects)
|
||||
{
|
||||
// 检查对象是否有父对象
|
||||
if (obj.transform.parent == null)
|
||||
{
|
||||
// 如果没有父对象,则它是一个根节点
|
||||
rootNodes.Add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var node in rootNodes)
|
||||
LoopPrefabNode<T>(0, path, path, node, 0);
|
||||
|
||||
}
|
||||
string[] prefabGuids = AssetDatabase.FindAssets("t:Prefab");
|
||||
foreach (string guid in prefabGuids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
GetPrefab<T>(path);
|
||||
}
|
||||
|
||||
AxiProjectTools.GoTAxiProjectToolsSence();
|
||||
Debug.Log("<Color=#FFF333>处理完毕 统计所有预制体和场景下的" + typeof(T).FullName + "</color>");
|
||||
}
|
||||
|
||||
static void GetPrefab<T>(string path) where T : Component
|
||||
{
|
||||
#if UNITY_4_6
|
||||
GameObject prefab = AssetDatabase.LoadAssetAtPath(path,typeof(GameObject)) as GameObject;
|
||||
#else
|
||||
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
||||
#endif
|
||||
|
||||
LoopPrefabNode<T>(1, path, path, prefab.gameObject, 0);
|
||||
}
|
||||
|
||||
static void LoopPrefabNode<T>(int _type, string _rootPath, string noderootPath, GameObject trans, int depth) where T : Component
|
||||
{
|
||||
// #if UNITY_2018_4_OR_NEWER
|
||||
string nodename = noderootPath + "/" + trans.name;
|
||||
GameObject prefabRoot = trans.gameObject;
|
||||
|
||||
Component[] components = prefabRoot.GetComponents<Component>();
|
||||
for (int i = 0; i < components.Length; i++)
|
||||
{
|
||||
var com = components[i];
|
||||
|
||||
if (com == null)
|
||||
continue;
|
||||
|
||||
T comobj = com as T;
|
||||
if (comobj == null)
|
||||
continue;
|
||||
|
||||
if (CheckCom(components, i, _type, _rootPath, comobj, nodename))
|
||||
continue;
|
||||
}
|
||||
|
||||
//遍历
|
||||
foreach (Transform child in trans.transform)
|
||||
LoopPrefabNode<T>(_type, _rootPath, nodename, child.gameObject, depth + 1);
|
||||
//#else
|
||||
// Debug.Log("低版本不要执行本函数");
|
||||
//#endif
|
||||
}
|
||||
|
||||
#if UNITY_2017_1_OR_NEWER
|
||||
[MenuItem("Axibug移植工具/Statistics/[2]通过记录,对组件进行修补")]
|
||||
public static void RepairRigBodyByStatistics()
|
||||
{
|
||||
List<string> errLog = new List<string>();
|
||||
List<string> doneLog = new List<string>();
|
||||
List<ValueTuple<string, string>> NeedRepair = new List<ValueTuple<string, string>>();
|
||||
List<ValueTuple<string, string>> FinishRepair = new List<ValueTuple<string, string>>();
|
||||
string CurrScenePath = string.Empty;
|
||||
AxiProjectTools.GoTAxiProjectToolsSence();
|
||||
#if UNITY_4_6
|
||||
AxiStatisticsCache data = AssetDatabase.LoadAssetAtPath(cachecfgPath,typeof(AxiStatisticsCache)) as AxiStatisticsCache;
|
||||
#else
|
||||
AxiStatisticsCache data = AssetDatabase.LoadAssetAtPath<AxiStatisticsCache>(cachecfgPath);
|
||||
#endif
|
||||
string[] sceneGuids = AssetDatabase.FindAssets("t:scene");
|
||||
List<string> ScenePath = new List<string>();
|
||||
List<string> SceneName = new List<string>();
|
||||
foreach (string guid in sceneGuids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
if (path.Contains(AxiProjectTools.toolSenceName))
|
||||
continue;
|
||||
ScenePath.Add(path);
|
||||
SceneName.Add(Path.GetFileName(path));
|
||||
}
|
||||
|
||||
string[] prefabGuids = AssetDatabase.FindAssets("t:prefab");
|
||||
List<string> prefabPath = new List<string>();
|
||||
List<string> prefabName = new List<string>();
|
||||
foreach (string guid in prefabGuids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
prefabPath.Add(path);
|
||||
prefabName.Add(Path.GetFileName(path));
|
||||
}
|
||||
|
||||
foreach (var cache in data.caches.OrderBy(w => w.type))
|
||||
{
|
||||
//场景
|
||||
if (cache.type == 0)
|
||||
{
|
||||
#region 场景加载
|
||||
string targetName = Path.GetFileName(cache.FullPath);
|
||||
int Idx = SceneName.IndexOf(targetName);
|
||||
if (Idx < 0)
|
||||
{
|
||||
Debug.LogError(targetName + "[Repair]找不到对应资源");
|
||||
continue;
|
||||
}
|
||||
string targetpath = ScenePath[Idx];
|
||||
|
||||
//保证场景切换
|
||||
if (!string.Equals(CurrScenePath, targetpath))
|
||||
{
|
||||
#if UNITY_4_6
|
||||
EditorApplication.OpenScene(targetpath);
|
||||
#else
|
||||
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(targetpath);
|
||||
#endif
|
||||
}
|
||||
CurrScenePath = targetpath;
|
||||
#endregion
|
||||
|
||||
int DirtyCount = 0;
|
||||
foreach (var node in cache.nodes)
|
||||
{
|
||||
GameObject targetNodePathObj = GetNodeByLink(cache.FullPath, node.link, out string errStr);
|
||||
if (targetNodePathObj == null)
|
||||
{
|
||||
errLog.Add(errStr);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
string targetNodePath = node.NodeFullPath.Substring(cache.FullPath.Length, node.NodeFullPath.Length - cache.FullPath.Length);
|
||||
|
||||
//GameObject targetNodePathObj = GameObject.Find(targetNodePath);
|
||||
GameObject targetNodePathObj = GetNodeByIdx(node, targetNodePath);
|
||||
|
||||
if (targetNodePathObj == null)
|
||||
{
|
||||
string err = "[Repair]" + node.NodeFullPath + "找不到对应节点";
|
||||
errLog.Add(err);
|
||||
Debug.LogError(err);
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
foreach (var com in node.components)
|
||||
{
|
||||
if (RepairComponent(node.LinkFullStr, targetNodePathObj, com, out var errlog))
|
||||
{
|
||||
NeedRepair.Add(new ValueTuple<string, string>($"{cache.FullPath}:{node.LinkFullStr}", $"{com.type}[{com.ComIdxNum}]"));
|
||||
DirtyCount++;
|
||||
}
|
||||
errLog.AddRange(errlog);
|
||||
}
|
||||
}
|
||||
if (DirtyCount > 0)
|
||||
{
|
||||
Debug.Log($"[Repair][场景处理]{cache.FullPath}共{DirtyCount}个需要处理");
|
||||
|
||||
|
||||
// 获取当前打开的场景
|
||||
var activeScene = UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene();
|
||||
|
||||
// 保存场景
|
||||
UnityEditor.SceneManagement.EditorSceneManager.SaveScene(activeScene);
|
||||
|
||||
Debug.Log("场景已保存: " + activeScene.path);
|
||||
string donestr = $"[Repair][场景处理成功]{targetpath},共{DirtyCount}个";
|
||||
doneLog.Add(donestr);
|
||||
|
||||
}
|
||||
}
|
||||
else if (cache.type == 1)
|
||||
{
|
||||
string targetpath = cache.FullPath;
|
||||
|
||||
//来到空场景
|
||||
if (!string.IsNullOrEmpty(CurrScenePath))
|
||||
{
|
||||
AxiProjectTools.GoTAxiProjectToolsSence();
|
||||
CurrScenePath = string.Empty;
|
||||
}
|
||||
|
||||
|
||||
GameObject prefabInstance = AssetDatabase.LoadAssetAtPath<GameObject>(targetpath);
|
||||
if (prefabInstance == null)
|
||||
{
|
||||
Debug.LogError($"[Repair]Failed to load prefab at path: {prefabPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
var obj = GameObject.Instantiate(prefabInstance, null);
|
||||
|
||||
int DirtyCount = 0;
|
||||
foreach (var node in cache.nodes)
|
||||
{
|
||||
GameObject targetNodePathObj = GetNodeByLink(cache.FullPath, node.link, out string errStr, obj);
|
||||
|
||||
if (targetNodePathObj == null)
|
||||
{
|
||||
errLog.Add(errStr);
|
||||
continue;
|
||||
}
|
||||
//if (node.NodeFullPath == targetpath + "/" + Path.GetFileNameWithoutExtension(targetpath))
|
||||
//{
|
||||
// //预制体自己就是目标
|
||||
// targetNodePathObj = obj;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// string targetNodePath = node.NodeFullPath.Substring(cache.FullPath.Length + prefabInstance.name.Length + 2, node.NodeFullPath.Length - cache.FullPath.Length - prefabInstance.name.Length - 2);
|
||||
// //targetNodePathObj = obj.transform.Find(targetNodePath)?.gameObject;
|
||||
// targetNodePathObj = GetNodeByIdx(node, targetNodePath, obj);
|
||||
|
||||
// if (targetNodePathObj == null)
|
||||
// {
|
||||
// Debug.LogError("[Repair]" + targetNodePath + "找不到对应节点");
|
||||
// continue;
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
foreach (var com in node.components)
|
||||
{
|
||||
if (RepairComponent(node.LinkFullStr, targetNodePathObj, com, out var errlog))
|
||||
{
|
||||
NeedRepair.Add(new ValueTuple<string, string>($"{cache.FullPath}:{node.LinkFullStr}", $"{com.type}[{com.ComIdxNum}]"));
|
||||
DirtyCount++;
|
||||
}
|
||||
|
||||
errLog.AddRange(errlog);
|
||||
}
|
||||
}
|
||||
|
||||
if (DirtyCount > 0)
|
||||
{
|
||||
Debug.Log($"[Repair][预制体处理]{targetpath}共{DirtyCount}个需要处理");
|
||||
PrefabUtility.SaveAsPrefabAsset(obj, targetpath);
|
||||
string donestr = $"[Repair][预制体处理成功]{targetpath},共{DirtyCount}个";
|
||||
doneLog.Add(donestr);
|
||||
}
|
||||
GameObject.DestroyImmediate(obj);
|
||||
}
|
||||
}
|
||||
|
||||
AxiProjectTools.GoTAxiProjectToolsSence();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine("[Repair][统计]:");
|
||||
sb.AppendLine("----处理成功----");
|
||||
foreach (var val in doneLog.OrderBy(w => w))
|
||||
{
|
||||
sb.AppendLine(val);
|
||||
}
|
||||
sb.AppendLine("----异常统计----");
|
||||
foreach (var val in errLog.OrderBy(w => w))
|
||||
{
|
||||
sb.AppendLine(val);
|
||||
}
|
||||
sb.AppendLine("----需要处理----");
|
||||
foreach (var val in NeedRepair.OrderBy(w => w.Item1))
|
||||
{
|
||||
sb.AppendLine($"{val.Item1}=>{val.Item2}");
|
||||
}
|
||||
Debug.Log($"{sb}");
|
||||
|
||||
File.WriteAllText("Assets/AxiNeedRepair.txt", sb.ToString());
|
||||
}
|
||||
|
||||
|
||||
// static GameObject GetNodeByIdx(AxiStatistics_Node nodedata, string targetNodePath, GameObject root = null)
|
||||
// {
|
||||
// GameObject targetNodePathObj;
|
||||
|
||||
// if (root == null)
|
||||
// targetNodePathObj = GameObject.Find(targetNodePath);
|
||||
// else
|
||||
// targetNodePathObj = root.transform.Find(targetNodePath)?.gameObject;
|
||||
|
||||
// if (targetNodePathObj == null)
|
||||
// return null;
|
||||
|
||||
// string targetName = targetNodePathObj.name;
|
||||
// int currIdx = -1;
|
||||
// if (!nodedata.NodeIdxOnlyOne)
|
||||
// {
|
||||
// if (targetNodePathObj.transform.parent != null)
|
||||
// {
|
||||
//#if UNITY_2017_1_OR_NEWER
|
||||
// int count = targetNodePathObj.transform.parent.childCount;
|
||||
//#else
|
||||
// int count = targetNodePathObj.transform.parent.GetChildCount();
|
||||
//#endif
|
||||
// for (int i = 0; i < count; i++)
|
||||
// {
|
||||
// GameObject checkGobj = targetNodePathObj.transform.parent.GetChild(i).gameObject;
|
||||
// if (checkGobj.name == targetName)
|
||||
// {
|
||||
// currIdx++;
|
||||
// if (nodedata.NodeIdx == currIdx)
|
||||
// {
|
||||
// targetNodePathObj = checkGobj;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// return targetNodePathObj;
|
||||
// }
|
||||
|
||||
|
||||
static GameObject GetNodeByLink(string rootPath, List<AxiStatistics_Node_Link> linklist, out string errStr, GameObject PrefabRoot = null)
|
||||
{
|
||||
List<AxiStatistics_Node_Link> temp_useddlink = new List<AxiStatistics_Node_Link>();
|
||||
if (linklist.Count < 1)
|
||||
{
|
||||
errStr = $"[Repair] Link 为空";
|
||||
Debug.LogError(errStr);
|
||||
return null;
|
||||
}
|
||||
|
||||
temp_useddlink.Add(linklist[0]);
|
||||
GameObject currRoot;
|
||||
|
||||
if (PrefabRoot == null)
|
||||
currRoot = GameObject.Find(linklist[0].Name);
|
||||
else
|
||||
{
|
||||
currRoot = PrefabRoot;
|
||||
//currRoot = PrefabRoot.transform.Find(linklist[0].Name)?.gameObject;
|
||||
}
|
||||
|
||||
if (currRoot == null)
|
||||
{
|
||||
errStr = $"[Repair] 根节点找不到{rootPath}:{GetNodeLinkListStr(linklist)} => null";
|
||||
Debug.LogError(errStr);
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int link_i = 1; link_i < linklist.Count; link_i++)
|
||||
{
|
||||
AxiStatistics_Node_Link targetLink = linklist[link_i];
|
||||
temp_useddlink.Add(targetLink);
|
||||
GameObject findNode = null;
|
||||
#if UNITY_2017_1_OR_NEWER
|
||||
int count = currRoot.transform.childCount;
|
||||
#else
|
||||
int count = currNode.transform.GetChildCount();
|
||||
#endif
|
||||
|
||||
if (targetLink.OnlyOne)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
GameObject checkGobj = currRoot.transform.GetChild(i).gameObject;
|
||||
if (checkGobj.name == targetLink.Name)
|
||||
{
|
||||
findNode = checkGobj;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Dictionary<int, GameObject> tempHash2Node = new Dictionary<int, GameObject>();
|
||||
List<GameObject> tempGobjList = new List<GameObject>();
|
||||
bool HashDrity = false;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
GameObject checkGobj = currRoot.transform.GetChild(i).gameObject;
|
||||
if (checkGobj.name == targetLink.Name)
|
||||
{
|
||||
int temphash = GetNodeDataHash(checkGobj.transform);
|
||||
if (!tempHash2Node.ContainsKey(temphash))
|
||||
{
|
||||
tempHash2Node.Add(GetNodeDataHash(checkGobj.transform), checkGobj);
|
||||
}
|
||||
else
|
||||
{
|
||||
HashDrity = true;
|
||||
}
|
||||
tempGobjList.Add(checkGobj);
|
||||
}
|
||||
}
|
||||
|
||||
//Hash严格模式
|
||||
if (!HashDrity && tempHash2Node.TryGetValue(targetLink.NodeHash, out var val))
|
||||
{
|
||||
findNode = val;
|
||||
}
|
||||
//下标模式
|
||||
else
|
||||
{
|
||||
if (targetLink.Idx < 0 || tempGobjList.Count == 0 || (tempGobjList.Count != 0 && targetLink.Idx >= tempGobjList.Count))
|
||||
{
|
||||
errStr = $"[Repair]link 下标模式 找不到=>{rootPath}:{GetNodeLinkListStr(temp_useddlink)}[{targetLink.Idx}] => 完整链路{rootPath}:{GetNodeLinkListStr(linklist)}";
|
||||
Debug.LogError(errStr);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
findNode = tempGobjList[targetLink.Idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
currRoot = findNode;
|
||||
if (currRoot == null)
|
||||
break;
|
||||
}
|
||||
|
||||
if (currRoot == null)
|
||||
{
|
||||
errStr = $"[Repair]link 找不到[{rootPath}:{GetNodeLinkListStr(temp_useddlink)}] => 完整链路{rootPath}:{GetNodeLinkListStr(linklist)}";
|
||||
Debug.LogError(errStr);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
errStr = string.Empty;
|
||||
return currRoot;
|
||||
}
|
||||
}
|
||||
|
||||
static bool RepairComponent(string NodePath, GameObject targetNodePathObj, AxiStatistics_Node_Component comdata, out List<string> Errlog)
|
||||
{
|
||||
Errlog = new List<string>();
|
||||
string err;
|
||||
bool Dirty = false;
|
||||
if (comdata.type == typeof(Rigidbody2D).ToString())
|
||||
{
|
||||
Rigidbody2D rg2d = GetCompnentById<Rigidbody2D>(targetNodePathObj, comdata);
|
||||
if (rg2d == null)
|
||||
{
|
||||
err = $"[Repair]{NodePath}=> Rigidbody2D[{comdata.ComIdxNum}] == null";
|
||||
Debug.LogError(err);
|
||||
Errlog.Add(err);
|
||||
Dirty = false;
|
||||
}
|
||||
|
||||
/*
|
||||
与新版Unity的差异
|
||||
无BodyType选项:Unity 4.6.7中所有Rigidbody2D默认等效于新版的Dynamic类型(受重力影响),但无法设置为Static或Kinematic。
|
||||
无Simulated选项:只要物体启用了Rigidbody2D组件且Gravity Scale > 0,就会受重力作用。
|
||||
|
||||
所以,一旦老版本gravityScale > 0,就受重力作用,直接设置新版本这边:simulated = true;bodyType = RigidbodyType2D.Dynamic;
|
||||
*/
|
||||
|
||||
|
||||
if (rg2d.gravityScale != comdata.gravityScale)
|
||||
{
|
||||
Debug.Log($"[Repair]{NodePath}=> Rigidbody2D[{comdata.ComIdxNum}] simulated:{rg2d.gravityScale} != :{comdata.gravityScale} rg2d.bodyType => {rg2d.bodyType} ");
|
||||
|
||||
rg2d.gravityScale = comdata.gravityScale;
|
||||
Dirty = true;
|
||||
}
|
||||
|
||||
//if (rg2d.gravityScale > 0 && (!rg2d.simulated || rg2d.bodyType != RigidbodyType2D.Dynamic))
|
||||
if (!rg2d.simulated || rg2d.bodyType != RigidbodyType2D.Dynamic)
|
||||
{
|
||||
Debug.Log($"[Repair]{NodePath}=> Rigidbody2D[{comdata.ComIdxNum}] simulated:{rg2d.simulated} != :{comdata.simulated} rg2d.bodyType => {rg2d.bodyType} ");
|
||||
|
||||
rg2d.simulated = true;
|
||||
rg2d.bodyType = RigidbodyType2D.Dynamic;
|
||||
Dirty = true;
|
||||
}
|
||||
}
|
||||
else if (comdata.type == typeof(BoxCollider2D).ToString())
|
||||
{
|
||||
BoxCollider2D bc = GetCompnentById<BoxCollider2D>(targetNodePathObj, comdata);
|
||||
if (bc == null)
|
||||
{
|
||||
err = $"[Repair]{NodePath}=> BoxCollider2D[{comdata.ComIdxNum}] == null";
|
||||
Debug.LogError(err);
|
||||
Errlog.Add(err);
|
||||
Dirty = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bc.size != comdata.size)
|
||||
{
|
||||
Debug.Log($"[Repair]{NodePath} BoxCollider2D[{comdata.ComIdxNum}] => size:{bc.size} != {comdata.size} ");
|
||||
bc.size = comdata.size;
|
||||
Dirty = true;
|
||||
}
|
||||
if (bc.offset != comdata.center)
|
||||
{
|
||||
Debug.Log($"[Repair]{NodePath} BoxCollider2D[{comdata.ComIdxNum}] => offset:{bc.offset} != center{comdata.center} ");
|
||||
bc.offset = comdata.center;
|
||||
Dirty = true;
|
||||
}
|
||||
|
||||
if (Dirty)
|
||||
{
|
||||
bc.size = comdata.size;
|
||||
bc.offset = comdata.center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return Dirty;
|
||||
}
|
||||
|
||||
static T GetCompnentById<T>(GameObject gobj, AxiStatistics_Node_Component node) where T : Component
|
||||
{
|
||||
if (node.ComIdxNum == 0)
|
||||
return gobj.GetComponent<T>();
|
||||
else if (node.ComIdxNum > 0)
|
||||
{
|
||||
T[] coms = gobj.GetComponents<T>();
|
||||
if (node.ComIdxNum < coms.Length)
|
||||
return coms[node.ComIdxNum];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32aabdd304d2c4d47b8ef660f672ead1
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,506 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
public class AxiProjectTools : EditorWindow
|
||||
{
|
||||
static string cachecfgPath = "Assets/AxiComToolCache.asset";
|
||||
public static string toolSenceName = "AxiProjectTools";
|
||||
public static string outCsDir = Application.dataPath + "/AxiCom/";
|
||||
public static Dictionary<string, AxiPrefabCache_Com2GUID> ComType2GUID = new Dictionary<string, AxiPrefabCache_Com2GUID>();
|
||||
|
||||
public static void GoTAxiProjectToolsSence()
|
||||
{
|
||||
string[] sceneGuids = AssetDatabase.FindAssets("t:scene");
|
||||
foreach (string guid in sceneGuids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
if (path.Contains(toolSenceName))
|
||||
{
|
||||
#if UNITY_4_6
|
||||
EditorApplication.OpenScene(path);
|
||||
#else
|
||||
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(path);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Axibug移植工具/ToLowVersionUnity/[1]采集所有预制体和场景下的UGUI组件")]
|
||||
public static void Part1()
|
||||
{
|
||||
GoTAxiProjectToolsSence();
|
||||
ComType2GUID.Clear();
|
||||
string[] sceneGuids = AssetDatabase.FindAssets("t:scene");
|
||||
foreach (string guid in sceneGuids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
if (path.Contains(toolSenceName))
|
||||
continue;
|
||||
|
||||
#if UNITY_4_6
|
||||
EditorApplication.OpenScene(path);
|
||||
#else
|
||||
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(path);
|
||||
#endif
|
||||
|
||||
// 创建一个列表来存储根节点
|
||||
List<GameObject> rootNodes = new List<GameObject>();
|
||||
|
||||
// 遍历场景中的所有对象
|
||||
GameObject[] allObjects = FindObjectsOfType<GameObject>();
|
||||
foreach (GameObject obj in allObjects)
|
||||
{
|
||||
// 检查对象是否有父对象
|
||||
if (obj.transform.parent == null)
|
||||
{
|
||||
// 如果没有父对象,则它是一个根节点
|
||||
rootNodes.Add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var node in rootNodes)
|
||||
LoopPrefabNode(path, node, 0);
|
||||
}
|
||||
|
||||
|
||||
string[] prefabGuids = AssetDatabase.FindAssets("t:Prefab");
|
||||
foreach (string guid in prefabGuids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
GetPrefab(path);
|
||||
}
|
||||
|
||||
AxiPrefabCache cache = ScriptableObject.CreateInstance<AxiPrefabCache>();
|
||||
foreach (var data in ComType2GUID)
|
||||
cache.caches.Add(data.Value);
|
||||
AssetDatabase.CreateAsset(cache, cachecfgPath);
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
GoTAxiProjectToolsSence();
|
||||
Debug.Log("<Color=#FFF333>处理完毕 [1]采集所有预制体和场景下的UGUI组件</color>");
|
||||
}
|
||||
|
||||
static void GetPrefab(string path)
|
||||
{
|
||||
#if UNITY_4_6
|
||||
GameObject prefab = AssetDatabase.LoadAssetAtPath(path,typeof(GameObject)) as GameObject;
|
||||
#else
|
||||
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
||||
#endif
|
||||
|
||||
LoopPrefabNode(path, prefab.gameObject, 0);
|
||||
}
|
||||
static void LoopPrefabNode(string rootPath, GameObject trans, int depth)
|
||||
{
|
||||
// #if UNITY_2018_4_OR_NEWER
|
||||
string nodename = rootPath + trans.name;
|
||||
GameObject prefabRoot = trans.gameObject;
|
||||
|
||||
Component[] components = prefabRoot.GetComponents<Component>();
|
||||
for (int i = 0; i < components.Length; i++)
|
||||
{
|
||||
var com = components[i];
|
||||
|
||||
if (com == null)
|
||||
continue;
|
||||
|
||||
MonoBehaviour monoCom = com as MonoBehaviour;
|
||||
if (monoCom == null)
|
||||
continue;
|
||||
Type monoType = monoCom.GetType();
|
||||
if (!monoType.Assembly.FullName.Contains("UnityEngine.UI"))
|
||||
continue;
|
||||
// 获取MonoScript资源
|
||||
MonoScript monoScript = MonoScript.FromMonoBehaviour(monoCom);
|
||||
if (monoScript != null)
|
||||
{
|
||||
// 获取MonoScript资源的GUID
|
||||
string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(monoScript));
|
||||
Debug.Log(nodename+" | <color=#FFF333>["+monoType.Name+"]</color> <color=#FF0000>"+guid+"</color><color=#00FF00>("+monoType.FullName+")</color>");
|
||||
ComType2GUID[monoType.FullName] =
|
||||
new AxiPrefabCache_Com2GUID()
|
||||
{
|
||||
SrcFullName = monoType.FullName,
|
||||
SrcName = monoType.Name,
|
||||
GUID = guid,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("!!!! 没得");
|
||||
}
|
||||
}
|
||||
|
||||
//遍历
|
||||
foreach (Transform child in trans.transform)
|
||||
LoopPrefabNode(nodename, child.gameObject, depth + 1);
|
||||
//#else
|
||||
// Debug.Log("低版本不要执行本函数");
|
||||
//#endif
|
||||
}
|
||||
|
||||
[MenuItem("Axibug移植工具/ToLowVersionUnity/[2]生成中间脚本代码")]
|
||||
public static void Part2()
|
||||
{
|
||||
|
||||
#if UNITY_4_6
|
||||
if(System.IO.Directory.Exists(outCsDir))
|
||||
System.IO.Directory.Delete(outCsDir);
|
||||
#else
|
||||
if (UnityEngine.Windows.Directory.Exists(outCsDir))
|
||||
UnityEngine.Windows.Directory.Delete(outCsDir);
|
||||
#endif
|
||||
|
||||
|
||||
Directory.CreateDirectory(outCsDir);
|
||||
|
||||
|
||||
|
||||
#if UNITY_4_6
|
||||
AxiPrefabCache cache = AssetDatabase.LoadAssetAtPath(cachecfgPath,typeof(AxiPrefabCache)) as AxiPrefabCache;
|
||||
#else
|
||||
AxiPrefabCache cache = AssetDatabase.LoadAssetAtPath<AxiPrefabCache>(cachecfgPath);
|
||||
#endif
|
||||
foreach (var data in cache.caches)
|
||||
{
|
||||
string toName = "Axi" + data.SrcName;
|
||||
string toPath = outCsDir + toName + ".cs";
|
||||
string codeStr = "namespace AxibugCom { public class " + toName + " : " + data.SrcFullName + " {} }";
|
||||
try
|
||||
{
|
||||
System.IO.File.WriteAllText(toPath, codeStr);
|
||||
data.ToName = toName;
|
||||
data.ToPATH = toPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError("写入失败" + ex.ToString());
|
||||
}
|
||||
}
|
||||
Debug.Log("写入完毕");
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
Debug.Log("<Color=#FFF333>处理完毕 [2]生成中间脚本代码</color>");
|
||||
}
|
||||
|
||||
[MenuItem("Axibug移植工具/ToLowVersionUnity/[3]收集生成的脚本")]
|
||||
public static void Part3()
|
||||
{
|
||||
#if UNITY_4_6
|
||||
AxiPrefabCache cache = AssetDatabase.LoadAssetAtPath(cachecfgPath,typeof(AxiPrefabCache)) as AxiPrefabCache;
|
||||
MonoScript[] allMonoScripts = (MonoScript[])Resources.FindObjectsOfTypeAll(typeof(MonoScript));
|
||||
#else
|
||||
AxiPrefabCache cache = AssetDatabase.LoadAssetAtPath<AxiPrefabCache>(cachecfgPath);
|
||||
List<MonoScript> allMonoScripts = FindAllAssetsOfType<MonoScript>();
|
||||
#endif
|
||||
|
||||
foreach (var data in cache.caches)
|
||||
{
|
||||
MonoScript monoScript = allMonoScripts.FirstOrDefault(w => w.name == data.ToName);
|
||||
if (monoScript == null)
|
||||
{
|
||||
Debug.LogError("没找到" + data.ToName);
|
||||
continue;
|
||||
}
|
||||
string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(monoScript));
|
||||
data.ToGUID = guid;
|
||||
data.monoScript = monoScript;
|
||||
}
|
||||
Debug.Log("写入完毕");
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
Debug.Log("<Color=#FFF333>处理完毕 [3]收集生成的脚本</color>");
|
||||
}
|
||||
|
||||
static List<T> FindAllAssetsOfType<T>() where T : UnityEngine.Object
|
||||
{
|
||||
List<T> assets = new List<T>();
|
||||
|
||||
string[] allGuids = AssetDatabase.FindAssets("");
|
||||
foreach (string guid in allGuids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
if (path.EndsWith(".cs") || path.EndsWith(".js") || path.EndsWith(".boo")) // Unity支持多种脚本语言,但现代Unity主要使用C#
|
||||
{
|
||||
#if UNITY_4_6
|
||||
T asset = AssetDatabase.LoadAssetAtPath(cachecfgPath,typeof(T)) as T;
|
||||
#else
|
||||
T asset = AssetDatabase.LoadAssetAtPath<T>(path);
|
||||
#endif
|
||||
|
||||
if (asset != null)
|
||||
{
|
||||
assets.Add(asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
return assets;
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("Axibug移植工具/ToLowVersionUnity/[4]替换所有预制体和场景中的组件")]
|
||||
public static void Part4()
|
||||
{
|
||||
|
||||
#if UNITY_4_6
|
||||
AxiPrefabCache cache = AssetDatabase.LoadAssetAtPath(cachecfgPath,typeof(AxiPrefabCache)) as AxiPrefabCache;
|
||||
#else
|
||||
AxiPrefabCache cache = AssetDatabase.LoadAssetAtPath<AxiPrefabCache>(cachecfgPath);
|
||||
#endif
|
||||
|
||||
Dictionary<string, string> tempReplaceDict = new Dictionary<string, string>();
|
||||
foreach (var data in cache.caches)
|
||||
{
|
||||
tempReplaceDict[data.GUID] = data.ToGUID;
|
||||
}
|
||||
ProcessAllPrefabs("*.prefab", tempReplaceDict);
|
||||
ProcessAllPrefabs("*.unity", tempReplaceDict);
|
||||
ProcessAllPrefabs("*.anim", tempReplaceDict);
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
Debug.Log("<Color=#FFF333>处理完毕 [4]替换所有预制体和场景中的组件</color>");
|
||||
}
|
||||
|
||||
static void ProcessAllPrefabs(string form, Dictionary<string, string> tempReplaceDict, bool reverse = false)
|
||||
{
|
||||
List<GameObject> prefabs = new List<GameObject>();
|
||||
var resourcesPath = Application.dataPath;
|
||||
var absolutePaths = Directory.GetFiles(resourcesPath, form, SearchOption.AllDirectories);
|
||||
for (int i = 0; i < absolutePaths.Length; i++)
|
||||
{
|
||||
Debug.Log("prefab name: " + absolutePaths[i]);
|
||||
foreach (var VARIABLE in tempReplaceDict)
|
||||
{
|
||||
string oldValue = reverse ? VARIABLE.Value : VARIABLE.Key;
|
||||
string newValue = reverse ? VARIABLE.Key : VARIABLE.Value;
|
||||
ReplaceValue(absolutePaths[i], oldValue, newValue);
|
||||
}
|
||||
EditorUtility.DisplayProgressBar("处理预制体……", "处理预制体中……", (float)i / absolutePaths.Length);
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 替换值
|
||||
/// </summary>
|
||||
/// <param name="strFilePath">文件路径</param>
|
||||
static void ReplaceValue(string strFilePath, string oldLine, string newLine)
|
||||
{
|
||||
if (File.Exists(strFilePath))
|
||||
{
|
||||
string[] lines = File.ReadAllLines(strFilePath);
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
lines[i] = lines[i].Replace(oldLine, newLine);
|
||||
}
|
||||
File.WriteAllLines(strFilePath, lines);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("Axibug移植工具/ToLowVersionUnity/[5]UnPack所有嵌套预制体和场景中的预制体")]
|
||||
public static void UnpackPrefabs()
|
||||
{
|
||||
|
||||
#if UNITY_2018_4_OR_NEWER
|
||||
GoTAxiProjectToolsSence();
|
||||
string[] allAssetPaths = AssetDatabase.GetAllAssetPaths();
|
||||
int prefabCount = 0;
|
||||
|
||||
foreach (string path in allAssetPaths)
|
||||
{
|
||||
if (Path.GetExtension(path).Equals(".prefab"))
|
||||
{
|
||||
Debug.Log($"Unpacking {path}");
|
||||
UnpackPrefab(path);
|
||||
prefabCount++;
|
||||
}
|
||||
}
|
||||
Debug.Log($"{prefabCount}个预制体Unpack");
|
||||
|
||||
string[] sceneGuids = AssetDatabase.FindAssets("t:scene");
|
||||
foreach (string guid in sceneGuids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
if (path.Contains(toolSenceName))
|
||||
continue;
|
||||
|
||||
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(path);
|
||||
UnityEngine.SceneManagement.Scene currentScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
|
||||
GameObject[] rootObjects = currentScene.GetRootGameObjects();
|
||||
foreach (GameObject rootObj in rootObjects)
|
||||
{
|
||||
// 遍历场景中的所有对象
|
||||
TraverseHierarchy(rootObj);
|
||||
}
|
||||
// Save the scene // 获取当前打开的场景
|
||||
currentScene = UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene();
|
||||
// 保存场景到文件(默认路径和名称)
|
||||
bool success = UnityEditor.SceneManagement.EditorSceneManager.SaveScene(currentScene, currentScene.path);
|
||||
|
||||
Debug.Log($"{currentScene.name}场景中 所有物体Unpack");
|
||||
}
|
||||
|
||||
GoTAxiProjectToolsSence();
|
||||
Debug.Log("<Color=#FFF333>处理完毕 [5]UnPack所有预制体</color>");
|
||||
#else
|
||||
Debug.Log("低版本不要执行本函数");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void UnpackPrefab(string prefabPath)
|
||||
{
|
||||
#if UNITY_2018_4_OR_NEWER
|
||||
GameObject prefabInstance = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
||||
if (prefabInstance == null)
|
||||
{
|
||||
Debug.LogError($"Failed to load prefab at path: {prefabPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
var obj = GameObject.Instantiate(prefabInstance, null);
|
||||
TraverseHierarchy(obj);
|
||||
PrefabUtility.SaveAsPrefabAsset(obj, prefabPath);
|
||||
GameObject.DestroyImmediate(obj);
|
||||
#else
|
||||
Debug.Log("低版本不要执行本函数");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void TraverseHierarchy(GameObject obj)
|
||||
{
|
||||
#if UNITY_2018_4_OR_NEWER
|
||||
// 检查该对象是否是预制体的实例
|
||||
if (PrefabUtility.IsPartOfPrefabInstance(obj))
|
||||
{
|
||||
// 将预制体实例转换为普通游戏对象
|
||||
PrefabUtility.UnpackPrefabInstance(obj, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
|
||||
Debug.Log("Prefab instance converted to game object: " + obj.name);
|
||||
}
|
||||
|
||||
// 递归遍历子对象
|
||||
for (int i = 0; i < obj.transform.childCount; i++)
|
||||
{
|
||||
TraverseHierarchy(obj.transform.GetChild(i).gameObject);
|
||||
}
|
||||
#else
|
||||
Debug.Log("低版本不要执行本函数");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("Axibug移植工具/ToLowVersionUnity/[6]修复Sprite")]
|
||||
public static void FixMultipleMaterialSprites()
|
||||
{
|
||||
string[] guids = AssetDatabase.FindAssets("t:sprite");
|
||||
List<Sprite> spritesToFix = new List<Sprite>();
|
||||
|
||||
foreach (string guid in guids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
Sprite sprite = AssetDatabase.LoadAssetAtPath<Sprite>(path);
|
||||
|
||||
// 检查是否有多个材质
|
||||
if (IsUsingMultipleMaterials(sprite))
|
||||
{
|
||||
spritesToFix.Add(sprite);
|
||||
Debug.Log("Found sprite with multiple materials: " + path);
|
||||
}
|
||||
}
|
||||
|
||||
// 修复每个找到的Sprite
|
||||
foreach (var sprite in spritesToFix)
|
||||
{
|
||||
FixSprite(sprite);
|
||||
}
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
Debug.Log("<Color=#FFF333>处理完毕 [6]修复Sprite</color>");
|
||||
}
|
||||
|
||||
private static bool IsUsingMultipleMaterials(Sprite sprite)
|
||||
{
|
||||
if (sprite == null) return false;
|
||||
|
||||
// 获取精灵的材质
|
||||
var textureImporter = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(sprite)) as TextureImporter;
|
||||
|
||||
return textureImporter != null && textureImporter.spriteImportMode == SpriteImportMode.Multiple;
|
||||
}
|
||||
|
||||
private static void FixSprite(Sprite sprite)
|
||||
{
|
||||
// 获取Sprite的路径
|
||||
string path = AssetDatabase.GetAssetPath(sprite);
|
||||
var textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
|
||||
|
||||
if (textureImporter != null)
|
||||
{
|
||||
// 保存当前切割信息
|
||||
SpriteMetaData[] originalMetaData = textureImporter.spritesheet;
|
||||
|
||||
// 临时禁用Sprite导入
|
||||
textureImporter.spriteImportMode = SpriteImportMode.None;
|
||||
textureImporter.SaveAndReimport();
|
||||
|
||||
// 重新启用Sprite导入并保持原样切割参数
|
||||
textureImporter.spriteImportMode = SpriteImportMode.Multiple;
|
||||
textureImporter.spritesheet = originalMetaData; // 恢复原来的切割信息
|
||||
|
||||
// 重新导入以应用更改
|
||||
textureImporter.SaveAndReimport();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("Axibug移植工具/ToLowVersionUnity/[7]导入后低版本执行:修复组件挂载预制体依赖丢失")]
|
||||
static void FixPrefabRefs()
|
||||
{
|
||||
// 1. 扫描所有预制体
|
||||
string[] prefabPaths = Directory.GetFiles("Assets", "*.prefab", SearchOption.AllDirectories);
|
||||
foreach (var path in prefabPaths) FixRefTypeInFile(path);
|
||||
|
||||
// 2. 处理场景文件
|
||||
string[] scenePaths = Directory.GetFiles("Assets", "*.unity", SearchOption.AllDirectories);
|
||||
foreach (var path in scenePaths) FixRefTypeInFile(path);
|
||||
|
||||
AssetDatabase.Refresh();
|
||||
Debug.Log("<Color=#FFF333>处理完毕 [5]导入低版本后:修复预制体依赖丢失</color>");
|
||||
Debug.Log("修复完成!已处理" + prefabPaths.Length + "个预制体");
|
||||
}
|
||||
|
||||
public static void FixRefTypeInFile(string filePath)
|
||||
{
|
||||
string content = File.ReadAllText(filePath);
|
||||
// 匹配所有 {fileID: X, guid: Y, type: Z} 结构
|
||||
string pattern = @"(\{[^}]*guid:\s*(\w+)[^}]*type:\s*)3(\s*[^}]*\})";
|
||||
string newContent = Regex.Replace(content, pattern, match => {
|
||||
string guid = match.Groups[2].Value;
|
||||
string assetPath = AssetDatabase.GUIDToAssetPath(guid);
|
||||
// 仅当资源类型为 GameObject 时修改 type
|
||||
if (AssetDatabase.GetMainAssetTypeAtPath(assetPath) == typeof(GameObject))
|
||||
// if (assetPath.ToLower().EndsWith(".prefab")
|
||||
////&& assetPath.Contains("/sound/")
|
||||
//&& assetPath.Contains("/level")
|
||||
// )
|
||||
{
|
||||
Debug.Log("已处理被引用项=>"+assetPath+" ,引用到=>"+ filePath);
|
||||
Debug.Log("原值=>" + match.Value + " ,处理值=>"+ match.Groups[1].Value + "2" + match.Groups[3].Value);
|
||||
//return match.Value;
|
||||
return match.Groups[1].Value + "2" + match.Groups[3].Value; // type:3→2
|
||||
}
|
||||
return match.Value;
|
||||
});
|
||||
File.WriteAllText(filePath, newContent);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 01c95f5e03a338749b54784eb6420d04
|
@ -0,0 +1,73 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
public class AxiStatisticsCache : ScriptableObject
|
||||
{
|
||||
public List<AxiStatisticsDatas> caches = new List<AxiStatisticsDatas>();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class AxiStatisticsDatas
|
||||
{
|
||||
/// <summary>
|
||||
/// [0]Sence [1]Prefab
|
||||
/// </summary>
|
||||
public int type;
|
||||
public string FullPath;
|
||||
public List<AxiStatistics_Node> nodes = new List<AxiStatistics_Node>();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class AxiStatistics_Node
|
||||
{
|
||||
public string Name;
|
||||
public List<AxiStatistics_Node_Link> link = new List<AxiStatistics_Node_Link>();
|
||||
public int LinkHash;
|
||||
public string LinkFullStr;
|
||||
//public string NodeFullPath;
|
||||
// /// <summary>
|
||||
// /// 表示相同路径只有一个
|
||||
// /// </summary>
|
||||
// public bool NodeIdxOnlyOne;
|
||||
/// <summary>
|
||||
/// 表示相同路径是第几个下标
|
||||
/// </summary>
|
||||
//public int NodeIdx;
|
||||
public List<AxiStatistics_Node_Component> components = new List<AxiStatistics_Node_Component>();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class AxiStatistics_Node_Link
|
||||
{
|
||||
public string Name;
|
||||
public bool OnlyOne;
|
||||
public int Idx;
|
||||
public int NodeHash;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class AxiStatistics_Node_Component
|
||||
{
|
||||
public string type;
|
||||
/// <summary>
|
||||
/// 表示相同组件只有一个
|
||||
/// </summary>
|
||||
public bool ComTypeOnlyOne;
|
||||
/// <summary>
|
||||
/// 表示相同组件是第几个下标
|
||||
/// </summary>
|
||||
public int ComIdxNum;
|
||||
//Rigboody
|
||||
public bool simulated;
|
||||
public float gravityScale;
|
||||
public bool isKinematic;
|
||||
//BoxCollider2D
|
||||
public Vector2 center;
|
||||
public Vector2 size;
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4cee4feffb506b4c833262e779424f6
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 11a59d59d152c214bb99a09f4d795c21
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,316 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!29 &1
|
||||
OcclusionCullingSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_OcclusionBakeSettings:
|
||||
smallestOccluder: 5
|
||||
smallestHole: 0.25
|
||||
backfaceThreshold: 100
|
||||
m_SceneGUID: 00000000000000000000000000000000
|
||||
m_OcclusionCullingData: {fileID: 0}
|
||||
--- !u!104 &2
|
||||
RenderSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 10
|
||||
m_Fog: 0
|
||||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
||||
m_FogMode: 3
|
||||
m_FogDensity: 0.01
|
||||
m_LinearFogStart: 0
|
||||
m_LinearFogEnd: 300
|
||||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
|
||||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
|
||||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
|
||||
m_AmbientIntensity: 1
|
||||
m_AmbientMode: 0
|
||||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
|
||||
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_HaloStrength: 0.5
|
||||
m_FlareStrength: 1
|
||||
m_FlareFadeSpeed: 3
|
||||
m_HaloTexture: {fileID: 0}
|
||||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_DefaultReflectionMode: 0
|
||||
m_DefaultReflectionResolution: 128
|
||||
m_ReflectionBounces: 1
|
||||
m_ReflectionIntensity: 1
|
||||
m_CustomReflection: {fileID: 0}
|
||||
m_Sun: {fileID: 0}
|
||||
m_UseRadianceAmbientProbe: 0
|
||||
--- !u!157 &3
|
||||
LightmapSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 13
|
||||
m_BakeOnSceneLoad: 0
|
||||
m_GISettings:
|
||||
serializedVersion: 2
|
||||
m_BounceScale: 1
|
||||
m_IndirectOutputScale: 1
|
||||
m_AlbedoBoost: 1
|
||||
m_EnvironmentLightingMode: 0
|
||||
m_EnableBakedLightmaps: 1
|
||||
m_EnableRealtimeLightmaps: 0
|
||||
m_LightmapEditorSettings:
|
||||
serializedVersion: 12
|
||||
m_Resolution: 2
|
||||
m_BakeResolution: 40
|
||||
m_AtlasSize: 1024
|
||||
m_AO: 0
|
||||
m_AOMaxDistance: 1
|
||||
m_CompAOExponent: 1
|
||||
m_CompAOExponentDirect: 0
|
||||
m_ExtractAmbientOcclusion: 0
|
||||
m_Padding: 2
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_LightmapsBakeMode: 1
|
||||
m_TextureCompression: 1
|
||||
m_ReflectionCompression: 2
|
||||
m_MixedBakeMode: 2
|
||||
m_BakeBackend: 1
|
||||
m_PVRSampling: 1
|
||||
m_PVRDirectSampleCount: 32
|
||||
m_PVRSampleCount: 512
|
||||
m_PVRBounces: 2
|
||||
m_PVREnvironmentSampleCount: 256
|
||||
m_PVREnvironmentReferencePointCount: 2048
|
||||
m_PVRFilteringMode: 1
|
||||
m_PVRDenoiserTypeDirect: 1
|
||||
m_PVRDenoiserTypeIndirect: 1
|
||||
m_PVRDenoiserTypeAO: 1
|
||||
m_PVRFilterTypeDirect: 0
|
||||
m_PVRFilterTypeIndirect: 0
|
||||
m_PVRFilterTypeAO: 0
|
||||
m_PVREnvironmentMIS: 1
|
||||
m_PVRCulling: 1
|
||||
m_PVRFilteringGaussRadiusDirect: 1
|
||||
m_PVRFilteringGaussRadiusIndirect: 1
|
||||
m_PVRFilteringGaussRadiusAO: 1
|
||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
|
||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2
|
||||
m_PVRFilteringAtrousPositionSigmaAO: 1
|
||||
m_ExportTrainingData: 0
|
||||
m_TrainingDataDestination: TrainingData
|
||||
m_LightProbeSampleCountMultiplier: 4
|
||||
m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_LightingSettings: {fileID: 0}
|
||||
--- !u!196 &4
|
||||
NavMeshSettings:
|
||||
serializedVersion: 2
|
||||
m_ObjectHideFlags: 0
|
||||
m_BuildSettings:
|
||||
serializedVersion: 3
|
||||
agentTypeID: 0
|
||||
agentRadius: 0.5
|
||||
agentHeight: 2
|
||||
agentSlope: 45
|
||||
agentClimb: 0.4
|
||||
ledgeDropHeight: 0
|
||||
maxJumpAcrossDistance: 0
|
||||
minRegionArea: 2
|
||||
manualCellSize: 0
|
||||
cellSize: 0.16666667
|
||||
manualTileSize: 0
|
||||
tileSize: 256
|
||||
buildHeightMesh: 0
|
||||
maxJobWorkers: 0
|
||||
preserveTilesOutsideBounds: 0
|
||||
debug:
|
||||
m_Flags: 0
|
||||
m_NavMeshData: {fileID: 0}
|
||||
--- !u!1 &1760378052
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1760378055}
|
||||
- component: {fileID: 1760378054}
|
||||
- component: {fileID: 1760378053}
|
||||
m_Layer: 0
|
||||
m_Name: Main Camera
|
||||
m_TagString: MainCamera
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!81 &1760378053
|
||||
AudioListener:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1760378052}
|
||||
m_Enabled: 1
|
||||
--- !u!20 &1760378054
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1760378052}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
m_ClearFlags: 1
|
||||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
|
||||
m_projectionMatrixMode: 1
|
||||
m_GateFitMode: 2
|
||||
m_FOVAxisMode: 0
|
||||
m_Iso: 200
|
||||
m_ShutterSpeed: 0.005
|
||||
m_Aperture: 16
|
||||
m_FocusDistance: 10
|
||||
m_FocalLength: 50
|
||||
m_BladeCount: 5
|
||||
m_Curvature: {x: 2, y: 11}
|
||||
m_BarrelClipping: 0.25
|
||||
m_Anamorphism: 0
|
||||
m_SensorSize: {x: 36, y: 24}
|
||||
m_LensShift: {x: 0, y: 0}
|
||||
m_NormalizedViewPortRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
field of view: 60
|
||||
orthographic: 0
|
||||
orthographic size: 5
|
||||
m_Depth: -1
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingPath: -1
|
||||
m_TargetTexture: {fileID: 0}
|
||||
m_TargetDisplay: 0
|
||||
m_TargetEye: 3
|
||||
m_HDR: 1
|
||||
m_AllowMSAA: 1
|
||||
m_AllowDynamicResolution: 0
|
||||
m_ForceIntoRT: 0
|
||||
m_OcclusionCulling: 1
|
||||
m_StereoConvergence: 10
|
||||
m_StereoSeparation: 0.022
|
||||
--- !u!4 &1760378055
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1760378052}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 1, z: -10}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1948026923
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1948026925}
|
||||
- component: {fileID: 1948026924}
|
||||
m_Layer: 0
|
||||
m_Name: Directional Light
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!108 &1948026924
|
||||
Light:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1948026923}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 11
|
||||
m_Type: 1
|
||||
m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
|
||||
m_Intensity: 1
|
||||
m_Range: 10
|
||||
m_SpotAngle: 30
|
||||
m_InnerSpotAngle: 21.80208
|
||||
m_CookieSize: 10
|
||||
m_Shadows:
|
||||
m_Type: 2
|
||||
m_Resolution: -1
|
||||
m_CustomResolution: -1
|
||||
m_Strength: 1
|
||||
m_Bias: 0.05
|
||||
m_NormalBias: 0.4
|
||||
m_NearPlane: 0.2
|
||||
m_CullingMatrixOverride:
|
||||
e00: 1
|
||||
e01: 0
|
||||
e02: 0
|
||||
e03: 0
|
||||
e10: 0
|
||||
e11: 1
|
||||
e12: 0
|
||||
e13: 0
|
||||
e20: 0
|
||||
e21: 0
|
||||
e22: 1
|
||||
e23: 0
|
||||
e30: 0
|
||||
e31: 0
|
||||
e32: 0
|
||||
e33: 1
|
||||
m_UseCullingMatrixOverride: 0
|
||||
m_Cookie: {fileID: 0}
|
||||
m_DrawHalo: 0
|
||||
m_Flare: {fileID: 0}
|
||||
m_RenderMode: 0
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingLayerMask: 1
|
||||
m_Lightmapping: 4
|
||||
m_LightShadowCasterMode: 0
|
||||
m_AreaSize: {x: 1, y: 1}
|
||||
m_BounceIntensity: 1
|
||||
m_ColorTemperature: 6570
|
||||
m_UseColorTemperature: 0
|
||||
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_UseBoundingSphereOverride: 0
|
||||
m_UseViewFrustumForShadowCasterCull: 1
|
||||
m_ForceVisible: 0
|
||||
m_ShadowRadius: 0
|
||||
m_ShadowAngle: 0
|
||||
m_LightUnit: 1
|
||||
m_LuxAtDistance: 1
|
||||
m_EnableSpotReflector: 1
|
||||
--- !u!4 &1948026925
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1948026923}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
|
||||
m_LocalPosition: {x: 0, y: 3, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
|
||||
--- !u!1660057539 &9223372036854775807
|
||||
SceneRoots:
|
||||
m_ObjectHideFlags: 0
|
||||
m_Roots:
|
||||
- {fileID: 1760378055}
|
||||
- {fileID: 1948026925}
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f578c65afd0d1c84b9b59664106fab66
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
AxibugEmuOnline.Client.Switch/Assets/Editors.meta
Normal file
8
AxibugEmuOnline.Client.Switch/Assets/Editors.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd41663b74cbfcc45a028bc891a8c4fc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "AxibugEmuOnline.Editor",
|
||||
"rootNamespace": "AxibugEmuOnline.Editors",
|
||||
"references": [
|
||||
"AxibugEmuOnline.Client"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a37804d4d608e1e4bb8204f442ab0e60
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,50 @@
|
||||
using AxibugEmuOnline.Client;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AxibugEmuOnline.Editors
|
||||
{
|
||||
[CustomEditor(typeof(CommandDispatcher))]
|
||||
public class CommandDispatcherEditor : Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
base.OnInspectorGUI();
|
||||
|
||||
if (!Application.isPlaying) return;
|
||||
|
||||
var dispacather = target as CommandDispatcher;
|
||||
IReadOnlyList<CommandExecuter> normal; IReadOnlyList<CommandExecuter> solo;
|
||||
dispacather.GetRegisters(out normal, out solo);
|
||||
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
EditorGUILayout.LabelField("NORMAL");
|
||||
foreach (var item in normal)
|
||||
{
|
||||
Draw(item);
|
||||
}
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
EditorGUILayout.LabelField("SOLO");
|
||||
foreach (var item in solo)
|
||||
{
|
||||
Draw(item);
|
||||
}
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.LabelField(dispacather.Mode.ToString());
|
||||
|
||||
Repaint();
|
||||
}
|
||||
|
||||
private void Draw(CommandExecuter item)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
|
||||
using (new EditorGUI.DisabledGroupScope(!item.Enable))
|
||||
EditorGUILayout.ObjectField(item.gameObject, typeof(GameObject), false);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b8790f215d873d044aa8d00bacdee237
|
||||
guid: 57378be70cec95341aea522ad2d8e30d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
@ -0,0 +1 @@
|
||||
{}
|
@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ad516ab7bf27b945a96f9c6eca54f10
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 11500000, guid: 8404be70184654265930450def6a9037, type: 3}
|
||||
generateWrapperCode: 0
|
||||
wrapperCodePath:
|
||||
wrapperClassName:
|
||||
wrapperCodeNamespace:
|
8
AxibugEmuOnline.Client.Switch/Assets/Plugins.meta
Normal file
8
AxibugEmuOnline.Client.Switch/Assets/Plugins.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 69eb869b06dab98439f07da4549fb7cb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4094856a5db24f142a14fda03f1215a2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,46 @@
|
||||
#if UNITY_SWITCH
|
||||
using nn.account;
|
||||
#endif
|
||||
|
||||
public class AxiNS
|
||||
{
|
||||
static AxiNS _instance;
|
||||
public static AxiNS instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
_instance = new AxiNS();
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 延迟提交是否使用多线程
|
||||
/// </summary>
|
||||
public static bool usedmultithreading = false;
|
||||
|
||||
public AxiNSUser user;
|
||||
public AxiNSMount mount;
|
||||
public AxiNSIO io;
|
||||
public AxiNSWaitHandle wait;
|
||||
AxiNS()
|
||||
{
|
||||
user = new AxiNSUser();
|
||||
mount = new AxiNSMount();
|
||||
io = new AxiNSIO();
|
||||
wait = new AxiNSWaitHandle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化(最好在项目第一时间初始化,保证先初始化再使用某些东西,才不闪退)
|
||||
/// </summary>
|
||||
public void Init()
|
||||
{
|
||||
#if UNITY_SWITCH
|
||||
if (!user.GetUserID(out Uid uid))
|
||||
return;
|
||||
mount.MountSave(uid);
|
||||
#endif
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c29cb72b155d20a48a3a47a7a05160bd
|
||||
guid: 52541c757d45c4c488726bcc39f73ba6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "AxiNSApi"
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c8ad600c72d635843bd8aeb9d8aebfb8
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,367 @@
|
||||
public static class AxiNSErrCode
|
||||
{
|
||||
#if UNITY_SWITCH
|
||||
public static string GetErrorInfo(this nn.Result result)
|
||||
{
|
||||
if (result.IsSuccess())
|
||||
return "NoErr";
|
||||
return GetErrorDetails(result.GetModule(), result.GetDescription());
|
||||
}
|
||||
#endif
|
||||
/// <summary>
|
||||
/// 根据模块 ID 和描述 ID 返回任天堂 Switch 错误码的含义、可能原因及解决办法。
|
||||
/// </summary>
|
||||
/// <param name="moduleId">模块 ID</param>
|
||||
/// <param name="descriptionId">描述 ID</param>
|
||||
/// <returns>包含错误码、含义、可能原因及解决办法的字符串</returns>
|
||||
public static string GetErrorDetails(int moduleId, int descriptionId)
|
||||
{
|
||||
string errorCode = $"2{moduleId:D3}-{descriptionId:D4}"; // 格式化为 2XXX-YYYY
|
||||
string meaning = "未知错误";
|
||||
string causeAndSolution = "未知错误,请检查日志或联系任天堂支持。";
|
||||
|
||||
switch (moduleId)
|
||||
{
|
||||
case 2: // nn::fs (文件系统)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 1:
|
||||
meaning = "ResultPathNotFound";
|
||||
causeAndSolution = "路径未找到。检查路径是否正确,确保父目录存在。使用 nn.fs.Directory.Create 创建父目录。";
|
||||
break;
|
||||
case 2:
|
||||
meaning = "ResultPermissionDenied";
|
||||
causeAndSolution = "权限被拒绝。可能是 Atmosphere 限制了 save:/ 的写权限。尝试调整 Atmosphere 配置或使用 sd:/ 挂载点。";
|
||||
break;
|
||||
case 3:
|
||||
meaning = "ResultPathAlreadyExists";
|
||||
causeAndSolution = "路径已存在。检查路径是否被占用,删除或重命名现有文件/目录。";
|
||||
break;
|
||||
case 5:
|
||||
meaning = "ResultTargetLocked";
|
||||
causeAndSolution = "目标被锁定。可能是文件正在使用中,关闭相关程序后重试。";
|
||||
break;
|
||||
case 7:
|
||||
meaning = "ResultTargetNotFound";
|
||||
causeAndSolution = "目标未找到。确认目标文件/目录是否存在,检查路径拼写。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 5: // nn::err (错误处理)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 3:
|
||||
meaning = "microSD 卡相关错误";
|
||||
causeAndSolution = "无法下载软件,可能是 microSD 卡损坏。移除 microSD 卡,重新插入,或更换卡。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 16: // nn::oe (操作系统错误)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 247:
|
||||
meaning = "microSD 卡存储问题";
|
||||
causeAndSolution = "microSD 卡损坏或不兼容。更换 microSD 卡,或将默认存储位置设置为系统内存。";
|
||||
break;
|
||||
case 390:
|
||||
meaning = "不支持的 microSD 卡";
|
||||
causeAndSolution = "microSD 卡格式不受支持。格式化为 exFAT 或 FAT32 后重试。";
|
||||
break;
|
||||
case 601:
|
||||
meaning = "microSD 卡数据损坏";
|
||||
causeAndSolution = "microSD 卡数据损坏。备份数据后格式化卡,或更换新卡。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 21: // nn::settings (设置)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 3:
|
||||
meaning = "系统软件未更新或损坏";
|
||||
causeAndSolution = "系统固件版本过旧或损坏。更新系统固件,或重新安装系统。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 101: // nn::fssrv (文件系统服务)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 1:
|
||||
meaning = "系统错误";
|
||||
causeAndSolution = "通用系统错误。重启 Switch,若问题持续,联系任天堂支持。";
|
||||
break;
|
||||
case 2:
|
||||
meaning = "固件损坏";
|
||||
causeAndSolution = "系统固件损坏。尝试更新系统,或恢复出厂设置(注意备份数据)。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 107: // nn::nim (网络安装管理)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 427:
|
||||
meaning = "固件损坏";
|
||||
causeAndSolution = "系统固件损坏。更新系统固件,或重新安装系统。";
|
||||
break;
|
||||
case 445:
|
||||
meaning = "硬件损坏或盗版内容";
|
||||
causeAndSolution = "可能是硬件损坏或存在盗版内容。删除盗版内容,重启 Switch,若无效联系任天堂支持。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 110: // nn::socket (网络套接字)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 1000:
|
||||
meaning = "网络连接失败";
|
||||
causeAndSolution = "无法连接到网络。检查网络连接,重启路由器,尝试切换到其他网络频段(2.4GHz/5GHz)。";
|
||||
break;
|
||||
case 2003:
|
||||
meaning = "无线网络连接失败";
|
||||
causeAndSolution = "无线信号弱或不稳定。靠近路由器,移除干扰物,或重启路由器。";
|
||||
break;
|
||||
case 2004:
|
||||
meaning = "网络设置不支持";
|
||||
causeAndSolution = "网络安全类型不受支持。Switch 支持 WEP/WPA/WPA2,调整路由器设置后重试。";
|
||||
break;
|
||||
case 2091:
|
||||
meaning = "有线网络连接失败";
|
||||
causeAndSolution = "有线连接问题。检查网线是否插好,重启 Switch 和路由器。";
|
||||
break;
|
||||
case 3127:
|
||||
meaning = "网络连接失败";
|
||||
causeAndSolution = "网络不稳定。测试网络连接,重启路由器,或联系网络提供商。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 115: // nn::mii (Mii 相关)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 96:
|
||||
meaning = "Mii 数据错误";
|
||||
causeAndSolution = "Mii 数据损坏。删除并重新创建 Mii,或更新系统。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 139: // nn::nfp (NFC/Amiibo)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 6:
|
||||
meaning = "Amiibo 读取错误";
|
||||
causeAndSolution = "Amiibo 读取失败。检查 Amiibo 是否损坏,更新系统,或尝试其他 Amiibo。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 153: // nn::ir (红外摄像头)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 321:
|
||||
meaning = "红外摄像头读取错误";
|
||||
causeAndSolution = "红外摄像头无法读取。检查摄像头是否被遮挡,清洁镜头,或联系任天堂支持。";
|
||||
break;
|
||||
case 1540:
|
||||
meaning = "红外摄像头硬件错误";
|
||||
causeAndSolution = "红外摄像头硬件故障。联系任天堂支持进行维修。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 155: // nn::account (账户服务)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 8006:
|
||||
meaning = "无法链接任天堂账户";
|
||||
causeAndSolution = "网络问题或服务中断。检查网络连接,稍后重试,或查看任天堂网络状态页面。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 160: // nn::online (在线服务)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 103:
|
||||
meaning = "无法加入在线匹配";
|
||||
causeAndSolution = "网络不稳定。重启 Switch,检查网络连接,或稍后重试。";
|
||||
break;
|
||||
case 202:
|
||||
meaning = "无法连接到在线服务";
|
||||
causeAndSolution = "服务可能正在维护。查看任天堂网络状态页面,稍后重试。";
|
||||
break;
|
||||
case 8006:
|
||||
meaning = "连接测试失败";
|
||||
causeAndSolution = "网络问题。重启路由器,检查网络设置,或尝试其他网络。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 162: // nn::application (应用程序)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 2:
|
||||
meaning = "软件崩溃";
|
||||
causeAndSolution = "软件崩溃,可能是盗版内容或固件问题。删除盗版内容,更新系统,或重新安装软件。";
|
||||
break;
|
||||
case 101:
|
||||
meaning = "软件需要更新";
|
||||
causeAndSolution = "游戏或软件需要更新。检查软件更新并安装。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 168: // nn::sys (系统)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 0:
|
||||
meaning = "需要软件更新";
|
||||
causeAndSolution = "软件需要更新。更新游戏或系统固件。";
|
||||
break;
|
||||
case 2:
|
||||
meaning = "系统崩溃";
|
||||
causeAndSolution = "系统崩溃,可能是硬件问题。重启 Switch,若无效联系任天堂支持。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 205: // nn::camera (摄像头)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 123:
|
||||
meaning = "摄像头读取错误";
|
||||
causeAndSolution = "摄像头无法读取。检查摄像头是否被遮挡,清洁镜头,或联系任天堂支持。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 306: // nn::ngc (网络游戏连接)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 501:
|
||||
meaning = "无法加入在线匹配";
|
||||
causeAndSolution = "网络连接中断。重启 Switch,检查网络连接,或稍后重试。";
|
||||
break;
|
||||
case 502:
|
||||
meaning = "匹配过程失败";
|
||||
causeAndSolution = "网络不稳定。测试网络连接,重启路由器,或联系网络提供商。";
|
||||
break;
|
||||
case 820:
|
||||
meaning = "在线服务不可用";
|
||||
causeAndSolution = "服务可能正在维护。查看任天堂网络状态页面,稍后重试。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 613: // nn::eShop (eShop)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 1400:
|
||||
meaning = "无法使用信用卡购买";
|
||||
causeAndSolution = "信用卡信息错误或 eShop 服务问题。检查信用卡信息,稍后重试,或联系任天堂支持。";
|
||||
break;
|
||||
case 6838:
|
||||
meaning = "eShop 连接失败";
|
||||
causeAndSolution = "网络问题或 eShop 维护。检查网络连接,查看任天堂网络状态页面,稍后重试。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 618: // nn::ngc (网络游戏连接)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 6:
|
||||
meaning = "无法加入在线匹配";
|
||||
causeAndSolution = "网络不稳定。重启 Switch,检查网络连接,或稍后重试。";
|
||||
break;
|
||||
case 201:
|
||||
meaning = "匹配已满";
|
||||
causeAndSolution = "尝试加入的匹配已满。稍后重试,或加入其他匹配。";
|
||||
break;
|
||||
case 501:
|
||||
meaning = "匹配过程失败";
|
||||
causeAndSolution = "网络连接中断。重启 Switch,检查网络连接,或稍后重试。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 801: // nn::sns (社交网络服务)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 7002:
|
||||
meaning = "无法上传截图到 Twitter";
|
||||
causeAndSolution = "服务可能正在维护。查看任天堂网络状态页面,稍后重试。";
|
||||
break;
|
||||
case 7199:
|
||||
meaning = "无法上传照片到 Facebook";
|
||||
causeAndSolution = "服务可能正在维护。查看任天堂网络状态页面,稍后重试。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 810: // nn::account (账户服务)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 1224:
|
||||
meaning = "无法登录任天堂账户";
|
||||
causeAndSolution = "网络问题或服务中断。检查网络连接,稍后重试,或查看任天堂网络状态页面。";
|
||||
break;
|
||||
case 1500:
|
||||
meaning = "无法登录 Facebook 账户";
|
||||
causeAndSolution = "服务可能正在维护。重启 Switch,稍后重试。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 811: // nn::account (账户服务)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 1006:
|
||||
meaning = "无法链接任天堂账户";
|
||||
causeAndSolution = "网络问题或 DNS 错误。检查网络连接,尝试更换 DNS(如 8.8.8.8),或稍后重试。";
|
||||
break;
|
||||
case 5001:
|
||||
meaning = "无法访问 eShop";
|
||||
causeAndSolution = "eShop 服务中断。查看任天堂网络状态页面,稍后重试。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 813: // nn::eShop (eShop)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 0:
|
||||
meaning = "eShop 访问失败";
|
||||
causeAndSolution = "eShop 服务中断。查看任天堂网络状态页面,稍后重试。";
|
||||
break;
|
||||
case 2470:
|
||||
meaning = "交易处理失败";
|
||||
causeAndSolution = "信用卡信息错误或 eShop 服务问题。检查信用卡信息,稍后重试,或联系任天堂支持。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 819: // nn::online (在线服务)
|
||||
switch (descriptionId)
|
||||
{
|
||||
case 3:
|
||||
meaning = "软件被暂停";
|
||||
causeAndSolution = "同一账户在另一台设备上使用。关闭其他设备上的软件,或使用不同账户。";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
meaning = "未知模块";
|
||||
causeAndSolution = "未识别的模块 ID,请检查日志或联系任天堂支持。";
|
||||
break;
|
||||
}
|
||||
|
||||
return $"错误码: {errorCode}\n含义: {meaning}\n可能原因及解决办法: {causeAndSolution}";
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c8339ffab20bfea4cbc7d3aa440c3fdb
|
||||
guid: 0e28b69692cb1bb4a9d8ddb91274fa50
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
869
AxibugEmuOnline.Client.Switch/Assets/Plugins/AxiNSApi/AxiNSIO.cs
Normal file
869
AxibugEmuOnline.Client.Switch/Assets/Plugins/AxiNSApi/AxiNSIO.cs
Normal file
@ -0,0 +1,869 @@
|
||||
#if UNITY_SWITCH
|
||||
using nn.fs;
|
||||
#endif
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
public class AxiNSIO
|
||||
{
|
||||
string save_name => AxiNS.instance.mount.SaveMountName;
|
||||
public string save_path => $"{save_name}:/";
|
||||
#if UNITY_SWITCH
|
||||
private FileHandle fileHandle = new nn.fs.FileHandle();
|
||||
#endif
|
||||
|
||||
static object commitLock = new object();
|
||||
|
||||
static bool bDirty = false;
|
||||
|
||||
bool CommitSave()
|
||||
{
|
||||
lock (commitLock)
|
||||
{
|
||||
#if UNITY_SWITCH && !UNITY_EDITOR
|
||||
|
||||
// 阻止用户在保存时,退出游戏 Switch 条例 0080
|
||||
UnityEngine.Switch.Notification.EnterExitRequestHandlingSection();
|
||||
nn.Result ret = FileSystem.Commit(save_name);
|
||||
|
||||
if (!ret.IsSuccess())
|
||||
{
|
||||
UnityEngine.Debug.LogError($"FileSystem.Commit({save_name}) 失败: " + ret.GetErrorInfo());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 停止阻止用户退出游戏
|
||||
UnityEngine.Switch.Notification.LeaveExitRequestHandlingSection();
|
||||
|
||||
bDirty = false;
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SetCommitDirty()
|
||||
{
|
||||
lock (commitLock)
|
||||
{
|
||||
bDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyAutoCommit()
|
||||
{
|
||||
bool temp;
|
||||
lock (commitLock)
|
||||
{
|
||||
temp = bDirty;
|
||||
}
|
||||
|
||||
if (temp)
|
||||
{
|
||||
CommitSave();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查Path是否存在
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <returns></returns>
|
||||
public bool CheckPathExists(string filePath)
|
||||
{
|
||||
#if !UNITY_SWITCH
|
||||
return false;
|
||||
#else
|
||||
nn.fs.EntryType entryType = 0;
|
||||
nn.Result result = nn.fs.FileSystem.GetEntryType(ref entryType, filePath);
|
||||
//result.abortUnlessSuccess();
|
||||
//这个异常捕获。真的别扭
|
||||
|
||||
//日,FileSystem.ResultPathAlreadyExists 貌似不太行
|
||||
//return nn.fs.FileSystem.ResultPathAlreadyExists.Includes(result);
|
||||
return !nn.fs.FileSystem.ResultPathNotFound.Includes(result);
|
||||
#endif
|
||||
}
|
||||
/// <summary>
|
||||
/// 检查Path是否不存在
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <returns></returns>
|
||||
public bool CheckPathNotFound(string filePath)
|
||||
{
|
||||
#if !UNITY_SWITCH
|
||||
return false;
|
||||
#else
|
||||
nn.fs.EntryType entryType = 0;
|
||||
nn.Result result = nn.fs.FileSystem.GetEntryType(ref entryType, filePath);
|
||||
//这个异常捕获。真的别扭
|
||||
return nn.fs.FileSystem.ResultPathNotFound.Includes(result);
|
||||
#endif
|
||||
}
|
||||
/// <summary>
|
||||
/// 创建目录,目录存在也会返回true
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <returns></returns>
|
||||
public bool CreateDir(string filePath)
|
||||
{
|
||||
lock (commitLock)
|
||||
{
|
||||
|
||||
#if !UNITY_SWITCH
|
||||
return false;
|
||||
#else
|
||||
// 使用封装函数检查和创建父目录
|
||||
if (!EnsureParentDirectory(filePath, true))
|
||||
{
|
||||
UnityEngine.Debug.LogError($"无法确保父目录,文件写入取消: {filePath}");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存并创建文件(如果目录不存在回先自动创建目录)
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="bw"></param>
|
||||
/// <returns></returns>
|
||||
public bool FileToSaveWithCreate(string filePath, System.IO.MemoryStream ms)
|
||||
{
|
||||
return FileToSaveWithCreate(filePath, ms.ToArray());
|
||||
}
|
||||
/// <summary>
|
||||
/// 保存并创建文件(如果目录不存在回先自动创建目录)
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public AxiNSWait_FileToSaveByMSWithCreate FileToSaveWithCreateAsync(string filePath, System.IO.MemoryStream ms)
|
||||
{
|
||||
var wait = new AxiNSWait_FileToSaveByMSWithCreate(filePath, ms);
|
||||
AxiNS.instance.wait.AddWait(wait);
|
||||
return wait;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存并创建文件(如果目录不存在回先自动创建目录)
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="immediatelyCommit">是否立即Commit到物理存储</param>
|
||||
/// <returns></returns>
|
||||
public bool FileToSaveWithCreate(string filePath, byte[] data, bool immediatelyCommit = true)
|
||||
{
|
||||
lock (commitLock)
|
||||
{
|
||||
#if !UNITY_SWITCH
|
||||
return false;
|
||||
#else
|
||||
if (!AxiNS.instance.mount.SaveIsMount)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"Save 尚未挂载,无法存储 {filePath}");
|
||||
return false;
|
||||
}
|
||||
|
||||
nn.Result result;
|
||||
#if UNITY_SWITCH && !UNITY_EDITOR
|
||||
// 阻止用户在保存时,退出游戏
|
||||
// Switch 条例 0080
|
||||
UnityEngine.Switch.Notification.EnterExitRequestHandlingSection();
|
||||
#endif
|
||||
// 使用封装函数检查和创建父目录
|
||||
if (!EnsureParentDirectory(filePath, true))
|
||||
{
|
||||
UnityEngine.Debug.LogError($"无法确保父目录,文件写入取消: {filePath}");
|
||||
return false;
|
||||
}
|
||||
|
||||
//string directoryPath = System.IO.Path.GetDirectoryName(filePath.Replace(save_path, ""));
|
||||
//string fullDirectoryPath = $"{save_path}{directoryPath}";
|
||||
//UnityEngine.Debug.Log($"检查父目录: {fullDirectoryPath}");
|
||||
|
||||
//nn.fs.EntryType entryType = 0;
|
||||
//result = nn.fs.FileSystem.GetEntryType(ref entryType, fullDirectoryPath);
|
||||
//if (!result.IsSuccess() && nn.fs.FileSystem.ResultPathNotFound.Includes(result))
|
||||
//{
|
||||
// UnityEngine.Debug.Log($"父目录 {fullDirectoryPath} 不存在,尝试创建 (判断依据 result=>{result.ToString()})");
|
||||
// result = nn.fs.Directory.Create(fullDirectoryPath);
|
||||
// if (!result.IsSuccess())
|
||||
// {
|
||||
// UnityEngine.Debug.LogError($"创建父目录失败: {result.GetErrorInfo()}");
|
||||
// return false;
|
||||
// }
|
||||
// UnityEngine.Debug.Log($"父目录 {fullDirectoryPath} 创建成功");
|
||||
//}
|
||||
//else if (result.IsSuccess() && entryType != nn.fs.EntryType.Directory)
|
||||
//{
|
||||
// UnityEngine.Debug.LogError($"路径 {fullDirectoryPath} 已存在,但不是目录");
|
||||
// return false;
|
||||
//}
|
||||
//else if (!result.IsSuccess())
|
||||
//{
|
||||
// UnityEngine.Debug.LogError($"检查父目录失败: {result.GetErrorInfo()}");
|
||||
// return false;
|
||||
//}
|
||||
|
||||
if (CheckPathNotFound(filePath))
|
||||
{
|
||||
UnityEngine.Debug.Log($"文件({filePath})不存在需要创建");
|
||||
result = nn.fs.File.Create(filePath, data.Length); //this makes a file the size of your save journal. You may want to make a file smaller than this.
|
||||
//result.abortUnlessSuccess();
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
UnityEngine.Debug.LogError($"创建文件失败 {filePath} : " + result.GetErrorInfo());
|
||||
return false;
|
||||
}
|
||||
//读取文件Handle
|
||||
result = File.Open(ref fileHandle, filePath, OpenFileMode.Write);
|
||||
}
|
||||
else
|
||||
{
|
||||
//读取文件Handle
|
||||
result = File.Open(ref fileHandle, filePath, OpenFileMode.Write);
|
||||
long currsize = 0;
|
||||
File.GetSize(ref currsize, fileHandle);
|
||||
if (currsize == data.Length)
|
||||
{
|
||||
UnityEngine.Debug.Log($"文件({filePath})存在,长度一致,不用重新创建");
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Debug.Log($"文件({filePath})存在,长度不一致,先删除再重建");
|
||||
nn.fs.File.Close(fileHandle);
|
||||
//删除
|
||||
File.Delete(filePath);
|
||||
//重新创建
|
||||
result = nn.fs.File.Create(filePath, data.Length);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
UnityEngine.Debug.LogError($"创建文件失败 {filePath} : " + result.GetErrorInfo());
|
||||
return false;
|
||||
}
|
||||
//重新读取文件Handle
|
||||
result = File.Open(ref fileHandle, filePath, OpenFileMode.Write);
|
||||
}
|
||||
}
|
||||
|
||||
// //OpenFileMode.AllowAppend 好像不可用
|
||||
// //result = File.Open(ref fileHandle, filePath, OpenFileMode.AllowAppend);
|
||||
// result = File.Open(ref fileHandle, filePath, OpenFileMode.Write);
|
||||
|
||||
//result.abortUnlessSuccess();
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
UnityEngine.Debug.LogError($"失败 File.Open(ref filehandle, {filePath}, OpenFileMode.Write): " + result.GetErrorInfo());
|
||||
return false;
|
||||
}
|
||||
UnityEngine.Debug.Log($"成功 File.Open(ref filehandle, {filePath}, OpenFileMode.Write)");
|
||||
|
||||
//nn.fs.WriteOption.Flush 应该就是覆盖写入
|
||||
result = nn.fs.File.Write(fileHandle, 0, data, data.Length, nn.fs.WriteOption.Flush); // Writes and flushes the write at the same time
|
||||
//result.abortUnlessSuccess();
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
UnityEngine.Debug.LogError("写入文件失败: " + result.GetErrorInfo());
|
||||
return false;
|
||||
}
|
||||
UnityEngine.Debug.Log("写入文件成功: " + filePath);
|
||||
|
||||
nn.fs.File.Close(fileHandle);
|
||||
|
||||
|
||||
#if UNITY_SWITCH && !UNITY_EDITOR
|
||||
// 停止阻止用户退出游戏
|
||||
UnityEngine.Switch.Notification.LeaveExitRequestHandlingSection();
|
||||
#endif
|
||||
|
||||
if (immediatelyCommit)
|
||||
{
|
||||
//必须得提交,否则没有真实写入
|
||||
return CommitSave();
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCommitDirty();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 保存并创建文件(如果目录不存在回先自动创建目录)
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public AxiNSWait_FileToSaveWithCreate FileToSaveWithCreateAsync(string filePath, byte[] data)
|
||||
{
|
||||
var wait = new AxiNSWait_FileToSaveWithCreate(filePath, data);
|
||||
AxiNS.instance.wait.AddWait(wait);
|
||||
return wait;
|
||||
}
|
||||
public byte[] LoadSwitchDataFile(string filename)
|
||||
{
|
||||
byte[] outputData;
|
||||
LoadSwitchDataFile(filename, out outputData);
|
||||
return outputData;
|
||||
}
|
||||
|
||||
public bool LoadSwitchDataFile(string filename, ref System.IO.MemoryStream ms)
|
||||
{
|
||||
byte[] outputData;
|
||||
if (LoadSwitchDataFile(filename, out outputData))
|
||||
{
|
||||
using (System.IO.BinaryWriter writer = new System.IO.BinaryWriter(ms))
|
||||
{
|
||||
writer.Write(outputData);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public bool LoadSwitchDataFile(string filename, out byte[] outputData)
|
||||
{
|
||||
#if !UNITY_SWITCH || UNITY_EDITOR
|
||||
outputData = null;
|
||||
return false;
|
||||
#else
|
||||
outputData = null;
|
||||
if (!AxiNS.instance.mount.SaveIsMount)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"Save 尚未挂载,无法读取 {filename}");
|
||||
return false;
|
||||
}
|
||||
if (CheckPathNotFound(filename))
|
||||
return false;
|
||||
|
||||
nn.Result result;
|
||||
result = nn.fs.File.Open(ref fileHandle, filename, nn.fs.OpenFileMode.Read);
|
||||
if (result.IsSuccess() == false)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"nn.fs.File.Open 失败 {filename} : result=>{result.GetErrorInfo()}");
|
||||
return false; // Could not open file. This can be used to detect if this is the first time a user has launched your game.
|
||||
// (However, be sure you are not getting this error due to your file being locked by another process, etc.)
|
||||
}
|
||||
UnityEngine.Debug.Log($"nn.fs.File.Open 成功 {filename}");
|
||||
long iFileSize = 0;
|
||||
result = nn.fs.File.GetSize(ref iFileSize, fileHandle);
|
||||
if (result.IsSuccess() == false)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"nn.fs.File.GetSize 失败 {filename} : result=>{result.GetErrorInfo()}");
|
||||
return false;
|
||||
}
|
||||
UnityEngine.Debug.Log($"nn.fs.File.GetSize 成功 {filename},size=>{iFileSize}");
|
||||
|
||||
byte[] loadedData = new byte[iFileSize];
|
||||
result = nn.fs.File.Read(fileHandle, 0, loadedData, iFileSize);
|
||||
if (result.IsSuccess() == false)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"nn.fs.File.Read 失败 {filename} : result=>{result.GetErrorInfo()}");
|
||||
return false;
|
||||
}
|
||||
UnityEngine.Debug.Log($"nn.fs.File.Read 成功 {filename}");
|
||||
|
||||
nn.fs.File.Close(fileHandle);
|
||||
|
||||
//for (int i = 0; i < loadedData.Length; i++)
|
||||
//{
|
||||
// UnityEngine.Debug.Log($"data[{i}]:{loadedData[i]}");
|
||||
//}
|
||||
|
||||
outputData = loadedData;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
public AxiNSWait_LoadSwitchDataFile LoadSwitchDataFileAsync(string filename)
|
||||
{
|
||||
var wait = new AxiNSWait_LoadSwitchDataFile(filename);
|
||||
AxiNS.instance.wait.AddWait(wait);
|
||||
return wait;
|
||||
}
|
||||
|
||||
public bool GetDirectoryFiles(string path, out string[] entrys)
|
||||
{
|
||||
#if !UNITY_SWITCH || UNITY_EDITOR
|
||||
|
||||
entrys = null;
|
||||
return false;
|
||||
#else
|
||||
return GetDirectoryEntrys(path,nn.fs.OpenDirectoryMode.File,out entrys);
|
||||
#endif
|
||||
}
|
||||
|
||||
public bool GetDirectoryDirs(string path, out string[] entrys)
|
||||
{
|
||||
#if !UNITY_SWITCH || UNITY_EDITOR
|
||||
entrys = null;
|
||||
return false;
|
||||
#else
|
||||
return GetDirectoryEntrys(path, nn.fs.OpenDirectoryMode.Directory, out entrys);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_SWITCH
|
||||
public bool GetDirectoryEntrys(string path, nn.fs.OpenDirectoryMode type, out string[] entrys)
|
||||
{
|
||||
nn.fs.DirectoryHandle eHandle = new nn.fs.DirectoryHandle();
|
||||
nn.Result result = nn.fs.Directory.Open(ref eHandle, path, type);
|
||||
if (nn.fs.FileSystem.ResultPathNotFound.Includes(result))
|
||||
{
|
||||
UnityEngine.Debug.Log($"目录 {path} 不存在");
|
||||
entrys = null;
|
||||
return false;
|
||||
}
|
||||
long entryCount = 0;
|
||||
nn.fs.Directory.GetEntryCount(ref entryCount, eHandle);
|
||||
nn.fs.DirectoryEntry[] entries = new nn.fs.DirectoryEntry[entryCount];
|
||||
long actualEntries = 0;
|
||||
nn.fs.Directory.Read(ref actualEntries, entries, eHandle, entryCount);
|
||||
|
||||
entrys = new string[actualEntries];
|
||||
for (int i = 0; i < actualEntries; i++)
|
||||
{
|
||||
entrys[i] = System.IO.Path.Combine(path, entries[i].name);
|
||||
}
|
||||
nn.fs.Directory.Close(eHandle);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
public bool GetDirectoryEntrysFullRecursion(string path, out string[] entrys)
|
||||
{
|
||||
#if UNITY_SWITCH
|
||||
|
||||
nn.fs.DirectoryHandle eHandle = new nn.fs.DirectoryHandle();
|
||||
nn.Result result = nn.fs.Directory.Open(ref eHandle, path, OpenDirectoryMode.All);
|
||||
if (nn.fs.FileSystem.ResultPathNotFound.Includes(result))
|
||||
{
|
||||
UnityEngine.Debug.Log($"目录 {path} 不存在");
|
||||
entrys = null;
|
||||
return false;
|
||||
}
|
||||
long entryCount = 0;
|
||||
nn.fs.Directory.GetEntryCount(ref entryCount, eHandle);
|
||||
nn.fs.DirectoryEntry[] entries = new nn.fs.DirectoryEntry[entryCount];
|
||||
long actualEntries = 0;
|
||||
nn.fs.Directory.Read(ref actualEntries, entries, eHandle, entryCount);
|
||||
|
||||
List<string> temp = new List<string>();
|
||||
for (int i = 0; i < actualEntries; i++)
|
||||
{
|
||||
string singlePath = System.IO.Path.Combine(path, entries[i].name);
|
||||
temp.Add(singlePath);
|
||||
if (entries[i].entryType == EntryType.Directory && GetDirectoryEntrysFullRecursion(singlePath, out string[] singleEntryList))
|
||||
{
|
||||
temp.AddRange(singleEntryList);
|
||||
}
|
||||
}
|
||||
nn.fs.Directory.Close(eHandle);
|
||||
entrys = temp.ToArray();
|
||||
return true;
|
||||
#else
|
||||
entrys = null;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
public IEnumerable<string> EnumerateFiles(string path, string searchPattern)
|
||||
{
|
||||
#if !UNITY_SWITCH || UNITY_EDITOR
|
||||
yield break;
|
||||
#else
|
||||
// 将通配符转换为正则表达式(支持*和?)
|
||||
var regexPattern = "^" +
|
||||
Regex.Escape(searchPattern)
|
||||
.Replace("\\*", ".*")
|
||||
.Replace("\\?", ".")
|
||||
+ "$";
|
||||
|
||||
var regex = new Regex(regexPattern, RegexOptions.IgnoreCase);
|
||||
|
||||
if (!GetDirectoryEntrys(path, nn.fs.OpenDirectoryMode.File, out string[] entrys))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < entrys.Length; i++)
|
||||
{
|
||||
if (regex.IsMatch(System.IO.Path.GetFileName(entrys[i])))
|
||||
{
|
||||
yield return entrys[i];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public bool DeletePathFile(string filename)
|
||||
{
|
||||
#if !UNITY_SWITCH
|
||||
return false;
|
||||
#else
|
||||
|
||||
|
||||
#if UNITY_SWITCH && !UNITY_EDITOR
|
||||
// This next line prevents the user from quitting the game while saving.
|
||||
// This is required for Nintendo Switch Guideline 0080
|
||||
UnityEngine.Switch.Notification.EnterExitRequestHandlingSection();
|
||||
#endif
|
||||
|
||||
if (CheckPathNotFound(filename))
|
||||
return false;
|
||||
nn.Result result;
|
||||
result = nn.fs.File.Delete(filename);
|
||||
if (result.IsSuccess() == false)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"nn.fs.File.Delete 失败 {filename} : result=>{result.GetErrorInfo()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if UNITY_SWITCH && !UNITY_EDITOR
|
||||
// End preventing the user from quitting the game while saving.
|
||||
UnityEngine.Switch.Notification.LeaveExitRequestHandlingSection();
|
||||
#endif
|
||||
return CommitSave();
|
||||
|
||||
#endif
|
||||
}
|
||||
public AxiNSWait_DeletePathFile DeletePathFileAsync(string filename)
|
||||
{
|
||||
var wait = new AxiNSWait_DeletePathFile(filename);
|
||||
AxiNS.instance.wait.AddWait(wait);
|
||||
return wait;
|
||||
}
|
||||
public bool DeletePathDir(string filename)
|
||||
{
|
||||
#if !UNITY_SWITCH
|
||||
return false;
|
||||
#else
|
||||
|
||||
#if UNITY_SWITCH && !UNITY_EDITOR
|
||||
// This next line prevents the user from quitting the game while saving.
|
||||
// This is required for Nintendo Switch Guideline 0080
|
||||
UnityEngine.Switch.Notification.EnterExitRequestHandlingSection();
|
||||
#endif
|
||||
|
||||
if (CheckPathNotFound(filename))
|
||||
return false;
|
||||
nn.Result result;
|
||||
result = nn.fs.Directory.Delete(filename);
|
||||
if (result.IsSuccess() == false)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"nn.fs.File.Delete 失败 {filename} : result=>{result.GetErrorInfo()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if UNITY_SWITCH && !UNITY_EDITOR
|
||||
// End preventing the user from quitting the game while saving.
|
||||
UnityEngine.Switch.Notification.LeaveExitRequestHandlingSection();
|
||||
#endif
|
||||
return CommitSave();
|
||||
#endif
|
||||
}
|
||||
public AxiNSWait_DeletePathDir DeletePathDirAsync(string filename)
|
||||
{
|
||||
var wait = new AxiNSWait_DeletePathDir(filename);
|
||||
AxiNS.instance.wait.AddWait(wait);
|
||||
return wait;
|
||||
}
|
||||
public bool DeletePathDirRecursively(string filename)
|
||||
{
|
||||
#if !UNITY_SWITCH
|
||||
return false;
|
||||
#else
|
||||
|
||||
#if UNITY_SWITCH && !UNITY_EDITOR
|
||||
// This next line prevents the user from quitting the game while saving.
|
||||
// This is required for Nintendo Switch Guideline 0080
|
||||
UnityEngine.Switch.Notification.EnterExitRequestHandlingSection();
|
||||
#endif
|
||||
|
||||
if (CheckPathNotFound(filename))
|
||||
return false;
|
||||
nn.Result result;
|
||||
result = nn.fs.Directory.DeleteRecursively(filename);
|
||||
if (result.IsSuccess() == false)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"nn.fs.File.Recursively 失败 {filename} : result=>{result.GetErrorInfo()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if UNITY_SWITCH && !UNITY_EDITOR
|
||||
// End preventing the user from quitting the game while saving.
|
||||
UnityEngine.Switch.Notification.LeaveExitRequestHandlingSection();
|
||||
#endif
|
||||
return CommitSave();
|
||||
#endif
|
||||
}
|
||||
public AxiNSWait_DeletePathDirRecursively DeletePathDirRecursivelyAsync(string filename)
|
||||
{
|
||||
var wait = new AxiNSWait_DeletePathDirRecursively(filename);
|
||||
AxiNS.instance.wait.AddWait(wait);
|
||||
return wait;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 递归删除目录
|
||||
/// </summary>
|
||||
/// <param name="filename"></param>
|
||||
/// <returns></returns>
|
||||
public bool DeleteRecursivelyPathDir(string filename)
|
||||
{
|
||||
#if !UNITY_SWITCH
|
||||
return false;
|
||||
#else
|
||||
|
||||
#if UNITY_SWITCH && !UNITY_EDITOR
|
||||
// This next line prevents the user from quitting the game while saving.
|
||||
// This is required for Nintendo Switch Guideline 0080
|
||||
UnityEngine.Switch.Notification.EnterExitRequestHandlingSection();
|
||||
#endif
|
||||
|
||||
if (CheckPathNotFound(filename))
|
||||
return false;
|
||||
nn.Result result;
|
||||
result = nn.fs.Directory.DeleteRecursively(filename);
|
||||
if (result.IsSuccess() == false)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"nn.fs.File.DeleteRecursively 失败 {filename} : result=>{result.GetErrorInfo()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if UNITY_SWITCH && !UNITY_EDITOR
|
||||
// End preventing the user from quitting the game while saving.
|
||||
UnityEngine.Switch.Notification.LeaveExitRequestHandlingSection();
|
||||
#endif
|
||||
return CommitSave();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 递归删除情况
|
||||
/// </summary>
|
||||
/// <param name="filename"></param>
|
||||
/// <returns></returns>
|
||||
public bool CleanRecursivelyPathDir(string filename)
|
||||
{
|
||||
#if !UNITY_SWITCH
|
||||
return false;
|
||||
#else
|
||||
|
||||
#if UNITY_SWITCH && !UNITY_EDITOR
|
||||
// This next line prevents the user from quitting the game while saving.
|
||||
// This is required for Nintendo Switch Guideline 0080
|
||||
UnityEngine.Switch.Notification.EnterExitRequestHandlingSection();
|
||||
#endif
|
||||
|
||||
if (CheckPathNotFound(filename))
|
||||
return false;
|
||||
nn.Result result;
|
||||
result = nn.fs.Directory.CleanRecursively(filename);
|
||||
if (result.IsSuccess() == false)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"nn.fs.File.DeleteRecursively 失败 {filename} : result=>{result.GetErrorInfo()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if UNITY_SWITCH && !UNITY_EDITOR
|
||||
// End preventing the user from quitting the game while saving.
|
||||
UnityEngine.Switch.Notification.LeaveExitRequestHandlingSection();
|
||||
#endif
|
||||
return CommitSave();
|
||||
#endif
|
||||
}
|
||||
|
||||
public bool RenameDir(string oldpath, string newpath)
|
||||
{
|
||||
#if !UNITY_SWITCH
|
||||
return false;
|
||||
#else
|
||||
|
||||
#if UNITY_SWITCH && !UNITY_EDITOR
|
||||
// This next line prevents the user from quitting the game while saving.
|
||||
// This is required for Nintendo Switch Guideline 0080
|
||||
UnityEngine.Switch.Notification.EnterExitRequestHandlingSection();
|
||||
#endif
|
||||
|
||||
if (CheckPathNotFound(oldpath))
|
||||
return false;
|
||||
|
||||
nn.Result result;
|
||||
result = nn.fs.Directory.Rename(oldpath, newpath);
|
||||
if (result.IsSuccess() == false)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"nn.fs.File.Rename 失败 {oldpath} to {newpath} : result=>{result.GetErrorInfo()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if UNITY_SWITCH && !UNITY_EDITOR
|
||||
// End preventing the user from quitting the game while saving.
|
||||
UnityEngine.Switch.Notification.LeaveExitRequestHandlingSection();
|
||||
#endif
|
||||
return CommitSave();
|
||||
|
||||
#endif
|
||||
}
|
||||
bool EnsureParentDirectory(string filePath, bool bAutoCreateDir = true)
|
||||
{
|
||||
#if !UNITY_SWITCH
|
||||
return false;
|
||||
#else
|
||||
// 参数校验
|
||||
if (string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
UnityEngine.Debug.LogError($"无效参数:filePath={filePath}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 提取路径前缀(如 save:/、sd:/)
|
||||
int prefixEndIndex = filePath.IndexOf(":/");
|
||||
if (prefixEndIndex == -1)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"文件路径 {filePath} 格式无效,未找到 ':/' 前缀");
|
||||
return false;
|
||||
}
|
||||
string pathPrefix = filePath.Substring(0, prefixEndIndex + 2); // 提取前缀,例如 "save:/"
|
||||
string relativePath = filePath.Substring(prefixEndIndex + 2); // 移除前缀,得到相对路径
|
||||
|
||||
// 检查挂载状态
|
||||
if (!IsMountPointAccessible(pathPrefix))
|
||||
{
|
||||
UnityEngine.Debug.LogError($"挂载点 {pathPrefix} 未挂载,无法操作路径 {filePath}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 提取父目录路径
|
||||
string directoryPath = System.IO.Path.GetDirectoryName(relativePath); // 获取父目录相对路径
|
||||
if (string.IsNullOrEmpty(directoryPath))
|
||||
{
|
||||
UnityEngine.Debug.Log($"文件路径 {filePath} 无需创建父目录(位于根目录)");
|
||||
return true; // 根目录无需创建
|
||||
}
|
||||
|
||||
string fullDirectoryPath = $"{pathPrefix}{directoryPath}"; // 拼接完整父目录路径
|
||||
UnityEngine.Debug.Log($"检查父目录: {fullDirectoryPath}");
|
||||
|
||||
// 检查路径是否存在及其类型
|
||||
nn.fs.EntryType entryType = 0;
|
||||
nn.Result result = nn.fs.FileSystem.GetEntryType(ref entryType, fullDirectoryPath);
|
||||
if (!result.IsSuccess() && nn.fs.FileSystem.ResultPathNotFound.Includes(result))
|
||||
{
|
||||
if (bAutoCreateDir)
|
||||
{
|
||||
//List<string> NeedCreateList = new List<string>();
|
||||
//NeedCreateList.Add(fullDirectoryPath);
|
||||
//nn.fs.EntryType entryTypeLoop = 0;
|
||||
//nn.Result resultloop;
|
||||
//string NodeLoop = fullDirectoryPath;
|
||||
//while (!NodeLoop.EndsWith(":/"))
|
||||
//{
|
||||
// NodeLoop = System.IO.Path.GetDirectoryName(NodeLoop);
|
||||
// if (NodeLoop.EndsWith(":/"))
|
||||
// break;
|
||||
// resultloop = nn.fs.FileSystem.GetEntryType(ref entryTypeLoop, NodeLoop);
|
||||
// if (!resultloop.IsSuccess() && nn.fs.FileSystem.ResultPathNotFound.Includes(resultloop))
|
||||
// {
|
||||
// NeedCreateList.Add(NodeLoop);
|
||||
// }
|
||||
//}
|
||||
|
||||
//for(int i = NeedCreateList.Count - 1; i >= 0; i--)
|
||||
//{
|
||||
// string dirToCreate = NeedCreateList[i];
|
||||
// // 路径不存在,尝试创建
|
||||
// UnityEngine.Debug.Log($"父目录 {dirToCreate} 不存在,尝试创建 (判断依据 result=>{result.ToString()})");
|
||||
// resultloop = nn.fs.Directory.Create(dirToCreate);
|
||||
// if (!resultloop.IsSuccess())
|
||||
// {
|
||||
// UnityEngine.Debug.LogError($"创建父目录失败: {resultloop.GetErrorInfo()}");
|
||||
// return false;
|
||||
// }
|
||||
// UnityEngine.Debug.Log($"父目录 {dirToCreate} 创建成功");
|
||||
//}
|
||||
//return true;
|
||||
|
||||
// 路径不存在,尝试创建
|
||||
UnityEngine.Debug.Log($"父目录 {fullDirectoryPath} 不存在,尝试创建 (判断依据 result=>{result.ToString()})");
|
||||
result = nn.fs.Directory.Create(fullDirectoryPath);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
UnityEngine.Debug.LogError($"创建父目录失败: {result.GetErrorInfo()}");
|
||||
return false;
|
||||
}
|
||||
UnityEngine.Debug.Log($"父目录 {fullDirectoryPath} 创建成功");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (result.IsSuccess() && entryType != nn.fs.EntryType.Directory)
|
||||
{
|
||||
// 路径存在,但不是目录
|
||||
UnityEngine.Debug.LogError($"路径 {fullDirectoryPath} 已存在,但不是目录");
|
||||
return false;
|
||||
}
|
||||
else if (!result.IsSuccess())
|
||||
{
|
||||
// 其他错误
|
||||
UnityEngine.Debug.LogError($"检查父目录失败: {result.GetErrorInfo()}");
|
||||
return false;
|
||||
}
|
||||
// 路径存在且是目录
|
||||
UnityEngine.Debug.Log($"父目录 {fullDirectoryPath} 已存在且有效");
|
||||
return true;
|
||||
|
||||
#endif
|
||||
}
|
||||
/// <summary>
|
||||
/// 检查指定挂载点是否可访问
|
||||
/// </summary>
|
||||
/// <param name="pathPrefix">路径前缀,例如 "save:/" 或 "sd:/"</param>
|
||||
/// <returns>挂载点是否可访问</returns>
|
||||
bool IsMountPointAccessible(string pathPrefix)
|
||||
{
|
||||
#if !UNITY_SWITCH
|
||||
return false;
|
||||
#else
|
||||
if (string.IsNullOrEmpty(pathPrefix))
|
||||
{
|
||||
UnityEngine.Debug.LogError($"无效挂载点: {pathPrefix}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 根据前缀判断挂载点类型并检查挂载状态
|
||||
if (pathPrefix == $"{save_name}:/")
|
||||
{
|
||||
if (!AxiNS.instance.mount.SaveIsMount)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"{save_name}:/ 未挂载");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (pathPrefix == "sd:/")
|
||||
{
|
||||
long freeSpace = 0;
|
||||
// 检查 SD 卡挂载状态(示例,需根据实际实现调整)
|
||||
nn.Result result = nn.fs.FileSystem.GetFreeSpaceSize(ref freeSpace, "sd:/");
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
UnityEngine.Debug.LogError($"sd:/ 未挂载或无法访问: {result.GetErrorInfo()}");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"未知挂载点 {pathPrefix},假定已挂载");
|
||||
return true; // 其他挂载点需根据实际需求实现
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d910a015a6b6561418bdff7f2c48cffa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
public class AxiNSMono : MonoBehaviour
|
||||
{
|
||||
Action act;
|
||||
float waittime;
|
||||
float lastinvokeTime;
|
||||
|
||||
public static void SetInvoke(Action _act, int _waitsec)
|
||||
{
|
||||
GameObject gobj = GameObject.Find($"[{nameof(AxiNSMono)}]");
|
||||
if (gobj == null)
|
||||
{
|
||||
gobj = new GameObject();
|
||||
gobj.name = $"[{nameof(AxiNSMono)}]";
|
||||
GameObject.DontDestroyOnLoad(gobj);
|
||||
}
|
||||
AxiNSMono com = gobj.GetComponent<AxiNSMono>();
|
||||
if (com == null)
|
||||
{
|
||||
com = gobj.AddComponent<AxiNSMono>();
|
||||
}
|
||||
com.act = _act;
|
||||
com.waittime = _waitsec;
|
||||
}
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
Debug.Log("AxiNSMono Enable");
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (Time.time - lastinvokeTime < waittime)
|
||||
return;
|
||||
lastinvokeTime = Time.time;
|
||||
if (act != null)
|
||||
act.Invoke();
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 23d5745b8989af04d8a871b5c7b65d50
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,134 @@
|
||||
#if UNITY_SWITCH
|
||||
using nn.account;
|
||||
#endif
|
||||
public class AxiNSMount
|
||||
{
|
||||
static bool bInMount = false;
|
||||
internal static string m_SaveMountName;
|
||||
static bool bInSdCardMount = false;
|
||||
internal static string m_SdCardMountName;
|
||||
static bool bInSdCardDebugMount = false;
|
||||
internal static string m_SdCardDebugMountName;
|
||||
|
||||
public bool SaveIsMount => bInMount;
|
||||
public string SaveMountName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!bInMount)
|
||||
return string.Empty;
|
||||
else
|
||||
return m_SaveMountName;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_SWITCH
|
||||
public bool MountSave(Uid userId, string mountName = "save")
|
||||
{
|
||||
if (bInMount)
|
||||
return true;
|
||||
|
||||
if (!nn.fs.SaveData.IsExisting(userId))
|
||||
{
|
||||
UnityEngine.Debug.LogError($"{userId.ToString()}存档不存在!");
|
||||
return false;
|
||||
}
|
||||
UnityEngine.Debug.Log($"{userId.ToString()}存档确保存在!");
|
||||
|
||||
nn.Result result;
|
||||
result = nn.fs.SaveData.Mount(mountName, userId);
|
||||
//result.abortUnlessSuccess();
|
||||
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
UnityEngine.Debug.LogError($"MountSave->挂载{mountName}:/ 失败: " + result.ToString());
|
||||
return false;
|
||||
}
|
||||
UnityEngine.Debug.Log($"MountSave->挂载{mountName}:/ 成功 ");
|
||||
m_SaveMountName = mountName;
|
||||
bInMount = true;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
public bool MountSDForDebug(string mountName = "dbgsd")
|
||||
{
|
||||
#if !UNITY_SWITCH
|
||||
return false;
|
||||
#else
|
||||
if (bInSdCardDebugMount)
|
||||
return true;
|
||||
nn.Result result;
|
||||
result = nn.fs.SdCard.MountForDebug(mountName);
|
||||
//result.abortUnlessSuccess();
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
UnityEngine.Debug.LogError($"nn_fs_MountSdCardForDebug->挂载{mountName}:/ 失败: " + result.ToString());
|
||||
return false;
|
||||
}
|
||||
UnityEngine.Debug.Log($"nn_fs_MountSdCardForDebug->挂载{mountName}:/ 成功 ");
|
||||
m_SdCardDebugMountName = mountName;
|
||||
bInSdCardDebugMount = true;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
public bool MountSD(string mountName = "sd")
|
||||
{
|
||||
#if !UNITY_SWITCH
|
||||
return false;
|
||||
#else
|
||||
if (bInSdCardMount)
|
||||
return true;
|
||||
nn.Result result;
|
||||
result = AxiNSSDCard.Mount(mountName);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
UnityEngine.Debug.LogError($"nn_fs_MountSdCard->挂载{mountName}:/ 失败: " + result.ToString());
|
||||
return false;
|
||||
}
|
||||
UnityEngine.Debug.Log($"nn_fs_MountSdCard->挂载{mountName}:/ 成功 ");
|
||||
m_SdCardMountName = mountName;
|
||||
bInSdCardMount = true;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
public void UnmountSave()
|
||||
{
|
||||
#if UNITY_SWITCH
|
||||
if (!bInMount)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"{m_SaveMountName}:/ 没有被挂载,无需卸载");
|
||||
return;
|
||||
}
|
||||
nn.fs.FileSystem.Unmount(m_SaveMountName);
|
||||
UnityEngine.Debug.LogError($"UnmountSaveForDebufa->已卸载{m_SaveMountName}:/ ");
|
||||
bInMount = false;
|
||||
#endif
|
||||
}
|
||||
public void UnmountSDCardForDebug()
|
||||
{
|
||||
#if UNITY_SWITCH
|
||||
if (!bInSdCardDebugMount)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"{m_SdCardDebugMountName}:/ 没有被挂载,无需卸载");
|
||||
return;
|
||||
}
|
||||
nn.fs.FileSystem.Unmount(m_SdCardDebugMountName);
|
||||
UnityEngine.Debug.LogError($"UnmountSDCardForDebug->已卸载{m_SdCardDebugMountName}:/ ");
|
||||
bInSdCardDebugMount = false;
|
||||
#endif
|
||||
}
|
||||
public void UnmountSDCard()
|
||||
{
|
||||
#if UNITY_SWITCH
|
||||
if (!bInSdCardMount)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"{m_SdCardMountName}:/ 没有被挂载,无需卸载");
|
||||
return;
|
||||
}
|
||||
nn.fs.FileSystem.Unmount(m_SdCardMountName);
|
||||
UnityEngine.Debug.LogError($"UnmountSDCard->已卸载{m_SdCardMountName}:/ ");
|
||||
bInSdCardMount = false;
|
||||
#endif
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48826c5dc8959ff4db8c6a51b6568bb7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,23 @@
|
||||
#if UNITY_SWITCH
|
||||
using nn.account;
|
||||
#endif
|
||||
public class AxiNSSDCard
|
||||
{
|
||||
#if UNITY_SWITCH
|
||||
|
||||
#if DEVELOPMENT_BUILD || NN_FS_SD_CARD_FOR_DEBUG_ENABLE
|
||||
[DllImport(Nn.DllName,
|
||||
CallingConvention = CallingConvention.Cdecl,
|
||||
EntryPoint = "nn_fs_MountSdCard")]
|
||||
public static extern nn.Result Mount(string name);
|
||||
#else
|
||||
|
||||
public static nn.Result Mount(string name)
|
||||
{
|
||||
return new nn.Result();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21fa04ba4da10d74aafd65dd138478b7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,106 @@
|
||||
#if UNITY_SWITCH
|
||||
using nn.account;
|
||||
#endif
|
||||
|
||||
public class AxiNSUser
|
||||
{
|
||||
bool m_bInit = false;
|
||||
bool m_bGotOpenPreselectedUser = false;
|
||||
|
||||
#if UNITY_SWITCH
|
||||
Uid m_UserId;
|
||||
nn.account.UserHandle mUserHandle;
|
||||
nn.account.Nickname m_NickName;
|
||||
#endif
|
||||
|
||||
#region 对外公开接口,确保内部安全处理,避免崩溃
|
||||
|
||||
#if UNITY_SWITCH
|
||||
public bool GetUserID(out Uid uid)
|
||||
{
|
||||
InitPreselectedUserInfo();
|
||||
if (!m_bGotOpenPreselectedUser)
|
||||
{
|
||||
uid = Uid.Invalid;
|
||||
return false;
|
||||
}
|
||||
uid = m_UserId;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
public bool GetNickName(out string NickName)
|
||||
{
|
||||
#if !UNITY_SWITCH
|
||||
NickName = "";
|
||||
return true;
|
||||
#else
|
||||
InitPreselectedUserInfo();
|
||||
if (!m_bGotOpenPreselectedUser)
|
||||
{
|
||||
NickName = string.Empty;
|
||||
return false;
|
||||
}
|
||||
NickName = m_NickName.ToString();
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
#endregion
|
||||
/// <summary>
|
||||
/// 初始化Account模块儿
|
||||
/// </summary>
|
||||
void InitNSAccount()
|
||||
{
|
||||
#if UNITY_SWITCH
|
||||
if (m_bInit)
|
||||
return;
|
||||
//必须先初始化NS的Account 不然调用即崩
|
||||
nn.account.Account.Initialize();
|
||||
m_bInit = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取预选用户
|
||||
/// </summary>
|
||||
void InitPreselectedUserInfo()
|
||||
{
|
||||
#if UNITY_SWITCH
|
||||
if (m_bGotOpenPreselectedUser)
|
||||
return;
|
||||
|
||||
InitNSAccount();
|
||||
nn.Result result;
|
||||
mUserHandle = new nn.account.UserHandle();
|
||||
if (!nn.account.Account.TryOpenPreselectedUser(ref mUserHandle))
|
||||
{
|
||||
UnityEngine.Debug.LogError("打开预选的用户失败.");
|
||||
return;
|
||||
}
|
||||
UnityEngine.Debug.Log("打开预选用户成功.");
|
||||
result = nn.account.Account.GetUserId(ref m_UserId, mUserHandle);
|
||||
//result.abortUnlessSuccess();
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
UnityEngine.Debug.LogError($"GetUserId失败: {result.ToString()}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_UserId == Uid.Invalid)
|
||||
{
|
||||
UnityEngine.Debug.LogError("无法获取用户 ID");
|
||||
return;
|
||||
}
|
||||
UnityEngine.Debug.Log($"获取用户 ID:{m_UserId.ToString()}");
|
||||
|
||||
result = nn.account.Account.GetNickname(ref m_NickName, m_UserId);
|
||||
//result.abortUnlessSuccess();
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
UnityEngine.Debug.LogError($"GetNickname失败: {result.ToString()}");
|
||||
return;
|
||||
}
|
||||
UnityEngine.Debug.Log($"获取用户 NickName ID:{m_NickName.ToString()}");
|
||||
m_bGotOpenPreselectedUser = true;
|
||||
#endif
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95c2e164c69c6cc4887a194d6eba0cc2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e28ed9d2fb16c7f42b28cafb6a2ce0ac
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,134 @@
|
||||
public abstract class AxiNSWaitBase : UnityEngine.CustomYieldInstruction
|
||||
{
|
||||
protected bool IsDone;
|
||||
public abstract void Invoke();
|
||||
public string errmsg = string.Empty;
|
||||
public AxiNSWaitBase()
|
||||
{
|
||||
this.IsDone = false;
|
||||
}
|
||||
|
||||
public void SetDone()
|
||||
{
|
||||
this.IsDone = true;
|
||||
}
|
||||
|
||||
~AxiNSWaitBase()
|
||||
{
|
||||
}
|
||||
|
||||
public override bool keepWaiting
|
||||
{
|
||||
get { return !IsDone; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public struct S_NSWAIT_PathWithBytes
|
||||
{
|
||||
public string filePath;
|
||||
public byte[] data;
|
||||
}
|
||||
|
||||
public class AxiNSWait_FileToSaveWithCreate : AxiNSWaitBase
|
||||
{
|
||||
S_NSWAIT_PathWithBytes req;
|
||||
public bool result;
|
||||
public AxiNSWait_FileToSaveWithCreate(string filePath, byte[] data)
|
||||
{
|
||||
req = new S_NSWAIT_PathWithBytes() { filePath = filePath, data = data };
|
||||
}
|
||||
|
||||
public override void Invoke()
|
||||
{
|
||||
result = AxiNS.instance.io.FileToSaveWithCreate(req.filePath, req.data);
|
||||
}
|
||||
}
|
||||
|
||||
public struct S_NSWAIT_PathWithMS
|
||||
{
|
||||
public string filePath;
|
||||
public System.IO.MemoryStream ms;
|
||||
}
|
||||
|
||||
public class AxiNSWait_FileToSaveByMSWithCreate : AxiNSWaitBase
|
||||
{
|
||||
S_NSWAIT_PathWithMS req;
|
||||
public bool result;
|
||||
public AxiNSWait_FileToSaveByMSWithCreate(string filePath, System.IO.MemoryStream ms)
|
||||
{
|
||||
req = new S_NSWAIT_PathWithMS() { filePath = filePath, ms = ms };
|
||||
}
|
||||
|
||||
public override void Invoke()
|
||||
{
|
||||
result = AxiNS.instance.io.FileToSaveWithCreate(req.filePath, req.ms);
|
||||
}
|
||||
}
|
||||
|
||||
public struct S_NSWAIT_Path
|
||||
{
|
||||
public string filePath;
|
||||
}
|
||||
|
||||
public class AxiNSWait_LoadSwitchDataFile : AxiNSWaitBase
|
||||
{
|
||||
S_NSWAIT_Path req;
|
||||
public bool result;
|
||||
public byte[] outputData;
|
||||
public AxiNSWait_LoadSwitchDataFile(string filePath)
|
||||
{
|
||||
req = new S_NSWAIT_Path() { filePath = filePath};
|
||||
}
|
||||
|
||||
public override void Invoke()
|
||||
{
|
||||
result = AxiNS.instance.io.LoadSwitchDataFile(req.filePath, out outputData);
|
||||
}
|
||||
}
|
||||
|
||||
public class AxiNSWait_DeletePathFile : AxiNSWaitBase
|
||||
{
|
||||
S_NSWAIT_Path req;
|
||||
public bool result;
|
||||
public AxiNSWait_DeletePathFile(string filePath)
|
||||
{
|
||||
req = new S_NSWAIT_Path() { filePath = filePath };
|
||||
}
|
||||
|
||||
public override void Invoke()
|
||||
{
|
||||
result = AxiNS.instance.io.DeletePathFile(req.filePath);
|
||||
}
|
||||
}
|
||||
|
||||
public class AxiNSWait_DeletePathDir : AxiNSWaitBase
|
||||
{
|
||||
S_NSWAIT_Path req;
|
||||
public bool result;
|
||||
public AxiNSWait_DeletePathDir(string filePath)
|
||||
{
|
||||
req = new S_NSWAIT_Path() { filePath = filePath };
|
||||
}
|
||||
|
||||
public override void Invoke()
|
||||
{
|
||||
result = AxiNS.instance.io.DeletePathDir(req.filePath);
|
||||
}
|
||||
}
|
||||
|
||||
public class AxiNSWait_DeletePathDirRecursively : AxiNSWaitBase
|
||||
{
|
||||
S_NSWAIT_Path req;
|
||||
public bool result;
|
||||
public AxiNSWait_DeletePathDirRecursively(string filePath)
|
||||
{
|
||||
req = new S_NSWAIT_Path() { filePath = filePath };
|
||||
}
|
||||
|
||||
public override void Invoke()
|
||||
{
|
||||
result = AxiNS.instance.io.DeletePathDirRecursively(req.filePath);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 46a1a776d2f9dba49b9641d8e0976861
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
public class AxiNSWaitHandle
|
||||
{
|
||||
static Queue<AxiNSWaitBase> m_QueueReady = new Queue<AxiNSWaitBase>();
|
||||
static Queue<AxiNSWaitBase> m_QueueWork = new Queue<AxiNSWaitBase>();
|
||||
public void AddWait(AxiNSWaitBase wait)
|
||||
{
|
||||
lock (m_QueueReady)
|
||||
{
|
||||
m_QueueReady.Enqueue(wait);
|
||||
}
|
||||
|
||||
if (AxiNS.usedmultithreading)
|
||||
{
|
||||
InitInternalThread();
|
||||
autoEvent.Set();
|
||||
}
|
||||
else
|
||||
{
|
||||
InitMonoInit();
|
||||
}
|
||||
}
|
||||
|
||||
#region 多线程实现
|
||||
static AutoResetEvent autoEvent = new AutoResetEvent(false);
|
||||
static Thread waitThread = new Thread(Loop);
|
||||
static bool bSingleInit = false;
|
||||
static void InitInternalThread()
|
||||
{
|
||||
if (bSingleInit) return;
|
||||
waitThread.Start();
|
||||
bSingleInit = true;
|
||||
}
|
||||
|
||||
static void Loop()
|
||||
{
|
||||
while (autoEvent.WaitOne())
|
||||
{
|
||||
Do();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 主线程时间间隔实现
|
||||
static bool bMonoInit = false;
|
||||
static void InitMonoInit()
|
||||
{
|
||||
if (bMonoInit) return;
|
||||
AxiNSMono.SetInvoke(Do,15);
|
||||
bMonoInit = true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
static void Do()
|
||||
{
|
||||
lock (m_QueueReady)
|
||||
{
|
||||
while (m_QueueReady.Count > 0)
|
||||
{
|
||||
m_QueueWork.Enqueue(m_QueueReady.Dequeue());
|
||||
}
|
||||
}
|
||||
while (m_QueueWork.Count > 0)
|
||||
{
|
||||
AxiNSWaitBase wait = m_QueueWork.Dequeue();
|
||||
try
|
||||
{
|
||||
wait.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
wait.errmsg = ex.ToString();
|
||||
UnityEngine.Debug.Log(ex.ToString());
|
||||
}
|
||||
wait.SetDone();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e36180ba1c4a8f4db3ceed533a43999
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 86a02c697fd26264cb5ee552b582449b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "AxiReplay"
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a45db2096af23647aaafe5b70ccb4d7
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace AxiReplay
|
||||
{
|
||||
public partial class FrameProfiler
|
||||
{
|
||||
private int m_headFrame;
|
||||
private int m_cacheCount;
|
||||
private int m_targetFrameRate;
|
||||
private RingBuffer<double> m_timePoints;
|
||||
private double m_lastTime;
|
||||
|
||||
private Stopwatch sw;
|
||||
|
||||
public void InputHead(int headFrame)
|
||||
{
|
||||
m_headFrame = headFrame;
|
||||
var currentTimeMs = GetCurrTime();
|
||||
|
||||
if (m_timePoints.Available() == 60)
|
||||
CalcCacheCount();
|
||||
m_timePoints.Write(currentTimeMs - m_lastTime);
|
||||
|
||||
m_lastTime = currentTimeMs;
|
||||
}
|
||||
public void Reset(int targetFrameRate = 60)
|
||||
{
|
||||
if (sw != null) sw.Stop();
|
||||
|
||||
sw = Stopwatch.StartNew();
|
||||
m_timePoints = new RingBuffer<double>(targetFrameRate);
|
||||
m_lastTime = 0;
|
||||
m_targetFrameRate = targetFrameRate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 临时方法(暂行)
|
||||
/// </summary>
|
||||
/// <param name="mRemoteForwardCount"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public int TempFrameCount(int mRemoteForwardCount)
|
||||
{
|
||||
if (mRemoteForwardCount == 0)
|
||||
return 0;
|
||||
if (mRemoteForwardCount < 5)
|
||||
return 1;
|
||||
else
|
||||
return (int)Math.Ceiling(mRemoteForwardCount / 5f);
|
||||
}
|
||||
|
||||
void CalcCacheCount()
|
||||
{
|
||||
double deltaMax = 0;
|
||||
while (m_timePoints.TryRead(out double delta))
|
||||
{
|
||||
deltaMax = Math.Max(deltaMax, delta);
|
||||
}
|
||||
|
||||
int minCacheCount = (int)Math.Ceiling(deltaMax * m_targetFrameRate);
|
||||
m_cacheCount = minCacheCount;
|
||||
}
|
||||
|
||||
double GetCurrTime()
|
||||
{
|
||||
if (sw == null) return 0;
|
||||
return sw.Elapsed.TotalMilliseconds;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cfd4511a83ff0bf4ea7615b87e7d09aa
|
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace AxiReplay
|
||||
{
|
||||
internal interface IReplayReader : IDisposable
|
||||
{
|
||||
bool NextFrame(out ReplayStep data);
|
||||
bool TakeFrame(int addFrame, out ReplayStep data);
|
||||
bool NextFramebyFrameIdx(int FrameID, out ReplayStep data);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 896ff07370157db46b612575616020ed
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace AxiReplay
|
||||
{
|
||||
internal interface IReplayWriter : IDisposable
|
||||
{
|
||||
void NextFrame(UInt64 frameInput);
|
||||
void NextFramebyFrameIdx(int FrameID, UInt64 frameInput);
|
||||
void TakeFrame(int addFrame, UInt64 frameInput);
|
||||
void SaveData(string path, bool bNeedDump = false, string dumpFilePath = null);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a8fcda365e5a7f428f88bc130eb913b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,78 @@
|
||||
using System.Threading;
|
||||
|
||||
namespace AxiReplay
|
||||
{
|
||||
public partial class FrameProfiler
|
||||
{
|
||||
internal class RingBuffer<T>
|
||||
{
|
||||
private readonly T[] buffer;
|
||||
private readonly int capacity;
|
||||
private int writePos;
|
||||
private int readPos;
|
||||
private int count;
|
||||
|
||||
public RingBuffer(int capacity)
|
||||
{
|
||||
this.capacity = capacity;
|
||||
this.buffer = new T[capacity];
|
||||
this.writePos = 0;
|
||||
this.readPos = 0;
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
public void Write(T item)
|
||||
{
|
||||
int localWritePos;
|
||||
int localReadPos;
|
||||
|
||||
do
|
||||
{
|
||||
localWritePos = Volatile.Read(ref writePos);
|
||||
localReadPos = Volatile.Read(ref readPos);
|
||||
|
||||
int nextWritePos = (localWritePos + 1) % capacity;
|
||||
|
||||
if (nextWritePos == localReadPos)
|
||||
{
|
||||
// 缓冲区已满,覆盖最旧的未读数据
|
||||
Interlocked.CompareExchange(ref readPos, (localReadPos + 1) % capacity, localReadPos);
|
||||
}
|
||||
}
|
||||
while (Interlocked.CompareExchange(ref writePos, (localWritePos + 1) % capacity, localWritePos) != localWritePos);
|
||||
|
||||
buffer[localWritePos] = item;
|
||||
Interlocked.Increment(ref count);
|
||||
}
|
||||
|
||||
public bool TryRead(out T item)
|
||||
{
|
||||
item = default(T);
|
||||
|
||||
int localReadPos;
|
||||
int localWritePos;
|
||||
|
||||
do
|
||||
{
|
||||
localReadPos = Volatile.Read(ref readPos);
|
||||
localWritePos = Volatile.Read(ref writePos);
|
||||
|
||||
if (localReadPos == localWritePos)
|
||||
{
|
||||
return false; // 缓冲区为空
|
||||
}
|
||||
}
|
||||
while (Interlocked.CompareExchange(ref readPos, (localReadPos + 1) % capacity, localReadPos) != localReadPos);
|
||||
|
||||
item = buffer[localReadPos];
|
||||
Interlocked.Decrement(ref count);
|
||||
return true;
|
||||
}
|
||||
|
||||
public int Available()
|
||||
{
|
||||
return Volatile.Read(ref count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b17d83b69bd47094594c32fcff9715f4
|
@ -0,0 +1,161 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AxiReplay
|
||||
{
|
||||
public class NetReplay
|
||||
{
|
||||
/// <summary>
|
||||
/// 客户端当前帧
|
||||
/// </summary>
|
||||
public int mCurrClientFrameIdx = 0;
|
||||
/// <summary>
|
||||
/// 服务器远端当前帧
|
||||
/// </summary>
|
||||
public int mRemoteFrameIdx { get; private set; }
|
||||
/// <summary>
|
||||
/// 服务器远端当前提前量
|
||||
/// </summary>
|
||||
public int mRemoteForwardCount { get; private set; }
|
||||
/// <summary>
|
||||
/// Remote 2 Client Frame Gap
|
||||
/// </summary>
|
||||
public int mDiffFrameCount => mRemoteFrameIdx - mCurrClientFrameIdx;
|
||||
/// <summary>
|
||||
/// 网络数据队列
|
||||
/// </summary>
|
||||
public Queue<ReplayStep> mNetReplayQueue { get; private set; } = new Queue<ReplayStep>();
|
||||
/// <summary>
|
||||
/// 当前数据
|
||||
/// </summary>
|
||||
ReplayStep mCurrReplay;
|
||||
/// <summary>
|
||||
/// 下一个数据数据
|
||||
/// </summary>
|
||||
ReplayStep mNextReplay;
|
||||
|
||||
FrameProfiler frameProfiler = new FrameProfiler();
|
||||
|
||||
bool bNetInit = false;
|
||||
public NetReplay()
|
||||
{
|
||||
ResetData();
|
||||
}
|
||||
public void ResetData()
|
||||
{
|
||||
mNetReplayQueue.Clear();
|
||||
mCurrReplay = default(ReplayStep);
|
||||
mCurrReplay.FrameStartID = int.MinValue;
|
||||
bNetInit = false;
|
||||
|
||||
frameProfiler.Reset();
|
||||
}
|
||||
public void InData(ReplayStep inputData, int ServerFrameIdx, uint ServerForwardCount)
|
||||
{
|
||||
mRemoteForwardCount = (int)ServerForwardCount;
|
||||
mNetReplayQueue.Enqueue(inputData);
|
||||
Debug.Log($"InData=>{inputData.FrameStartID} QCount = >{mNetReplayQueue.Count}");
|
||||
mRemoteFrameIdx = inputData.FrameStartID;
|
||||
if (!bNetInit)
|
||||
{
|
||||
bNetInit = true;
|
||||
mNextReplay = mNetReplayQueue.Dequeue();
|
||||
}
|
||||
|
||||
frameProfiler.InputHead(inputData.FrameStartID);
|
||||
}
|
||||
|
||||
public bool TryGetNextFrame(int targetFrame, bool indirectGet, out ReplayStep data, out int frameDiff, out bool inputDiff)
|
||||
{
|
||||
if (!bNetInit)
|
||||
{
|
||||
data = default(ReplayStep);
|
||||
frameDiff = default(int);
|
||||
inputDiff = false;
|
||||
return false;
|
||||
}
|
||||
return TakeFrameToTargetFrame(targetFrame, indirectGet, out data, out frameDiff, out inputDiff);
|
||||
}
|
||||
|
||||
bool checkCanGetFrame(int targetFrame, bool indirectGet)
|
||||
{
|
||||
if (indirectGet)
|
||||
{
|
||||
return targetFrame == mNextReplay.FrameStartID && targetFrame <= mRemoteFrameIdx;
|
||||
}
|
||||
else
|
||||
{
|
||||
return targetFrame == mNextReplay.FrameStartID && targetFrame <= mRemoteFrameIdx && mNetReplayQueue.Count >= frameProfiler.TempFrameCount(mRemoteForwardCount);
|
||||
}
|
||||
}
|
||||
|
||||
bool TakeFrameToTargetFrame(int targetFrame, bool indirectGet, out ReplayStep data, out int bFrameDiff, out bool inputDiff)
|
||||
{
|
||||
bool result;
|
||||
inputDiff = false;
|
||||
|
||||
if (checkCanGetFrame(targetFrame, indirectGet))
|
||||
{
|
||||
//当前帧追加
|
||||
mCurrClientFrameIdx = targetFrame;
|
||||
ulong oldInput = mCurrReplay.InPut;
|
||||
mCurrReplay = mNextReplay;
|
||||
if (oldInput != mCurrReplay.InPut)
|
||||
inputDiff = true;
|
||||
mNextReplay = mNetReplayQueue.Dequeue();
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
result = false;
|
||||
|
||||
bFrameDiff = mRemoteFrameIdx - mCurrClientFrameIdx;
|
||||
data = mCurrReplay;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public int GetSkipFrameCount()
|
||||
{
|
||||
if (!bNetInit)
|
||||
return 0;
|
||||
//本地队列差异高于服务器提前量的值
|
||||
int moreNum = mDiffFrameCount - mRemoteForwardCount;
|
||||
//if (mDiffFrameCount < 0 || mDiffFrameCount > 10000)
|
||||
// return 0;
|
||||
|
||||
////游戏刚开始的一小段时间,直接追满
|
||||
//if (mCurrClientFrameIdx < 60)
|
||||
// return moreNum;
|
||||
|
||||
int skip = 0;
|
||||
if (mDiffFrameCount > short.MaxValue) skip = 0;
|
||||
else if (moreNum <= mRemoteForwardCount) skip = 0;
|
||||
else if (moreNum <= mRemoteForwardCount + 2) skip = 0;
|
||||
else if (moreNum <= mRemoteForwardCount + 5) skip = 1;
|
||||
else if (moreNum <= mRemoteForwardCount + 6) skip = 2;
|
||||
else if (moreNum <= mRemoteForwardCount + 20) skip = moreNum / 2; //20帧以内,平滑跳帧数
|
||||
else skip = moreNum;//完全追上
|
||||
return skip;
|
||||
|
||||
//int skip = 0;
|
||||
//if (mDiffFrameCount > short.MaxValue) skip = 0;
|
||||
//else if (moreNum <= 1) skip = 0;
|
||||
//else if (moreNum <= 3) skip = 2;
|
||||
//else if (moreNum <= 6) skip = 2;
|
||||
//else if (moreNum <= 20) skip = moreNum / 2; //20帧以内,平滑跳帧数
|
||||
//else skip = moreNum;//完全追上
|
||||
//return skip;
|
||||
|
||||
//var frameGap = mDiffFrameCount;
|
||||
//if (frameGap > 10000) return 0;
|
||||
//if (frameGap <= 2) skip = 0;
|
||||
//if (frameGap > 2 && frameGap < 6) skip = 1 + 1;
|
||||
//else if (frameGap > 7 && frameGap < 12) skip = 2 + 1;
|
||||
//else if (frameGap > 13 && frameGap < 20) skip = 3 + 1;
|
||||
//else skip = frameGap - 2;
|
||||
|
||||
|
||||
//return skip;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 452b58ff73a0853449845fd9e1134cc2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace AxiReplay
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Size = 44)]
|
||||
public struct ReplayHandler
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public int Format;
|
||||
[FieldOffset(sizeof(int) * 1)]
|
||||
public int RomID;
|
||||
[FieldOffset(sizeof(int) * 2)]
|
||||
public int RomType;
|
||||
[FieldOffset(sizeof(int) * 3)]
|
||||
public int DataOffset;
|
||||
[FieldOffset(sizeof(int) * 4)]
|
||||
public int TitleOffset;
|
||||
[FieldOffset(sizeof(int) * 5)]
|
||||
public int NoteOffset;
|
||||
[FieldOffset(sizeof(int) * 6)]
|
||||
public int AllFrame;
|
||||
[FieldOffset(sizeof(int) * 7)]
|
||||
public int AllTime;
|
||||
[FieldOffset(sizeof(int) * 8)]
|
||||
public int SingleLenght;
|
||||
[FieldOffset(sizeof(int) * 9)]
|
||||
public long CreateTime;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct ReplayStep
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public UInt64 All64Data;
|
||||
[FieldOffset(0)]
|
||||
public Int32 FrameStartID;
|
||||
[FieldOffset(4)]
|
||||
public UInt64 InPut;
|
||||
}
|
||||
|
||||
public static class ReplayData
|
||||
{
|
||||
public static int HandlerLenght = sizeof(int) * 9 + sizeof(long);
|
||||
public enum ReplayFormat : byte
|
||||
{
|
||||
None = 0,
|
||||
FM32IPBYTE,
|
||||
FM32IP16,
|
||||
FM32IP32,
|
||||
FM32IP64,
|
||||
}
|
||||
public static void GetStringByteData(string str, out byte[] data, out int lenghtWithEnd, Encoding encoding)
|
||||
{
|
||||
data = encoding.GetBytes(str);
|
||||
lenghtWithEnd = data.Length + 1;
|
||||
}
|
||||
|
||||
public static byte[] GetHandlerData(ReplayHandler replayhandler)
|
||||
{
|
||||
int size = Marshal.SizeOf(typeof(ReplayHandler));
|
||||
byte[] arr = new byte[size];
|
||||
|
||||
IntPtr ptr = Marshal.AllocHGlobal(size);
|
||||
try
|
||||
{
|
||||
Marshal.StructureToPtr(replayhandler, ptr, false);
|
||||
Marshal.Copy(ptr, arr, 0, size);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
public static ReplayHandler GetReplayHandlerFromData(byte[] data)
|
||||
{
|
||||
if (data == null || data.Length < ReplayData.HandlerLenght)
|
||||
{
|
||||
throw new ArgumentException("Invalid data length or null data.");
|
||||
}
|
||||
|
||||
IntPtr ptr = Marshal.AllocHGlobal(ReplayData.HandlerLenght);
|
||||
try
|
||||
{
|
||||
// 将byte数组的内容复制到非托管内存中
|
||||
Marshal.Copy(data, 0, ptr, ReplayData.HandlerLenght);
|
||||
// 从非托管内存将内容转换回ReplayHandler结构体
|
||||
return (ReplayHandler)Marshal.PtrToStructure(ptr, typeof(ReplayHandler));
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 释放非托管内存
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42df5a138f4f4ae488815f35d8e748da
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,174 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using static AxiReplay.ReplayData;
|
||||
|
||||
namespace AxiReplay
|
||||
{
|
||||
public class ReplayReader : IReplayReader
|
||||
{
|
||||
public ReplayData.ReplayFormat mFormat { get; private set; }
|
||||
public Encoding TexEncoding { get; private set; }
|
||||
ReplayHandler handler;
|
||||
string mTitle;
|
||||
string mNote;
|
||||
int mAllFrame;
|
||||
int mAllTime;
|
||||
long mData;
|
||||
int mSingleInputLenght;
|
||||
int mSingleDataLenght;
|
||||
FileStream mStream;
|
||||
BinaryReader mBinaryReader;
|
||||
|
||||
int mCurrFrame = -1;
|
||||
byte[] mNextOutbytes;
|
||||
public ReplayStep currStep;
|
||||
public ReplayStep nextStep;
|
||||
bool bEnd;
|
||||
|
||||
List<string> dbgList = new List<string>();
|
||||
bool bdbg = false;
|
||||
string dumpPath;
|
||||
|
||||
public ReplayReader(string path, bool bWithDump = false, string dumppath = null)
|
||||
{
|
||||
dbgList.Clear();
|
||||
bdbg = bWithDump;
|
||||
dumpPath = dumppath;
|
||||
mStream = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||
mBinaryReader = new BinaryReader(mStream);
|
||||
byte[] Outbytes;
|
||||
Outbytes = mBinaryReader.ReadBytes(ReplayData.HandlerLenght);
|
||||
handler = ReplayData.GetReplayHandlerFromData(Outbytes);
|
||||
mFormat = (ReplayFormat)handler.Format;
|
||||
switch (mFormat)
|
||||
{
|
||||
case ReplayData.ReplayFormat.FM32IP64: mSingleInputLenght = sizeof(UInt64); break;
|
||||
case ReplayData.ReplayFormat.FM32IP32: mSingleInputLenght = sizeof(UInt32); break;
|
||||
case ReplayData.ReplayFormat.FM32IP16: mSingleInputLenght = sizeof(UInt16); break;
|
||||
case ReplayData.ReplayFormat.FM32IPBYTE: mSingleInputLenght = sizeof(byte); break;
|
||||
}
|
||||
//Frame+Lenght
|
||||
mSingleDataLenght = (sizeof(UInt32)) + mSingleInputLenght;
|
||||
nextStep = new ReplayStep();
|
||||
nextStep.FrameStartID = -1;
|
||||
bEnd = false;
|
||||
|
||||
dbgList.Add($"Format => {handler.Format}");
|
||||
dbgList.Add($"DataOffset => {handler.DataOffset}");
|
||||
dbgList.Add($"CreateTime => {handler.CreateTime}");
|
||||
dbgList.Add($"AllFrame => {handler.AllFrame}");
|
||||
dbgList.Add($"SingleLenght => {handler.SingleLenght}");
|
||||
|
||||
|
||||
mNextOutbytes = new byte[mSingleDataLenght];
|
||||
|
||||
if (bWithDump)
|
||||
{
|
||||
int TestFrameIdx = -1;
|
||||
while (!bEnd)
|
||||
{
|
||||
UpdateNextFrame(TestFrameIdx++);
|
||||
}
|
||||
File.WriteAllLines(dumppath, dbgList);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateNextFrame(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void UpdateNextFrame(int targetFrame)
|
||||
{
|
||||
//如果已经超过
|
||||
while (targetFrame >= nextStep.FrameStartID)
|
||||
{
|
||||
if (nextStep.FrameStartID >= handler.AllFrame)
|
||||
{
|
||||
bEnd = true;
|
||||
break;
|
||||
}
|
||||
mBinaryReader.Read(mNextOutbytes, 0, mSingleDataLenght);
|
||||
switch (mFormat)
|
||||
{
|
||||
case ReplayFormat.FM32IP64:
|
||||
{
|
||||
nextStep.FrameStartID = BitConverter.ToInt32(mNextOutbytes, 0);
|
||||
nextStep.InPut = BitConverter.ToUInt64(mNextOutbytes, sizeof(UInt32));
|
||||
}
|
||||
break;
|
||||
case ReplayFormat.FM32IP32:
|
||||
{
|
||||
nextStep.All64Data = BitConverter.ToUInt64(mNextOutbytes, 0);
|
||||
}
|
||||
break;
|
||||
case ReplayFormat.FM32IP16:
|
||||
{
|
||||
nextStep.All64Data = BitConverter.ToUInt64(mNextOutbytes, 0);
|
||||
}
|
||||
break;
|
||||
case ReplayFormat.FM32IPBYTE:
|
||||
{
|
||||
nextStep.All64Data = BitConverter.ToUInt64(mNextOutbytes, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
dbgList.Add($"{nextStep.FrameStartID} | {nextStep.InPut}");
|
||||
|
||||
targetFrame++;
|
||||
}
|
||||
}
|
||||
|
||||
int byFrameIdx = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 往前推进1帧的Input(返回是否变化)
|
||||
/// </summary>
|
||||
public bool NextFrame(out ReplayStep data)
|
||||
{
|
||||
return TakeFrame(1, out data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 往前推进指定帧数量的Input (返回是否变化)
|
||||
/// </summary>
|
||||
/// <param name="addFrame"></param>
|
||||
public bool TakeFrame(int addFrame, out ReplayStep data)
|
||||
{
|
||||
bool Changed = false;
|
||||
mCurrFrame += addFrame;
|
||||
if (mCurrFrame >= nextStep.FrameStartID)
|
||||
{
|
||||
Changed = currStep.InPut != nextStep.InPut;
|
||||
currStep = nextStep;
|
||||
data = currStep;
|
||||
UpdateNextFrame(mCurrFrame);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = currStep;
|
||||
}
|
||||
return Changed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 往前推进帧的,指定帧下标
|
||||
/// </summary>
|
||||
public bool NextFramebyFrameIdx(int FrameID, out ReplayStep data)
|
||||
{
|
||||
bool res = TakeFrame(FrameID - byFrameIdx, out data);
|
||||
byFrameIdx = FrameID;
|
||||
return res;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
mStream.Dispose();
|
||||
mBinaryReader.Dispose();
|
||||
//TODO
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66e0e18d1f5981745a3078e8460cb0e6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,158 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace AxiReplay
|
||||
{
|
||||
public class ReplayWriter : IReplayWriter
|
||||
{
|
||||
public ReplayData.ReplayFormat mFormat { get; private set; }
|
||||
public Encoding TexEncoding { get; private set; }
|
||||
ReplayHandler handler;
|
||||
string mTitle;
|
||||
string mNote;
|
||||
int mAllFrame;
|
||||
int mAllTime;
|
||||
long mData;
|
||||
int mSingleInputLenght;
|
||||
int mSingleDataLenght;
|
||||
MemoryStream mStream;
|
||||
BinaryWriter mBinaryWriter;
|
||||
|
||||
int mCurrFrame;
|
||||
UInt64 mCurrInput;
|
||||
ReplayStep wirteStep;
|
||||
|
||||
List<string> dbgList = new List<string>();
|
||||
|
||||
public ReplayWriter(string Title, string Note, ReplayData.ReplayFormat format, Encoding encoding)
|
||||
{
|
||||
mTitle = Title;
|
||||
mNote = Note;
|
||||
TexEncoding = encoding;
|
||||
mFormat = format;
|
||||
switch (mFormat)
|
||||
{
|
||||
case ReplayData.ReplayFormat.FM32IP64: mSingleInputLenght = sizeof(UInt64); break;
|
||||
case ReplayData.ReplayFormat.FM32IP32: mSingleInputLenght = sizeof(UInt32); break;
|
||||
case ReplayData.ReplayFormat.FM32IP16: mSingleInputLenght = sizeof(UInt16); break;
|
||||
case ReplayData.ReplayFormat.FM32IPBYTE: mSingleInputLenght = sizeof(byte); break;
|
||||
}
|
||||
mSingleDataLenght = (sizeof(UInt32)) + mSingleInputLenght;
|
||||
|
||||
mStream = new MemoryStream();
|
||||
mBinaryWriter = new BinaryWriter(mStream);
|
||||
|
||||
mCurrFrame = -1;
|
||||
mCurrInput = int.MaxValue;
|
||||
wirteStep = new ReplayStep();
|
||||
|
||||
dbgList.Clear();
|
||||
|
||||
}
|
||||
|
||||
int byFrameIdx = 0;
|
||||
/// <summary>
|
||||
/// 往前推进帧的,指定帧下标
|
||||
/// </summary>
|
||||
/// <param name="frameInput"></param>
|
||||
public void NextFramebyFrameIdx(int FrameID, UInt64 frameInput)
|
||||
{
|
||||
TakeFrame(FrameID - byFrameIdx, frameInput);
|
||||
byFrameIdx = FrameID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 往前推进1帧的Input
|
||||
/// </summary>
|
||||
/// <param name="frameInput"></param>
|
||||
public void NextFrame(UInt64 frameInput)
|
||||
{
|
||||
TakeFrame(1, frameInput);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 往前推进指定帧数量的Input
|
||||
/// </summary>
|
||||
/// <param name="frameInput"></param>
|
||||
public void TakeFrame(int addFrame, UInt64 frameInput)
|
||||
{
|
||||
if (addFrame < 0)
|
||||
{
|
||||
|
||||
}
|
||||
mCurrFrame += addFrame;
|
||||
if (mCurrInput == frameInput)
|
||||
return;
|
||||
mCurrInput = frameInput;
|
||||
|
||||
wirteStep.FrameStartID = mCurrFrame;
|
||||
wirteStep.InPut = mCurrInput;
|
||||
dbgList.Add($"{mCurrFrame} | {mCurrInput}");
|
||||
|
||||
switch (mFormat)
|
||||
{
|
||||
case ReplayData.ReplayFormat.FM32IP64:
|
||||
mBinaryWriter.Write(wirteStep.FrameStartID);
|
||||
mBinaryWriter.Write(wirteStep.InPut);
|
||||
break;
|
||||
case ReplayData.ReplayFormat.FM32IP32:
|
||||
mBinaryWriter.Write(BitConverter.GetBytes(wirteStep.All64Data), 0, 4 + 4);
|
||||
break;
|
||||
case ReplayData.ReplayFormat.FM32IP16:
|
||||
mBinaryWriter.Write(BitConverter.GetBytes(wirteStep.All64Data), 0, 4 + 2);
|
||||
break;
|
||||
case ReplayData.ReplayFormat.FM32IPBYTE:
|
||||
mBinaryWriter.Write(BitConverter.GetBytes(wirteStep.All64Data), 0, 4 + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveData(string path, bool bWithDump = false, string dumppath = null)
|
||||
{
|
||||
byte[] titleData; int titleLenghtWithEnd;
|
||||
ReplayData.GetStringByteData(mTitle, out titleData, out titleLenghtWithEnd, TexEncoding);
|
||||
byte[] noteData; int noteLenghtWithEnd;
|
||||
ReplayData.GetStringByteData(mNote, out noteData, out noteLenghtWithEnd, TexEncoding);
|
||||
|
||||
ReplayHandler handler = new ReplayHandler();
|
||||
handler.Format = (int)this.mFormat;
|
||||
handler.DataOffset = ReplayData.HandlerLenght;
|
||||
handler.CreateTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
handler.AllFrame = wirteStep.FrameStartID;
|
||||
handler.SingleLenght = mSingleDataLenght;
|
||||
|
||||
using (FileStream fs = new FileStream(path, FileMode.Create))
|
||||
{
|
||||
using (BinaryWriter bw = new BinaryWriter(fs))
|
||||
{
|
||||
//写入Handler
|
||||
bw.Write(ReplayData.GetHandlerData(handler));
|
||||
//写入Data
|
||||
bw.Write(mStream.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
if (bWithDump)
|
||||
{
|
||||
List<string> temp = new List<string>();
|
||||
temp.Add($"Format => {handler.Format}");
|
||||
temp.Add($"DataOffset => {handler.DataOffset}");
|
||||
temp.Add($"CreateTime => {handler.CreateTime}");
|
||||
temp.Add($"AllFrame => {handler.AllFrame}");
|
||||
temp.Add($"SingleLenght => {handler.SingleLenght}");
|
||||
dbgList.InsertRange(0, temp);
|
||||
File.WriteAllLines(dumppath, dbgList);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
mStream.Dispose();
|
||||
mBinaryWriter.Dispose();
|
||||
//TODO
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc53a3d9a3e1749438b6ad1cef7b39bc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 38ad9fa7ab0e4e04788cecad771d86a1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,21 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a50bd9a009c8dfc4ebd88cc8101225a7
|
||||
labels:
|
||||
- Tween
|
||||
- Tweening
|
||||
- Animation
|
||||
- HOTween
|
||||
- Paths
|
||||
- iTween
|
||||
- DFTween
|
||||
- LeanTween
|
||||
- Ease
|
||||
- Easing
|
||||
- Shake
|
||||
- Punch
|
||||
- 2DToolkit
|
||||
- TextMeshPro
|
||||
- Text
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 34192c5e0d14aee43a0e86cc4823268a
|
||||
TextScriptImporter:
|
||||
userData:
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f007001a22b3d24dae350342c4d19c8
|
||||
DefaultImporter:
|
||||
userData:
|
@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a811bde74b26b53498b4f6d872b09b6d
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b27f58ae5d5c33a4bb2d1f4f34bd036d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
@ -0,0 +1,144 @@
|
||||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>DOTweenEditor</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="T:DG.DOTweenEditor.EditorCompatibilityUtils">
|
||||
<summary>
|
||||
Contains compatibility methods taken from DemiEditor (for when DOTween is without it)
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorCompatibilityUtils.FindObjectOfType``1(System.Boolean)">
|
||||
<summary>
|
||||
Warning: some versions of this method don't have the includeInactive parameter so it won't be taken into account
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorCompatibilityUtils.FindObjectOfType(System.Type,System.Boolean)">
|
||||
<summary>
|
||||
Warning: some versions of this method don't have the includeInactive parameter so it won't be taken into account
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorCompatibilityUtils.FindObjectsOfType``1(System.Boolean)">
|
||||
<summary>
|
||||
Warning: some versions of this method don't have the includeInactive parameter so it won't be taken into account
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorCompatibilityUtils.FindObjectsOfType(System.Type,System.Boolean)">
|
||||
<summary>
|
||||
Warning: some versions of this method don't have the includeInactive parameter so it won't be taken into account
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.DOTweenEditorPreview.Start(System.Action)">
|
||||
<summary>
|
||||
Starts the update loop of tween in the editor. Has no effect during playMode.
|
||||
</summary>
|
||||
<param name="onPreviewUpdated">Eventual callback to call after every update</param>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.DOTweenEditorPreview.Stop(System.Boolean,System.Boolean)">
|
||||
<summary>
|
||||
Stops the update loop and clears the onPreviewUpdated callback.
|
||||
</summary>
|
||||
<param name="resetTweenTargets">If TRUE also resets the tweened objects to their original state.
|
||||
Note that this works by calling Rewind on all tweens, so it will work correctly
|
||||
only if you have a single tween type per object and it wasn't killed</param>
|
||||
<param name="clearTweens">If TRUE also kills any cached tween</param>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.DOTweenEditorPreview.PrepareTweenForPreview(DG.Tweening.Tween,System.Boolean,System.Boolean,System.Boolean)">
|
||||
<summary>
|
||||
Readies the tween for editor preview by setting its UpdateType to Manual plus eventual extra settings.
|
||||
</summary>
|
||||
<param name="t">The tween to ready</param>
|
||||
<param name="clearCallbacks">If TRUE (recommended) removes all callbacks (OnComplete/Rewind/etc)</param>
|
||||
<param name="preventAutoKill">If TRUE prevents the tween from being auto-killed at completion</param>
|
||||
<param name="andPlay">If TRUE starts playing the tween immediately</param>
|
||||
</member>
|
||||
<member name="F:DG.DOTweenEditor.EditorVersion.Version">
|
||||
<summary>Full major version + first minor version (ex: 2018.1f)</summary>
|
||||
</member>
|
||||
<member name="F:DG.DOTweenEditor.EditorVersion.MajorVersion">
|
||||
<summary>Major version</summary>
|
||||
</member>
|
||||
<member name="F:DG.DOTweenEditor.EditorVersion.MinorVersion">
|
||||
<summary>First minor version (ex: in 2018.1 it would be 1)</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.SetEditorTexture(UnityEngine.Texture2D,UnityEngine.FilterMode,System.Int32)">
|
||||
<summary>
|
||||
Checks that the given editor texture use the correct import settings,
|
||||
and applies them if they're incorrect.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.DOTweenSetupRequired">
|
||||
<summary>
|
||||
Returns TRUE if setup is required
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.AssetExists(System.String)">
|
||||
<summary>
|
||||
Returns TRUE if the file/directory at the given path exists.
|
||||
</summary>
|
||||
<param name="adbPath">Path, relative to Unity's project folder</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.ADBPathToFullPath(System.String)">
|
||||
<summary>
|
||||
Converts the given project-relative path to a full path,
|
||||
with backward (\) slashes).
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.FullPathToADBPath(System.String)">
|
||||
<summary>
|
||||
Converts the given full path to a path usable with AssetDatabase methods
|
||||
(relative to Unity's project folder, and with the correct Unity forward (/) slashes).
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.ConnectToSourceAsset``1(System.String,System.Boolean)">
|
||||
<summary>
|
||||
Connects to a <see cref="T:UnityEngine.ScriptableObject"/> asset.
|
||||
If the asset already exists at the given path, loads it and returns it.
|
||||
Otherwise, either returns NULL or automatically creates it before loading and returning it
|
||||
(depending on the given parameters).
|
||||
</summary>
|
||||
<typeparam name="T">Asset type</typeparam>
|
||||
<param name="adbFilePath">File path (relative to Unity's project folder)</param>
|
||||
<param name="createIfMissing">If TRUE and the requested asset doesn't exist, forces its creation</param>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.GetAssemblyFilePath(System.Reflection.Assembly)">
|
||||
<summary>
|
||||
Full path for the given loaded assembly, assembly file included
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.AddGlobalDefine(System.String)">
|
||||
<summary>
|
||||
Adds the given global define if it's not already present
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.RemoveGlobalDefine(System.String)">
|
||||
<summary>
|
||||
Removes the given global define if it's present
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.HasGlobalDefine(System.String,System.Nullable{UnityEditor.BuildTargetGroup})">
|
||||
<summary>
|
||||
Returns TRUE if the given global define is present in all the <see cref="T:UnityEditor.BuildTargetGroup"/>
|
||||
or only in the given <see cref="T:UnityEditor.BuildTargetGroup"/>, depending on passed parameters.<para/>
|
||||
</summary>
|
||||
<param name="id"></param>
|
||||
<param name="buildTargetGroup"><see cref="T:UnityEditor.BuildTargetGroup"/>to use. Leave NULL to check in all of them.</param>
|
||||
</member>
|
||||
<member name="T:DG.DOTweenEditor.DOTweenDefines">
|
||||
<summary>
|
||||
Not used as menu item anymore, but as a utility function
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:DG.DOTweenEditor.UnityEditorVersion.Version">
|
||||
<summary>Full major version + first minor version (ex: 2018.1f)</summary>
|
||||
</member>
|
||||
<member name="F:DG.DOTweenEditor.UnityEditorVersion.MajorVersion">
|
||||
<summary>Major version</summary>
|
||||
</member>
|
||||
<member name="F:DG.DOTweenEditor.UnityEditorVersion.MinorVersion">
|
||||
<summary>First minor version (ex: in 2018.1 it would be 1)</summary>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e2c6224d345d9249acfa6e8ef40bb2d
|
||||
TextScriptImporter:
|
||||
userData:
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f46310a8b0a8f04a92993c37c713243
|
||||
DefaultImporter:
|
||||
userData:
|
@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45d5034162d6cf04dbe46da84fc7d074
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0034ebae0c2a9344e897db1160d71b6d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
Binary file not shown.
After ![]() (image error) Size: 1.5 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user