Update example unity project's Editor scripts
This commit is contained in:
parent
7b008c39c9
commit
f973f620e9
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
@ -14,17 +13,31 @@ public class Build : MonoBehaviour
|
||||||
|
|
||||||
static readonly string apkPath = Path.Combine(ProjectPath, "Builds/" + Application.productName + ".apk");
|
static readonly string apkPath = Path.Combine(ProjectPath, "Builds/" + Application.productName + ".apk");
|
||||||
|
|
||||||
[MenuItem("Flutter/Export Android %&a", false, 1)]
|
static readonly string androidExportPath = Path.GetFullPath(Path.Combine(ProjectPath, "../../android/UnityExport"));
|
||||||
public static void DoBuildAndroid()
|
static readonly string iosExportPath = Path.GetFullPath(Path.Combine(ProjectPath, "../../ios/UnityExport"));
|
||||||
{
|
|
||||||
string buildPath = Path.Combine(apkPath, Application.productName);
|
|
||||||
string exportPath = Path.GetFullPath(Path.Combine(ProjectPath, "../../android/UnityExport"));
|
|
||||||
|
|
||||||
|
[MenuItem("Flutter/Export Android (Unity 2019.3.*) %&n", false, 1)]
|
||||||
|
public static void DoBuildAndroidLibrary()
|
||||||
|
{
|
||||||
|
DoBuildAndroid(Path.Combine(apkPath, "unityLibrary"));
|
||||||
|
|
||||||
|
// Copy over resources from the launcher module that are used by the library
|
||||||
|
Copy(Path.Combine(apkPath + "/launcher/src/main/res"), Path.Combine(androidExportPath, "src/main/res"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Flutter/Export Android %&a", false, 2)]
|
||||||
|
public static void DoBuildAndroidLegacy()
|
||||||
|
{
|
||||||
|
DoBuildAndroid(Path.Combine(apkPath, Application.productName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DoBuildAndroid(String buildPath)
|
||||||
|
{
|
||||||
if (Directory.Exists(apkPath))
|
if (Directory.Exists(apkPath))
|
||||||
Directory.Delete(apkPath, true);
|
Directory.Delete(apkPath, true);
|
||||||
|
|
||||||
if (Directory.Exists(exportPath))
|
if (Directory.Exists(androidExportPath))
|
||||||
Directory.Delete(exportPath, true);
|
Directory.Delete(androidExportPath, true);
|
||||||
|
|
||||||
EditorUserBuildSettings.androidBuildSystem = AndroidBuildSystem.Gradle;
|
EditorUserBuildSettings.androidBuildSystem = AndroidBuildSystem.Gradle;
|
||||||
|
|
||||||
|
@ -39,17 +52,18 @@ public class Build : MonoBehaviour
|
||||||
if (report.summary.result != BuildResult.Succeeded)
|
if (report.summary.result != BuildResult.Succeeded)
|
||||||
throw new Exception("Build failed");
|
throw new Exception("Build failed");
|
||||||
|
|
||||||
Copy(buildPath, exportPath);
|
Copy(buildPath, androidExportPath);
|
||||||
|
|
||||||
// Modify build.gradle
|
// Modify build.gradle
|
||||||
var build_file = Path.Combine(exportPath, "build.gradle");
|
var build_file = Path.Combine(androidExportPath, "build.gradle");
|
||||||
var build_text = File.ReadAllText(build_file);
|
var build_text = File.ReadAllText(build_file);
|
||||||
build_text = build_text.Replace("com.android.application", "com.android.library");
|
build_text = build_text.Replace("com.android.application", "com.android.library");
|
||||||
|
build_text = build_text.Replace("implementation fileTree(dir: 'libs', include: ['*.jar'])", "implementation project(':unity-classes')");
|
||||||
build_text = Regex.Replace(build_text, @"\n.*applicationId '.+'.*\n", "\n");
|
build_text = Regex.Replace(build_text, @"\n.*applicationId '.+'.*\n", "\n");
|
||||||
File.WriteAllText(build_file, build_text);
|
File.WriteAllText(build_file, build_text);
|
||||||
|
|
||||||
// Modify AndroidManifest.xml
|
// Modify AndroidManifest.xml
|
||||||
var manifest_file = Path.Combine(exportPath, "src/main/AndroidManifest.xml");
|
var manifest_file = Path.Combine(androidExportPath, "src/main/AndroidManifest.xml");
|
||||||
var manifest_text = File.ReadAllText(manifest_file);
|
var manifest_text = File.ReadAllText(manifest_file);
|
||||||
manifest_text = Regex.Replace(manifest_text, @"<application .*>", "<application>");
|
manifest_text = Regex.Replace(manifest_text, @"<application .*>", "<application>");
|
||||||
Regex regex = new Regex(@"<activity.*>(\s|\S)+?</activity>", RegexOptions.Multiline);
|
Regex regex = new Regex(@"<activity.*>(\s|\S)+?</activity>", RegexOptions.Multiline);
|
||||||
|
@ -57,20 +71,18 @@ public class Build : MonoBehaviour
|
||||||
File.WriteAllText(manifest_file, manifest_text);
|
File.WriteAllText(manifest_file, manifest_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MenuItem("Flutter/Export IOS %&i", false, 2)]
|
[MenuItem("Flutter/Export IOS (Unity 2019.3.*) %&i", false, 3)]
|
||||||
public static void DoBuildIOS()
|
public static void DoBuildIOS()
|
||||||
{
|
{
|
||||||
string exportPath = Path.GetFullPath(Path.Combine(ProjectPath, "../../ios/UnityExport"));
|
if (Directory.Exists(iosExportPath))
|
||||||
|
Directory.Delete(iosExportPath, true);
|
||||||
if (Directory.Exists(exportPath))
|
|
||||||
Directory.Delete(exportPath, true);
|
|
||||||
|
|
||||||
EditorUserBuildSettings.iOSBuildConfigType = iOSBuildType.Release;
|
EditorUserBuildSettings.iOSBuildConfigType = iOSBuildType.Release;
|
||||||
|
|
||||||
var options = BuildOptions.AcceptExternalModificationsToPlayer;
|
var options = BuildOptions.AcceptExternalModificationsToPlayer;
|
||||||
var report = BuildPipeline.BuildPlayer(
|
var report = BuildPipeline.BuildPlayer(
|
||||||
GetEnabledScenes(),
|
GetEnabledScenes(),
|
||||||
exportPath,
|
iosExportPath,
|
||||||
BuildTarget.iOS,
|
BuildTarget.iOS,
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,68 +21,25 @@ SOFTWARE.
|
||||||
#if UNITY_IOS
|
#if UNITY_IOS
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEditor.Callbacks;
|
using UnityEditor.Callbacks;
|
||||||
using UnityEditor.iOS.Xcode;
|
using UnityEditor.iOS.Xcode;
|
||||||
using Application = UnityEngine.Application;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adding this post build script to Unity project enables Unity iOS build output to be embedded
|
/// Adding this post build script to Unity project enables the flutter-unity-widget to access it
|
||||||
/// into existing Xcode Swift project.
|
|
||||||
///
|
|
||||||
/// However, since this script touches Unity iOS build output, you will not be able to use Unity
|
|
||||||
/// iOS build directly in Xcode. As a result, it is recommended to put Unity iOS build output into
|
|
||||||
/// a temporary directory that you generally do not touch, such as '/tmp'.
|
|
||||||
///
|
|
||||||
/// In order for this to work, necessary changes to the target Xcode Swift project are needed.
|
|
||||||
/// Especially the 'AppDelegate.swift' should be modified to properly initialize Unity.
|
|
||||||
/// See https://github.com/jiulongw/swift-unity for details.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class XcodePostBuild
|
public static class XcodePostBuild
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Path to the root directory of Xcode project.
|
|
||||||
/// This should point to the directory of '${XcodeProjectName}.xcodeproj'.
|
|
||||||
/// It is recommended to use relative path here.
|
|
||||||
/// Current directory is the root directory of this Unity project, i.e. the directory of 'Assets' folder.
|
|
||||||
/// Sample value: "../xcode"
|
|
||||||
/// </summary>
|
|
||||||
private const string XcodeProjectRoot = "../../ios";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Name of the Xcode project.
|
|
||||||
/// This script looks for '${XcodeProjectName} + ".xcodeproj"' under '${XcodeProjectRoot}'.
|
|
||||||
/// Sample value: "DemoApp"
|
|
||||||
/// </summary>
|
|
||||||
private static string XcodeProjectName = Application.productName;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Directories, relative to the root directory of the Xcode project, to put generated Unity iOS build output.
|
|
||||||
/// </summary>
|
|
||||||
private static string ClassesProjectPath = "UnityExport/Classes";
|
|
||||||
private static string LibrariesProjectPath = "UnityExport/Libraries";
|
|
||||||
private static string DataProjectPath = "UnityExport/Data";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Path, relative to the root directory of the Xcode project, to put information about generated Unity output.
|
|
||||||
/// </summary>
|
|
||||||
private static string ExportsConfigProjectPath = "UnityExport/Exports.xcconfig";
|
|
||||||
|
|
||||||
private static string PbxFilePath = XcodeProjectName + ".xcodeproj/project.pbxproj";
|
|
||||||
|
|
||||||
private const string BackupExtension = ".bak";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The identifier added to touched file to avoid double edits when building to existing directory without
|
/// The identifier added to touched file to avoid double edits when building to existing directory without
|
||||||
/// replace existing content.
|
/// replace existing content.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const string TouchedMarker = "https://github.com/jiulongw/swift-unity#v1";
|
private const string TouchedMarker = "https://github.com/snowballdigital/flutter-unity-view-widget";
|
||||||
|
|
||||||
[PostProcessBuild]
|
[PostProcessBuild]
|
||||||
public static void OnPostBuild(BuildTarget target, string pathToBuiltProject)
|
public static void OnPostBuild(BuildTarget target, string pathToBuiltProject)
|
||||||
|
@ -94,217 +51,49 @@ public static class XcodePostBuild
|
||||||
|
|
||||||
PatchUnityNativeCode(pathToBuiltProject);
|
PatchUnityNativeCode(pathToBuiltProject);
|
||||||
|
|
||||||
UpdateUnityIOSExports(pathToBuiltProject);
|
|
||||||
|
|
||||||
UpdateUnityProjectFiles(pathToBuiltProject);
|
UpdateUnityProjectFiles(pathToBuiltProject);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes current Unity version and output path to 'Exports.xcconfig' file.
|
/// We need to add the Data folder to the UnityFramework framework
|
||||||
/// </summary>
|
|
||||||
private static void UpdateUnityIOSExports(string pathToBuiltProject)
|
|
||||||
{
|
|
||||||
var config = new StringBuilder();
|
|
||||||
config.AppendFormat("UNITY_RUNTIME_VERSION = {0};", Application.unityVersion);
|
|
||||||
config.AppendLine();
|
|
||||||
config.AppendFormat("UNITY_IOS_EXPORT_PATH = {0};", pathToBuiltProject);
|
|
||||||
config.AppendLine();
|
|
||||||
|
|
||||||
var configPath = Path.Combine(XcodeProjectRoot, ExportsConfigProjectPath);
|
|
||||||
var configDir = Path.GetDirectoryName(configPath);
|
|
||||||
if (!Directory.Exists(configDir))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(configDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
File.WriteAllText(configPath, config.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Enumerates Unity output files and add necessary files into Xcode project file.
|
|
||||||
/// It only add a reference entry into project.pbx file, without actually copy it.
|
|
||||||
/// Xcode pre-build script will copy files into correct location.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static void UpdateUnityProjectFiles(string pathToBuiltProject)
|
private static void UpdateUnityProjectFiles(string pathToBuiltProject)
|
||||||
{
|
{
|
||||||
var pbx = new PBXProject();
|
var pbx = new PBXProject();
|
||||||
var pbxPath = Path.Combine(XcodeProjectRoot, PbxFilePath);
|
var pbxPath = Path.Combine(pathToBuiltProject, "Unity-iPhone.xcodeproj/project.pbxproj");
|
||||||
pbx.ReadFromFile(pbxPath);
|
pbx.ReadFromFile(pbxPath);
|
||||||
|
|
||||||
// Add UnityExport/Classes
|
|
||||||
ProcessUnityDirectory(
|
|
||||||
pbx,
|
|
||||||
Path.Combine(pathToBuiltProject, "Classes"),
|
|
||||||
Path.Combine(XcodeProjectRoot, ClassesProjectPath),
|
|
||||||
ClassesProjectPath);
|
|
||||||
|
|
||||||
// Add UnityExport/Libraries
|
|
||||||
ProcessUnityDirectory(
|
|
||||||
pbx,
|
|
||||||
Path.Combine(pathToBuiltProject, "Libraries"),
|
|
||||||
Path.Combine(XcodeProjectRoot, LibrariesProjectPath),
|
|
||||||
LibrariesProjectPath);
|
|
||||||
|
|
||||||
// Add UnityExport/Data
|
// Add UnityExport/Data
|
||||||
var targetGuid = pbx.TargetGuidByName(XcodeProjectName);
|
var targetGuid = pbx.TargetGuidByName("UnityFramework");
|
||||||
var fileGuid = pbx.AddFolderReference(Path.Combine(pathToBuiltProject, "Data"), DataProjectPath);
|
var fileGuid = pbx.AddFolderReference(Path.Combine(pathToBuiltProject, "Data"), "Data");
|
||||||
pbx.AddFileToBuild(targetGuid, fileGuid);
|
pbx.AddFileToBuild(targetGuid, fileGuid);
|
||||||
|
|
||||||
pbx.WriteToFile(pbxPath);
|
pbx.WriteToFile(pbxPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Update pbx project file by adding src files and removing extra files that
|
|
||||||
/// exists in dest but not in src any more.
|
|
||||||
///
|
|
||||||
/// This method only updates the pbx project file. It does not copy or delete
|
|
||||||
/// files in Swift Xcode project. The Swift Xcode project will do copy and delete
|
|
||||||
/// during build, and it should copy files if contents are different, regardless
|
|
||||||
/// of the file time.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pbx">The pbx project.</param>
|
|
||||||
/// <param name="src">The directory where Unity project is built.</param>
|
|
||||||
/// <param name="dest">The directory of the Swift Xcode project where the
|
|
||||||
/// Unity project is embedded into.</param>
|
|
||||||
/// <param name="projectPathPrefix">The prefix of project path in Swift Xcode
|
|
||||||
/// project for Unity code files. E.g. "DempApp/Unity/Classes" for all files
|
|
||||||
/// under Classes folder from Unity iOS build output.</param>
|
|
||||||
private static void ProcessUnityDirectory(PBXProject pbx, string src, string dest, string projectPathPrefix)
|
|
||||||
{
|
|
||||||
var targetGuid = pbx.TargetGuidByName(XcodeProjectName);
|
|
||||||
if (string.IsNullOrEmpty(targetGuid))
|
|
||||||
{
|
|
||||||
throw new Exception(string.Format("TargetGuid could not be found for '{0}'", XcodeProjectName));
|
|
||||||
}
|
|
||||||
|
|
||||||
// newFiles: array of file names in build output that do not exist in project.pbx manifest.
|
|
||||||
// extraFiles: array of file names in project.pbx manifest that do not exist in build output.
|
|
||||||
// Build output files that already exist in project.pbx manifest will be skipped to minimize
|
|
||||||
// changes to project.pbx file.
|
|
||||||
string[] newFiles, extraFiles;
|
|
||||||
CompareDirectories(src, dest, out newFiles, out extraFiles);
|
|
||||||
|
|
||||||
foreach (var f in newFiles)
|
|
||||||
{
|
|
||||||
if (ShouldExcludeFile(f))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var projPath = Path.Combine(projectPathPrefix, f);
|
|
||||||
if (!pbx.ContainsFileByProjectPath(projPath))
|
|
||||||
{
|
|
||||||
var guid = pbx.AddFile(projPath, projPath);
|
|
||||||
pbx.AddFileToBuild(targetGuid, guid);
|
|
||||||
|
|
||||||
Debug.LogFormat("Added file to pbx: '{0}'", projPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var f in extraFiles)
|
|
||||||
{
|
|
||||||
var projPath = Path.Combine(projectPathPrefix, f);
|
|
||||||
if (pbx.ContainsFileByProjectPath(projPath))
|
|
||||||
{
|
|
||||||
var guid = pbx.FindFileGuidByProjectPath(projPath);
|
|
||||||
pbx.RemoveFile(guid);
|
|
||||||
|
|
||||||
Debug.LogFormat("Removed file from pbx: '{0}'", projPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Compares the directories. Returns files that exists in src and
|
|
||||||
/// extra files that exists in dest but not in src any more.
|
|
||||||
/// </summary>
|
|
||||||
private static void CompareDirectories(string src, string dest, out string[] srcFiles, out string[] extraFiles)
|
|
||||||
{
|
|
||||||
srcFiles = GetFilesRelativePath(src);
|
|
||||||
|
|
||||||
var destFiles = GetFilesRelativePath(dest);
|
|
||||||
var extraFilesSet = new HashSet<string>(destFiles);
|
|
||||||
|
|
||||||
extraFilesSet.ExceptWith(srcFiles);
|
|
||||||
extraFiles = extraFilesSet.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string[] GetFilesRelativePath(string directory)
|
|
||||||
{
|
|
||||||
var results = new List<string>();
|
|
||||||
|
|
||||||
if (Directory.Exists(directory))
|
|
||||||
{
|
|
||||||
foreach (var path in Directory.GetFiles(directory, "*", SearchOption.AllDirectories))
|
|
||||||
{
|
|
||||||
var relative = path.Substring(directory.Length).TrimStart('/');
|
|
||||||
results.Add(relative);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return results.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool ShouldExcludeFile(string fileName)
|
|
||||||
{
|
|
||||||
if (fileName.EndsWith(".bak", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Make necessary changes to Unity build output that enables it to be embedded into existing Xcode project.
|
/// Make necessary changes to Unity build output that enables it to be embedded into existing Xcode project.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static void PatchUnityNativeCode(string pathToBuiltProject)
|
private static void PatchUnityNativeCode(string pathToBuiltProject)
|
||||||
{
|
{
|
||||||
EditMainMM(Path.Combine(pathToBuiltProject, "Classes/main.mm"));
|
EditUnityFrameworkH(Path.Combine(pathToBuiltProject, "UnityFramework/UnityFramework.h"));
|
||||||
EditUnityAppControllerH(Path.Combine(pathToBuiltProject, "Classes/UnityAppController.h"));
|
EditUnityAppControllerH(Path.Combine(pathToBuiltProject, "Classes/UnityAppController.h"));
|
||||||
EditUnityAppControllerMM(Path.Combine(pathToBuiltProject, "Classes/UnityAppController.mm"));
|
EditUnityAppControllerMM(Path.Combine(pathToBuiltProject, "Classes/UnityAppController.mm"));
|
||||||
|
EditUnityViewMM(Path.Combine(pathToBuiltProject, "Classes/UI/UnityView.mm"));
|
||||||
if (Application.unityVersion == "2017.1.1f1")
|
|
||||||
{
|
|
||||||
EditMetalHelperMM(Path.Combine(pathToBuiltProject, "Classes/Unity/MetalHelper.mm"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Parse unity version number and do range comparison.
|
|
||||||
if (Application.unityVersion.StartsWith("2017.3.0f") || Application.unityVersion.StartsWith("2017.3.1f"))
|
|
||||||
{
|
|
||||||
EditSplashScreenMM(Path.Combine(pathToBuiltProject, "Classes/UI/SplashScreen.mm"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Edit 'main.mm': removes 'main' entry that would conflict with the Xcode project it embeds into.
|
|
||||||
/// </summary>
|
|
||||||
private static void EditMainMM(string path)
|
|
||||||
{
|
|
||||||
EditCodeFile(path, line =>
|
|
||||||
{
|
|
||||||
if (line.TrimStart().StartsWith("int main", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
return line.Replace("int main", "int old_main");
|
|
||||||
}
|
|
||||||
|
|
||||||
return line;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Edit 'UnityAppController.h': returns 'UnityAppController' from 'AppDelegate' class.
|
/// Edit 'UnityFramework.h': add 'frameworkWarmup'
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static void EditUnityAppControllerH(string path)
|
private static void EditUnityFrameworkH(string path)
|
||||||
{
|
{
|
||||||
var inScope = false;
|
var inScope = false;
|
||||||
var markerDetected = false;
|
|
||||||
var markerAdded = false;
|
|
||||||
|
|
||||||
// Add static GetAppController
|
// Add frameworkWarmup method
|
||||||
EditCodeFile(path, line =>
|
EditCodeFile(path, line =>
|
||||||
{
|
{
|
||||||
inScope |= line.Contains("- (void)startUnity:");
|
inScope |= line.Contains("- (void)runUIApplicationMainWithArgc:");
|
||||||
|
|
||||||
if (inScope)
|
if (inScope)
|
||||||
{
|
{
|
||||||
|
@ -316,7 +105,7 @@ public static class XcodePostBuild
|
||||||
{
|
{
|
||||||
"",
|
"",
|
||||||
"// Added by " + TouchedMarker,
|
"// Added by " + TouchedMarker,
|
||||||
"+ (UnityAppController*)GetAppController;",
|
"- (void)frameworkWarmup:(int)argc argv:(char*[])argv;",
|
||||||
""
|
""
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -324,6 +113,130 @@ public static class XcodePostBuild
|
||||||
|
|
||||||
return new string[] { line };
|
return new string[] { line };
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Edit 'UnityAppController.h': returns 'UnityAppController' from 'AppDelegate' class.
|
||||||
|
/// </summary>
|
||||||
|
private static void EditUnityAppControllerH(string path)
|
||||||
|
{
|
||||||
|
var inScope = false;
|
||||||
|
var markerDetected = false;
|
||||||
|
|
||||||
|
// Add static GetAppController
|
||||||
|
EditCodeFile(path, line =>
|
||||||
|
{
|
||||||
|
inScope |= line.Contains("- (void)startUnity:");
|
||||||
|
|
||||||
|
if (inScope)
|
||||||
|
{
|
||||||
|
if (line.Trim() == "")
|
||||||
|
{
|
||||||
|
inScope = false;
|
||||||
|
|
||||||
|
return new string[]
|
||||||
|
{
|
||||||
|
"",
|
||||||
|
"// Added by " + TouchedMarker,
|
||||||
|
"+ (UnityAppController*)GetAppController;",
|
||||||
|
""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new string[] { line };
|
||||||
|
});
|
||||||
|
|
||||||
|
inScope = false;
|
||||||
|
markerDetected = false;
|
||||||
|
|
||||||
|
// Modify inline GetAppController
|
||||||
|
EditCodeFile(path, line =>
|
||||||
|
{
|
||||||
|
inScope |= line.Contains("extern UnityAppController* GetAppController");
|
||||||
|
|
||||||
|
if (inScope && !markerDetected)
|
||||||
|
{
|
||||||
|
if (line.Trim() == "")
|
||||||
|
{
|
||||||
|
inScope = false;
|
||||||
|
markerDetected = true;
|
||||||
|
|
||||||
|
return new string[]
|
||||||
|
{
|
||||||
|
"// }",
|
||||||
|
"",
|
||||||
|
"static inline UnityAppController* GetAppController()",
|
||||||
|
"{",
|
||||||
|
" return [UnityAppController GetAppController];",
|
||||||
|
"}",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new string[] { "// " + line };
|
||||||
|
}
|
||||||
|
|
||||||
|
return new string[] { line };
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Edit 'UnityAppController.mm': triggers 'UnityReady' notification after Unity is actually started.
|
||||||
|
/// </summary>
|
||||||
|
private static void EditUnityAppControllerMM(string path)
|
||||||
|
{
|
||||||
|
var inScope = false;
|
||||||
|
var markerDetected = false;
|
||||||
|
|
||||||
|
EditCodeFile(path, line =>
|
||||||
|
{
|
||||||
|
if (line.Trim() == "@end")
|
||||||
|
{
|
||||||
|
return new string[]
|
||||||
|
{
|
||||||
|
"",
|
||||||
|
"// Added by " + TouchedMarker,
|
||||||
|
"static UnityAppController *unityAppController = nil;",
|
||||||
|
"",
|
||||||
|
@"+ (UnityAppController*)GetAppController",
|
||||||
|
"{",
|
||||||
|
" static dispatch_once_t onceToken;",
|
||||||
|
" dispatch_once(&onceToken, ^{",
|
||||||
|
" unityAppController = [[self alloc] init];",
|
||||||
|
" });",
|
||||||
|
" return unityAppController;",
|
||||||
|
"}",
|
||||||
|
"",
|
||||||
|
line,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
inScope |= line.Contains("- (void)startUnity:");
|
||||||
|
markerDetected |= inScope && line.Contains(TouchedMarker);
|
||||||
|
|
||||||
|
if (inScope && line.Trim() == "}")
|
||||||
|
{
|
||||||
|
inScope = false;
|
||||||
|
|
||||||
|
if (markerDetected)
|
||||||
|
{
|
||||||
|
return new string[] { line };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new string[]
|
||||||
|
{
|
||||||
|
" // Modified by " + TouchedMarker,
|
||||||
|
@" [[NSNotificationCenter defaultCenter] postNotificationName: @""UnityReady"" object:self];",
|
||||||
|
"}",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new string[] { line };
|
||||||
|
});
|
||||||
|
|
||||||
inScope = false;
|
inScope = false;
|
||||||
markerDetected = false;
|
markerDetected = false;
|
||||||
|
@ -331,7 +244,7 @@ public static class XcodePostBuild
|
||||||
// Modify inline GetAppController
|
// Modify inline GetAppController
|
||||||
EditCodeFile(path, line =>
|
EditCodeFile(path, line =>
|
||||||
{
|
{
|
||||||
inScope |= line.Contains("inline UnityAppController");
|
inScope |= line.Contains("UnityAppController* GetAppController()");
|
||||||
|
|
||||||
if (inScope && !markerDetected)
|
if (inScope && !markerDetected)
|
||||||
{
|
{
|
||||||
|
@ -342,22 +255,7 @@ public static class XcodePostBuild
|
||||||
|
|
||||||
return new string[]
|
return new string[]
|
||||||
{
|
{
|
||||||
"// }",
|
|
||||||
"",
|
"",
|
||||||
"static inline UnityAppController* GetAppController()",
|
|
||||||
"{",
|
|
||||||
" return [UnityAppController GetAppController];",
|
|
||||||
"}",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!markerAdded)
|
|
||||||
{
|
|
||||||
markerAdded = true;
|
|
||||||
return new string[]
|
|
||||||
{
|
|
||||||
"// Modified by " + TouchedMarker,
|
|
||||||
"// " + line,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,114 +267,34 @@ public static class XcodePostBuild
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Edit 'UnityAppController.mm': triggers 'UnityReady' notification after Unity is actually started.
|
/// Edit 'UnityView.mm': fix the width and height needed for the Metal renderer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static void EditUnityAppControllerMM(string path)
|
private static void EditUnityViewMM(string path)
|
||||||
{
|
{
|
||||||
EditCodeFile(path, line =>
|
|
||||||
{
|
|
||||||
if (line.Trim() == "@end")
|
|
||||||
{
|
|
||||||
return new string[]
|
|
||||||
{
|
|
||||||
"",
|
|
||||||
"// Added by " + TouchedMarker,
|
|
||||||
"static UnityAppController *unityAppController = nil;",
|
|
||||||
"",
|
|
||||||
@"+ (UnityAppController*)GetAppController",
|
|
||||||
"{",
|
|
||||||
" static dispatch_once_t onceToken;",
|
|
||||||
" dispatch_once(&onceToken, ^{",
|
|
||||||
" unityAppController = [[self alloc] init];",
|
|
||||||
" });",
|
|
||||||
" return unityAppController;",
|
|
||||||
"}",
|
|
||||||
"",
|
|
||||||
line,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return new string[] { line };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Edit 'MetalHelper.mm': fixes a bug (only in 2017.1.1f1) that causes crash.
|
|
||||||
/// </summary>
|
|
||||||
private static void EditMetalHelperMM(string path)
|
|
||||||
{
|
|
||||||
var markerDetected = false;
|
|
||||||
|
|
||||||
EditCodeFile(path, line =>
|
|
||||||
{
|
|
||||||
markerDetected |= line.Contains(TouchedMarker);
|
|
||||||
|
|
||||||
if (!markerDetected && line.Trim() == "surface->stencilRB = [surface->device newTextureWithDescriptor: stencilTexDesc];")
|
|
||||||
{
|
|
||||||
return new string[]
|
|
||||||
{
|
|
||||||
"",
|
|
||||||
" // Modified by " + TouchedMarker,
|
|
||||||
" // Default stencilTexDesc.usage has flag 1. In runtime it will cause assertion failure:",
|
|
||||||
" // validateRenderPassDescriptor:589: failed assertion `Texture at stencilAttachment has usage (0x01) which doesn't specify MTLTextureUsageRenderTarget (0x04)'",
|
|
||||||
" // Adding MTLTextureUsageRenderTarget seems to fix this issue.",
|
|
||||||
" stencilTexDesc.usage |= MTLTextureUsageRenderTarget;",
|
|
||||||
line,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return new string[] { line };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Edit 'SplashScreen.mm': Unity introduces its own 'LaunchScreen.storyboard' since 2017.3.0f3.
|
|
||||||
/// Disable it here and use Swift project's launch screen instead.
|
|
||||||
/// </summary>
|
|
||||||
private static void EditSplashScreenMM(string path)
|
|
||||||
{
|
|
||||||
var markerDetected = false;
|
|
||||||
var markerAdded = false;
|
|
||||||
var inScope = false;
|
var inScope = false;
|
||||||
var level = 0;
|
|
||||||
|
|
||||||
|
// Add frameworkWarmup method
|
||||||
EditCodeFile(path, line =>
|
EditCodeFile(path, line =>
|
||||||
{
|
{
|
||||||
inScope |= line.Trim() == "void ShowSplashScreen(UIWindow* window)";
|
inScope |= line.Contains("UnityGetRenderingResolution(&requestedW, &requestedH)");
|
||||||
markerDetected |= line.Contains(TouchedMarker);
|
|
||||||
|
|
||||||
if (inScope && !markerDetected)
|
if (inScope)
|
||||||
{
|
{
|
||||||
if (line.Trim() == "{")
|
if (line.Trim() == "")
|
||||||
{
|
|
||||||
level++;
|
|
||||||
}
|
|
||||||
else if (line.Trim() == "}")
|
|
||||||
{
|
|
||||||
level--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line.Trim() == "}" && level == 0)
|
|
||||||
{
|
{
|
||||||
inScope = false;
|
inScope = false;
|
||||||
}
|
|
||||||
|
|
||||||
if (level > 0 && line.Trim().StartsWith("bool hasStoryboard"))
|
|
||||||
{
|
|
||||||
return new string[]
|
return new string[]
|
||||||
{
|
{
|
||||||
" // " + line,
|
"",
|
||||||
" bool hasStoryboard = false;",
|
"// Added by " + TouchedMarker,
|
||||||
};
|
" if (requestedW == 0) {",
|
||||||
}
|
" requestedW = _surfaceSize.width;",
|
||||||
|
" }",
|
||||||
if (!markerAdded)
|
" if (requestedH == 0) {",
|
||||||
{
|
" requestedH = _surfaceSize.height;",
|
||||||
markerAdded = true;
|
" }",
|
||||||
return new string[]
|
""
|
||||||
{
|
|
||||||
"// Modified by " + TouchedMarker,
|
|
||||||
line,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -485,14 +303,6 @@ public static class XcodePostBuild
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EditCodeFile(string path, Func<string, string> lineHandler)
|
|
||||||
{
|
|
||||||
EditCodeFile(path, line =>
|
|
||||||
{
|
|
||||||
return new string[] { lineHandler(line) };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EditCodeFile(string path, Func<string, IEnumerable<string>> lineHandler)
|
private static void EditCodeFile(string path, Func<string, IEnumerable<string>> lineHandler)
|
||||||
{
|
{
|
||||||
var bakPath = path + ".bak";
|
var bakPath = path + ".bak";
|
||||||
|
|
|
@ -1,175 +1,175 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
public class MessageHandler
|
public class MessageHandler
|
||||||
{
|
{
|
||||||
public int id;
|
public int id;
|
||||||
public string seq;
|
public string seq;
|
||||||
|
|
||||||
public String name;
|
public String name;
|
||||||
private JToken data;
|
private JToken data;
|
||||||
|
|
||||||
public static MessageHandler Deserialize(string message)
|
public static MessageHandler Deserialize(string message)
|
||||||
{
|
{
|
||||||
JObject m = JObject.Parse(message);
|
JObject m = JObject.Parse(message);
|
||||||
MessageHandler handler = new MessageHandler(
|
MessageHandler handler = new MessageHandler(
|
||||||
m.GetValue("id").Value<int>(),
|
m.GetValue("id").Value<int>(),
|
||||||
m.GetValue("seq").Value<string>(),
|
m.GetValue("seq").Value<string>(),
|
||||||
m.GetValue("name").Value<string>(),
|
m.GetValue("name").Value<string>(),
|
||||||
m.GetValue("data")
|
m.GetValue("data")
|
||||||
);
|
);
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T getData<T>()
|
public T getData<T>()
|
||||||
{
|
{
|
||||||
return data.Value<T>();
|
return data.Value<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public MessageHandler(int id, string seq, string name, JToken data)
|
public MessageHandler(int id, string seq, string name, JToken data)
|
||||||
{
|
{
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.seq = seq;
|
this.seq = seq;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void send(object data)
|
public void send(object data)
|
||||||
{
|
{
|
||||||
JObject o = JObject.FromObject(new
|
JObject o = JObject.FromObject(new
|
||||||
{
|
{
|
||||||
id = id,
|
id = id,
|
||||||
seq = "end",
|
seq = "end",
|
||||||
name = name,
|
name = name,
|
||||||
data = data
|
data = data
|
||||||
});
|
});
|
||||||
UnityMessageManager.Instance.SendMessageToFlutter(UnityMessageManager.MessagePrefix + o.ToString());
|
UnityMessageManager.Instance.SendMessageToFlutter(UnityMessageManager.MessagePrefix + o.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UnityMessage
|
public class UnityMessage
|
||||||
{
|
{
|
||||||
public String name;
|
public String name;
|
||||||
public JObject data;
|
public JObject data;
|
||||||
public Action<object> callBack;
|
public Action<object> callBack;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UnityMessageManager : MonoBehaviour
|
public class UnityMessageManager : MonoBehaviour
|
||||||
{
|
{
|
||||||
#if UNITY_IOS && !UNITY_EDITOR
|
#if UNITY_IOS && !UNITY_EDITOR
|
||||||
[DllImport("__Internal")]
|
[DllImport("__Internal")]
|
||||||
private static extern void onUnityMessage(string message);
|
private static extern void onUnityMessage(string message);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public const string MessagePrefix = "@UnityMessage@";
|
public const string MessagePrefix = "@UnityMessage@";
|
||||||
|
|
||||||
private static int ID = 0;
|
private static int ID = 0;
|
||||||
|
|
||||||
private static int generateId()
|
private static int generateId()
|
||||||
{
|
{
|
||||||
ID = ID + 1;
|
ID = ID + 1;
|
||||||
return ID;
|
return ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UnityMessageManager Instance { get; private set; }
|
public static UnityMessageManager Instance { get; private set; }
|
||||||
|
|
||||||
public delegate void MessageDelegate(string message);
|
public delegate void MessageDelegate(string message);
|
||||||
public event MessageDelegate OnMessage;
|
public event MessageDelegate OnMessage;
|
||||||
|
|
||||||
public delegate void MessageHandlerDelegate(MessageHandler handler);
|
public delegate void MessageHandlerDelegate(MessageHandler handler);
|
||||||
public event MessageHandlerDelegate OnFlutterMessage;
|
public event MessageHandlerDelegate OnFlutterMessage;
|
||||||
|
|
||||||
private Dictionary<int, UnityMessage> waitCallbackMessageMap = new Dictionary<int, UnityMessage>();
|
private Dictionary<int, UnityMessage> waitCallbackMessageMap = new Dictionary<int, UnityMessage>();
|
||||||
|
|
||||||
static UnityMessageManager()
|
static UnityMessageManager()
|
||||||
{
|
{
|
||||||
GameObject go = new GameObject("UnityMessageManager");
|
GameObject go = new GameObject("UnityMessageManager");
|
||||||
DontDestroyOnLoad(go);
|
DontDestroyOnLoad(go);
|
||||||
Instance = go.AddComponent<UnityMessageManager>();
|
Instance = go.AddComponent<UnityMessageManager>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Awake()
|
void Awake()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendMessageToFlutter(string message)
|
public void SendMessageToFlutter(string message)
|
||||||
{
|
{
|
||||||
if (Application.platform == RuntimePlatform.Android)
|
if (Application.platform == RuntimePlatform.Android)
|
||||||
{
|
{
|
||||||
using (AndroidJavaClass jc = new AndroidJavaClass("com.reactnative.unity.view.UnityUtils"))
|
using (AndroidJavaClass jc = new AndroidJavaClass("com.rexraphael.flutterunitywidget.UnityUtils"))
|
||||||
{
|
{
|
||||||
jc.CallStatic("onUnityMessage", message);
|
jc.CallStatic("onUnityMessage", message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (Application.platform == RuntimePlatform.IPhonePlayer)
|
else if (Application.platform == RuntimePlatform.IPhonePlayer)
|
||||||
{
|
{
|
||||||
#if UNITY_IOS && !UNITY_EDITOR
|
#if UNITY_IOS && !UNITY_EDITOR
|
||||||
onUnityMessage(message);
|
onUnityMessage(message);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendMessageToFlutter(UnityMessage message)
|
public void SendMessageToFlutter(UnityMessage message)
|
||||||
{
|
{
|
||||||
int id = generateId();
|
int id = generateId();
|
||||||
if (message.callBack != null)
|
if (message.callBack != null)
|
||||||
{
|
{
|
||||||
waitCallbackMessageMap.Add(id, message);
|
waitCallbackMessageMap.Add(id, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
JObject o = JObject.FromObject(new
|
JObject o = JObject.FromObject(new
|
||||||
{
|
{
|
||||||
id = id,
|
id = id,
|
||||||
seq = message.callBack != null ? "start" : "",
|
seq = message.callBack != null ? "start" : "",
|
||||||
name = message.name,
|
name = message.name,
|
||||||
data = message.data
|
data = message.data
|
||||||
});
|
});
|
||||||
UnityMessageManager.Instance.SendMessageToFlutter(MessagePrefix + o.ToString());
|
UnityMessageManager.Instance.SendMessageToFlutter(MessagePrefix + o.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void onMessage(string message)
|
void onMessage(string message)
|
||||||
{
|
{
|
||||||
if (OnMessage != null)
|
if (OnMessage != null)
|
||||||
{
|
{
|
||||||
OnMessage(message);
|
OnMessage(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onFlutterMessage(string message)
|
void onFlutterMessage(string message)
|
||||||
{
|
{
|
||||||
if (message.StartsWith(MessagePrefix))
|
if (message.StartsWith(MessagePrefix))
|
||||||
{
|
{
|
||||||
message = message.Replace(MessagePrefix, "");
|
message = message.Replace(MessagePrefix, "");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageHandler handler = MessageHandler.Deserialize(message);
|
MessageHandler handler = MessageHandler.Deserialize(message);
|
||||||
if ("end".Equals(handler.seq))
|
if ("end".Equals(handler.seq))
|
||||||
{
|
{
|
||||||
// handle callback message
|
// handle callback message
|
||||||
UnityMessage m;
|
UnityMessage m;
|
||||||
if (waitCallbackMessageMap.TryGetValue(handler.id, out m))
|
if (waitCallbackMessageMap.TryGetValue(handler.id, out m))
|
||||||
{
|
{
|
||||||
waitCallbackMessageMap.Remove(handler.id);
|
waitCallbackMessageMap.Remove(handler.id);
|
||||||
if (m.callBack != null)
|
if (m.callBack != null)
|
||||||
{
|
{
|
||||||
m.callBack(handler.getData<object>()); // todo
|
m.callBack(handler.getData<object>()); // todo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (OnFlutterMessage != null)
|
if (OnFlutterMessage != null)
|
||||||
{
|
{
|
||||||
OnFlutterMessage(handler);
|
OnFlutterMessage(handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue