From 86810e55657ac97ddaac0c99ff8386ea77ec7fce Mon Sep 17 00:00:00 2001 From: chiuan Date: Sat, 27 May 2017 18:35:25 +0800 Subject: [PATCH] - fix unity5.6 bug for ugui setting --- .gitignore | 2 + .vscode/launch.json | 36 + .vscode/settings.json | 56 + Assets/Plugins.meta | 9 + Assets/Plugins/Editor.meta | 9 + Assets/Plugins/Editor/VSCode.cs | 1378 +++++++++++++++++ Assets/Plugins/Editor/VSCode.cs.meta | 12 + Assets/Sample/GameManager/GameMain.cs | 1 + .../Sample/Resources/UIPrefab/UIRoot.prefab | Bin 11244 -> 11268 bytes Assets/UIFramework/Source/TTUIRoot.cs | 276 ++-- Assets/test.unity | Bin 19820 -> 18984 bytes ProjectSettings/ClusterInputManager.asset | Bin 0 -> 4104 bytes ProjectSettings/ProjectSettings.asset | Bin 39985 -> 52270 bytes ProjectSettings/ProjectVersion.txt | 3 +- ProjectSettings/UnityConnectSettings.asset | Bin 0 -> 4216 bytes 15 files changed, 1641 insertions(+), 141 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 Assets/Plugins.meta create mode 100644 Assets/Plugins/Editor.meta create mode 100755 Assets/Plugins/Editor/VSCode.cs create mode 100644 Assets/Plugins/Editor/VSCode.cs.meta create mode 100644 ProjectSettings/ClusterInputManager.asset create mode 100644 ProjectSettings/UnityConnectSettings.asset diff --git a/.gitignore b/.gitignore index 997e1a4..8bc9334 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ /.vs/ /TTUIFramework.CSharp.csproj /TTUIFramework.sln +*.csproj +/obj/ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..06a7741 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,36 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Unity Editor", + "type": "unity", + "request": "launch" + }, + { + "name": "Windows Player", + "type": "unity", + "request": "launch" + }, + { + "name": "OSX Player", + "type": "unity", + "request": "launch" + }, + { + "name": "Linux Player", + "type": "unity", + "request": "launch" + }, + { + "name": "iOS Player", + "type": "unity", + "request": "launch" + }, + { + "name": "Android Player", + "type": "unity", + "request": "launch" + + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..4edd970 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,56 @@ +{ + "files.exclude": + { + "**/.DS_Store":true, + "**/.git":true, + "**/.gitignore":true, + "**/.gitmodules":true, + "**/*.booproj":true, + "**/*.pidb":true, + "**/*.suo":true, + "**/*.user":true, + "**/*.userprefs":true, + "**/*.unityproj":true, + "**/*.dll":true, + "**/*.exe":true, + "**/*.pdf":true, + "**/*.mid":true, + "**/*.midi":true, + "**/*.wav":true, + "**/*.gif":true, + "**/*.ico":true, + "**/*.jpg":true, + "**/*.jpeg":true, + "**/*.png":true, + "**/*.psd":true, + "**/*.tga":true, + "**/*.tif":true, + "**/*.tiff":true, + "**/*.3ds":true, + "**/*.3DS":true, + "**/*.fbx":true, + "**/*.FBX":true, + "**/*.lxo":true, + "**/*.LXO":true, + "**/*.ma":true, + "**/*.MA":true, + "**/*.obj":true, + "**/*.OBJ":true, + "**/*.asset":true, + "**/*.cubemap":true, + "**/*.flare":true, + "**/*.mat":true, + "**/*.meta":true, + "**/*.prefab":true, + "**/*.unity":true, + "build/":true, + "Build/":true, + "Library/":true, + "library/":true, + "obj/":true, + "Obj/":true, + "ProjectSettings/":true, + "temp/":true, + "Temp/":true + } +} \ No newline at end of file diff --git a/Assets/Plugins.meta b/Assets/Plugins.meta new file mode 100644 index 0000000..3f9f225 --- /dev/null +++ b/Assets/Plugins.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: bc32e9ab610d0408a8b23d4dad02eb03 +folderAsset: yes +timeCreated: 1495875513 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Editor.meta b/Assets/Plugins/Editor.meta new file mode 100644 index 0000000..d4f5b9f --- /dev/null +++ b/Assets/Plugins/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 005f50411d6f74ae89c90580cc8a78e9 +folderAsset: yes +timeCreated: 1495875517 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Editor/VSCode.cs b/Assets/Plugins/Editor/VSCode.cs new file mode 100755 index 0000000..c56b19c --- /dev/null +++ b/Assets/Plugins/Editor/VSCode.cs @@ -0,0 +1,1378 @@ +/* + * Unity VSCode Support + * + * Seamless support for Microsoft Visual Studio Code in Unity + * + * Version: + * 2.8 + * + * Authors: + * Matthew Davey + */ +namespace dotBunny.Unity +{ + using System; + using System.IO; + using System.Text.RegularExpressions; + using UnityEditor; + using UnityEngine; + + [InitializeOnLoad] + public static class VSCode + { + /// + /// Current Version Number + /// + public const float Version = 2.8f; + + /// + /// Current Version Code + /// + public const string VersionCode = "-RELEASE"; + + /// + /// Additional File Extensions + /// + public const string FileExtensions = ".ts, .bjs, .javascript, .json, .html, .shader, .template"; + + /// + /// Download URL for Unity Debbuger + /// + public const string UnityDebuggerURL = "https://unity.gallery.vsassets.io/_apis/public/gallery/publisher/unity/extension/unity-debug/latest/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage"; + + #region Properties + + /// + /// Path to VSCode executable + public static string CodePath + { + get + { + string current = EditorPrefs.GetString("VSCode_CodePath", ""); + if(current == "" || !VSCodeExists(current)) + { + //Value not set, set to "" or current path is invalid, try to autodetect it + //If autodetect fails, a error will be printed and the default value set + EditorPrefs.SetString("VSCode_CodePath", AutodetectCodePath()); + //If its not installed or the install folder isn't a "normal" one, + //AutodetectCodePath will print a error message to the Unity Console + } + return EditorPrefs.GetString("VSCode_CodePath", current); + } + set + { + EditorPrefs.SetString("VSCode_CodePath", value); + } + } + + /// + /// Get Program Files Path + /// + /// The platforms "Program Files" path. + static string ProgramFilesx86() + { + if( 8 == IntPtr.Size + || (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432")))) + { + return Environment.GetEnvironmentVariable("ProgramFiles(x86)"); + } + + return Environment.GetEnvironmentVariable("ProgramFiles"); + } + + + /// + /// Should debug information be displayed in the Unity terminal? + /// + public static bool Debug + { + get + { + return EditorPrefs.GetBool("VSCode_Debug", false); + } + set + { + EditorPrefs.SetBool("VSCode_Debug", value); + } + } + + /// + /// Is the Visual Studio Code Integration Enabled? + /// + /// + /// We do not want to automatically turn it on, for in larger projects not everyone is using VSCode + /// + public static bool Enabled + { + get + { + return EditorPrefs.GetBool("VSCode_Enabled", false); + } + set + { + // When turning the plugin on, we should remove all the previous project files + if (!Enabled && value) + { + ClearProjectFiles(); + } + EditorPrefs.SetBool("VSCode_Enabled", value); + } + } + public static bool UseUnityDebugger + { + get + { + return EditorPrefs.GetBool("VSCode_UseUnityDebugger", false); + } + set + { + if ( value != UseUnityDebugger ) { + + // Set value + EditorPrefs.SetBool("VSCode_UseUnityDebugger", value); + + // Do not write the launch JSON file because the debugger uses its own + if ( value ) { + WriteLaunchFile = false; + } + + // Update launch file + UpdateLaunchFile(); + } + } + } + + /// + /// When opening a project in Unity, should it automatically open in VS Code. + /// + public static bool AutoOpenEnabled + { + get + { + return EditorPrefs.GetBool("VSCode_AutoOpenEnabled", false); + } + set + { + EditorPrefs.SetBool("VSCode_AutoOpenEnabled", value); + } + } + + /// + /// Should the launch.json file be written? + /// + /// + /// Useful to disable if someone has their own custom one rigged up + /// + public static bool WriteLaunchFile + { + get + { + return EditorPrefs.GetBool("VSCode_WriteLaunchFile", true); + } + set + { + EditorPrefs.SetBool("VSCode_WriteLaunchFile", value); + } + } + + /// + /// Should the plugin automatically update itself. + /// + static bool AutomaticUpdates + { + get + { + return EditorPrefs.GetBool("VSCode_AutomaticUpdates", false); + } + set + { + EditorPrefs.SetBool("VSCode_AutomaticUpdates", value); + } + } + + static float GitHubVersion + { + get + { + return EditorPrefs.GetFloat("VSCode_GitHubVersion", Version); + } + set + { + EditorPrefs.SetFloat("VSCode_GitHubVersion", value); + } + } + + /// + /// When was the last time that the plugin was updated? + /// + static DateTime LastUpdate + { + get + { + // Feature creation date. + DateTime lastTime = new DateTime(2015, 10, 8); + + if (EditorPrefs.HasKey("VSCode_LastUpdate")) + { + DateTime.TryParse(EditorPrefs.GetString("VSCode_LastUpdate"), out lastTime); + } + return lastTime; + } + set + { + EditorPrefs.SetString("VSCode_LastUpdate", value.ToString()); + } + } + + /// + /// Quick reference to the VSCode launch settings file + /// + static string LaunchPath + { + get + { + return SettingsFolder + System.IO.Path.DirectorySeparatorChar + "launch.json"; + } + } + + /// + /// The full path to the project + /// + static string ProjectPath + { + get + { + return System.IO.Path.GetDirectoryName(UnityEngine.Application.dataPath); + } + } + + /// + /// Should the script editor be reverted when quiting Unity. + /// + /// + /// Useful for environments where you do not use VSCode for everything. + /// + static bool RevertExternalScriptEditorOnExit + { + get + { + return EditorPrefs.GetBool("VSCode_RevertScriptEditorOnExit", true); + } + set + { + EditorPrefs.SetBool("VSCode_RevertScriptEditorOnExit", value); + } + } + + /// + /// Quick reference to the VSCode settings folder + /// + static string SettingsFolder + { + get + { + return ProjectPath + System.IO.Path.DirectorySeparatorChar + ".vscode"; + } + } + + static string SettingsPath + { + + get + { + return SettingsFolder + System.IO.Path.DirectorySeparatorChar + "settings.json"; + } + } + + static int UpdateTime + { + get + { + return EditorPrefs.GetInt("VSCode_UpdateTime", 7); + } + set + { + EditorPrefs.SetInt("VSCode_UpdateTime", value); + } + } + + #endregion + + /// + /// Integration Constructor + /// + static VSCode() + { + if (Enabled) + { + UpdateUnityPreferences(true); + UpdateLaunchFile(); + + // Add Update Check + DateTime targetDate = LastUpdate.AddDays(UpdateTime); + if (DateTime.Now >= targetDate && AutomaticUpdates) + { + CheckForUpdate(); + } + + // Open VS Code automatically when project is loaded + if (AutoOpenEnabled) + { + CheckForAutoOpen(); + } + + } + + // Event for when script is reloaded + System.AppDomain.CurrentDomain.DomainUnload += System_AppDomain_CurrentDomain_DomainUnload; + } + static void System_AppDomain_CurrentDomain_DomainUnload(object sender, System.EventArgs e) + { + if (Enabled && RevertExternalScriptEditorOnExit) + { + UpdateUnityPreferences(false); + } + } + + + #region Public Members + + /// + /// Force Unity To Write Project File + /// + /// + /// Reflection! + /// + public static void SyncSolution() + { + System.Type T = System.Type.GetType("UnityEditor.SyncVS,UnityEditor"); + System.Reflection.MethodInfo SyncSolution = T.GetMethod("SyncSolution", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); + SyncSolution.Invoke(null, null); + + } + + /// + /// Update the solution files so that they work with VS Code + /// + public static void UpdateSolution() + { + // No need to process if we are not enabled + if (!VSCode.Enabled) + { + return; + } + + if (VSCode.Debug) + { + UnityEngine.Debug.Log("[VSCode] Updating Solution & Project Files"); + } + + var currentDirectory = Directory.GetCurrentDirectory(); + var solutionFiles = Directory.GetFiles(currentDirectory, "*.sln"); + var projectFiles = Directory.GetFiles(currentDirectory, "*.csproj"); + + foreach (var filePath in solutionFiles) + { + string content = File.ReadAllText(filePath); + content = ScrubSolutionContent(content); + + File.WriteAllText(filePath, content); + + ScrubFile(filePath); + } + + foreach (var filePath in projectFiles) + { + string content = File.ReadAllText(filePath); + content = ScrubProjectContent(content); + + File.WriteAllText(filePath, content); + + ScrubFile(filePath); + } + + } + + #endregion + + #region Private Members + + /// + /// Try to find automatically the installation of VSCode + /// + static string AutodetectCodePath() + { + string[] possiblePaths = +#if UNITY_EDITOR_OSX + { + "/Applications/Visual Studio Code.app", + "/Applications/Visual Studio Code - Insiders.app" + }; +#elif UNITY_EDITOR_WIN + { + ProgramFilesx86() + Path.DirectorySeparatorChar + "Microsoft VS Code" + + Path.DirectorySeparatorChar + "bin" + Path.DirectorySeparatorChar + "code.cmd", + ProgramFilesx86() + Path.DirectorySeparatorChar + "Microsoft VS Code Insiders" + + Path.DirectorySeparatorChar + "bin" + Path.DirectorySeparatorChar + "code-insiders.cmd" + }; +#else + { + "/usr/bin/code", + "/bin/code", + "/usr/local/bin/code" + }; +#endif + for(int i = 0; i < possiblePaths.Length; i++) + { + if(VSCodeExists(possiblePaths[i])) + { + return possiblePaths[i]; + } + } + PrintNotFound(possiblePaths[0]); + return possiblePaths[0]; //returns the default one, printing a warning message 'executable not found' + } + + /// + /// Call VSCode with arguments + /// + static void CallVSCode(string args) + { + System.Diagnostics.Process proc = new System.Diagnostics.Process(); + if(!VSCodeExists(CodePath)) + { + PrintNotFound(CodePath); + return; + } + +#if UNITY_EDITOR_OSX + proc.StartInfo.FileName = "open"; + + // Check the path to see if there is "Insiders" + if (CodePath.Contains("Insiders")) + { + proc.StartInfo.Arguments = " -n -b \"com.microsoft.VSCodeInsiders\" --args " + args.Replace(@"\", @"\\"); + } + else + { + proc.StartInfo.Arguments = " -n -b \"com.microsoft.VSCode\" --args " + args.Replace(@"\", @"\\"); + } + + proc.StartInfo.UseShellExecute = false; +#elif UNITY_EDITOR_WIN + proc.StartInfo.FileName = CodePath; + proc.StartInfo.Arguments = args; + proc.StartInfo.UseShellExecute = false; +#else + proc.StartInfo.FileName = CodePath; + proc.StartInfo.Arguments = args.Replace(@"\", @"\\"); + proc.StartInfo.UseShellExecute = false; +#endif + proc.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; + proc.StartInfo.CreateNoWindow = true; + proc.StartInfo.RedirectStandardOutput = true; + proc.Start(); + } + + /// + /// Check for Updates with GitHub + /// + static void CheckForUpdate() + { + var fileContent = string.Empty; + + EditorUtility.DisplayProgressBar("VSCode", "Checking for updates ...", 0.5f); + + // Because were not a runtime framework, lets just use the simplest way of doing this + try + { + using (var webClient = new System.Net.WebClient()) + { + fileContent = webClient.DownloadString("https://raw.githubusercontent.com/dotBunny/VSCode/master/Plugins/Editor/VSCode.cs"); + } + } + catch (Exception e) + { + if (Debug) + { + UnityEngine.Debug.Log("[VSCode] " + e.Message); + + } + + // Don't go any further if there is an error + return; + } + finally + { + EditorUtility.ClearProgressBar(); + } + + // Set the last update time + LastUpdate = DateTime.Now; + + // Fix for oddity in downlo + if (fileContent.Substring(0, 2) != "/*") + { + int startPosition = fileContent.IndexOf("/*", StringComparison.CurrentCultureIgnoreCase); + + // Jump over junk characters + fileContent = fileContent.Substring(startPosition); + } + + string[] fileExploded = fileContent.Split('\n'); + if (fileExploded.Length > 7) + { + float github = Version; + if (float.TryParse(fileExploded[6].Replace("*", "").Trim(), out github)) + { + GitHubVersion = github; + } + + + if (github > Version) + { + var GUIDs = AssetDatabase.FindAssets("t:Script VSCode"); + var path = Application.dataPath.Substring(0, Application.dataPath.Length - "/Assets".Length) + System.IO.Path.DirectorySeparatorChar + + AssetDatabase.GUIDToAssetPath(GUIDs[0]).Replace('/', System.IO.Path.DirectorySeparatorChar); + + if (EditorUtility.DisplayDialog("VSCode Update", "A newer version of the VSCode plugin is available, would you like to update your version?", "Yes", "No")) + { + // Always make sure the file is writable + System.IO.FileInfo fileInfo = new System.IO.FileInfo(path); + fileInfo.IsReadOnly = false; + + // Write update file + File.WriteAllText(path, fileContent); + + // Force update on text file + AssetDatabase.ImportAsset(AssetDatabase.GUIDToAssetPath(GUIDs[0]), ImportAssetOptions.ForceUpdate); + } + + } + } + } + + /// + /// Checks whether it should auto-open VSCode + /// + /// + /// VSCode() gets called on Launch and Run, through IntializeOnLoad + /// https://docs.unity3d.com/ScriptReference/InitializeOnLoadAttribute.html + /// To make sure it only opens VSCode when Unity (re)launches (i.e. opens a project), + /// we compare the launch time, which we calculate using EditorApplication.timeSinceStartup. + /// + static void CheckForAutoOpen() + { + double timeInSeconds = (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; + int unityLaunchTimeInSeconds = (int)(timeInSeconds - EditorApplication.timeSinceStartup); + int prevUnityLaunchTime = EditorPrefs.GetInt("VSCode_UnityLaunchTime", 0); + // If launch time has changed, then Unity was re-opened + if (unityLaunchTimeInSeconds > prevUnityLaunchTime) { + // Launch VSCode + VSCode.MenuOpenProject(); + // Save new launch time + EditorPrefs.SetInt("VSCode_UnityLaunchTime", unityLaunchTimeInSeconds); + } + } + + /// + /// Clear out any existing project files and lingering stuff that might cause problems + /// + static void ClearProjectFiles() + { + var currentDirectory = Directory.GetCurrentDirectory(); + var solutionFiles = Directory.GetFiles(currentDirectory, "*.sln"); + var projectFiles = Directory.GetFiles(currentDirectory, "*.csproj"); + var unityProjectFiles = Directory.GetFiles(currentDirectory, "*.unityproj"); + + foreach (string solutionFile in solutionFiles) + { + File.Delete(solutionFile); + } + foreach (string projectFile in projectFiles) + { + File.Delete(projectFile); + } + foreach (string unityProjectFile in unityProjectFiles) + { + File.Delete(unityProjectFile); + } + + // Replace with our clean files (only in Unity 5) +#if !UNITY_4_0 && !UNITY_4_1 && !UNITY_4_2 && !UNITY_4_3 && !UNITY_4_5 && !UNITY_4_6 && !UNITY_4_7 + SyncSolution(); +#endif + } + + /// + /// Force Unity Preferences Window To Read From Settings + /// + static void FixUnityPreferences() + { + // I want that window, please and thank you + System.Type T = System.Type.GetType("UnityEditor.PreferencesWindow,UnityEditor"); + + if (EditorWindow.focusedWindow == null) + return; + + // Only run this when the editor window is visible (cause its what screwed us up) + if (EditorWindow.focusedWindow.GetType() == T) + { + var window = EditorWindow.GetWindow(T, true, "Unity Preferences"); + + + if (window == null) + { + if (Debug) + { + UnityEngine.Debug.Log("[VSCode] No Preferences Window Found (really?)"); + } + return; + } + + var invokerType = window.GetType(); + var invokerMethod = invokerType.GetMethod("ReadPreferences", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + + if (invokerMethod != null) + { + invokerMethod.Invoke(window, null); + } + else if (Debug) + { + UnityEngine.Debug.Log("[VSCode] No Reflection Method Found For Preferences"); + } + } + } + + /// + /// Determine what port Unity is listening for on Windows + /// + static int GetDebugPort() + { +#if UNITY_EDITOR_WIN + System.Diagnostics.Process process = new System.Diagnostics.Process(); + process.StartInfo.FileName = "netstat"; + process.StartInfo.Arguments = "-a -n -o -p TCP"; + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardOutput = true; + process.Start(); + + string output = process.StandardOutput.ReadToEnd(); + string[] lines = output.Split('\n'); + + process.WaitForExit(); + + foreach (string line in lines) + { + string[] tokens = Regex.Split(line, "\\s+"); + if (tokens.Length > 4) + { + int test = -1; + int.TryParse(tokens[5], out test); + + if (test > 1023) + { + try + { + var p = System.Diagnostics.Process.GetProcessById(test); + if (p.ProcessName == "Unity") + { + return test; + } + } + catch + { + + } + } + } + } +#else + System.Diagnostics.Process process = new System.Diagnostics.Process(); + process.StartInfo.FileName = "lsof"; + process.StartInfo.Arguments = "-c /^Unity$/ -i 4tcp -a"; + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardOutput = true; + process.Start(); + + // Not thread safe (yet!) + string output = process.StandardOutput.ReadToEnd(); + string[] lines = output.Split('\n'); + + process.WaitForExit(); + + foreach (string line in lines) + { + int port = -1; + if (line.StartsWith("Unity")) + { + string[] portions = line.Split(new string[] { "TCP *:" }, System.StringSplitOptions.None); + if (portions.Length >= 2) + { + Regex digitsOnly = new Regex(@"[^\d]"); + string cleanPort = digitsOnly.Replace(portions[1], ""); + if (int.TryParse(cleanPort, out port)) + { + if (port > -1) + { + return port; + } + } + } + } + } +#endif + return -1; + } + + /// + /// Manually install the original Unity Debuger + /// + /// + /// This should auto update to the latest. + /// + static void InstallUnityDebugger() + { + EditorUtility.DisplayProgressBar("VSCode", "Downloading Unity Debugger ...", 0.1f); + byte[] fileContent; + + try + { + using (var webClient = new System.Net.WebClient()) + { + fileContent = webClient.DownloadData(UnityDebuggerURL); + } + } + catch (Exception e) + { + if (Debug) + { + UnityEngine.Debug.Log("[VSCode] " + e.Message); + } + // Don't go any further if there is an error + return; + } + finally + { + EditorUtility.ClearProgressBar(); + } + + // Do we have a file to install? + if ( fileContent != null ) { + string fileName = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".vsix"; + File.WriteAllBytes(fileName, fileContent); + + CallVSCode(fileName); + } + + } + + // HACK: This is in until Unity can figure out why MD keeps opening even though a different program is selected. + [MenuItem("Assets/Open C# Project In Code", false, 1000)] + static void MenuOpenProject() + { + // Force the project files to be sync + SyncSolution(); + + // Load Project + CallVSCode("\"" + ProjectPath + "\""); + } + + /// + /// Print a error message to the Unity Console about not finding the code executable + /// + static void PrintNotFound(string path) + { + UnityEngine.Debug.LogError("[VSCode] Code executable in '" + path + "' not found. Check your" + + "Visual Studio Code installation and insert the correct path in the Preferences menu."); + } + + [MenuItem("Assets/Open C# Project In Code", true, 1000)] + static bool ValidateMenuOpenProject() + { + return Enabled; + } + + /// + /// VS Code Integration Preferences Item + /// + /// + /// Contains all 3 toggles: Enable/Disable; Debug On/Off; Writing Launch File On/Off + /// + [PreferenceItem("VSCode")] + static void VSCodePreferencesItem() + { + if (EditorApplication.isCompiling) + { + EditorGUILayout.HelpBox("Please wait for Unity to finish compiling. \nIf the window doesn't refresh, simply click on the window or move it around to cause a repaint to happen.", MessageType.Warning); + return; + } + EditorGUILayout.BeginVertical(); + + var developmentInfo = "Support development of this plugin, follow @reapazor and @dotbunny on Twitter."; + var versionInfo = string.Format("{0:0.00}", Version) + VersionCode + ", GitHub version @ " + string.Format("{0:0.00}", GitHubVersion); + EditorGUILayout.HelpBox(developmentInfo + " --- [ " + versionInfo + " ]", MessageType.None); + + EditorGUI.BeginChangeCheck(); + +// Need the VS Code executable + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("VS Code Path", GUILayout.Width(75)); +#if UNITY_5_3_OR_NEWER + CodePath = EditorGUILayout.DelayedTextField(CodePath, GUILayout.ExpandWidth(true)); +#else + CodePath = EditorGUILayout.TextField(CodePath, GUILayout.ExpandWidth(true)); +#endif + GUI.SetNextControlName("PathSetButton"); + if(GUILayout.Button("...", GUILayout.Height(14), GUILayout.Width(20))) + { + GUI.FocusControl("PathSetButton"); + string path = EditorUtility.OpenFilePanel( "Visual Studio Code Executable", "", "" ); + if( path.Length != 0 && File.Exists(path) || Directory.Exists(path)) + { + CodePath = path; + } + } + EditorGUILayout.EndHorizontal(); + EditorGUILayout.Space(); + + Enabled = EditorGUILayout.Toggle(new GUIContent("Enable Integration", "Should the integration work its magic for you?"), Enabled); + + UseUnityDebugger = EditorGUILayout.Toggle(new GUIContent("Use Unity Debugger", "Should the integration integrate with Unity's VSCode Extension (must be installed)."), UseUnityDebugger); + + AutoOpenEnabled = EditorGUILayout.Toggle(new GUIContent("Enable Auto Open", "When opening a project in Unity, should it automatically open in VS Code?"), AutoOpenEnabled); + + EditorGUILayout.Space(); + RevertExternalScriptEditorOnExit = EditorGUILayout.Toggle(new GUIContent("Revert Script Editor On Unload", "Should the external script editor setting be reverted to its previous setting on project unload? This is useful if you do not use Code with all your projects."),RevertExternalScriptEditorOnExit); + + Debug = EditorGUILayout.Toggle(new GUIContent("Output Messages To Console", "Should informational messages be sent to Unity's Console?"), Debug); + + WriteLaunchFile = EditorGUILayout.Toggle(new GUIContent("Always Write Launch File", "Always write the launch.json settings when entering play mode?"), WriteLaunchFile); + + EditorGUILayout.Space(); + + AutomaticUpdates = EditorGUILayout.Toggle(new GUIContent("Automatic Updates", "Should the plugin automatically update itself?"), AutomaticUpdates); + + UpdateTime = EditorGUILayout.IntSlider(new GUIContent("Update Timer (Days)", "After how many days should updates be checked for?"), UpdateTime, 1, 31); + + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + if (EditorGUI.EndChangeCheck()) + { + UpdateUnityPreferences(Enabled); + + // TODO: Force Unity To Reload Preferences + // This seems to be a hick up / issue + if (VSCode.Debug) + { + if (Enabled) + { + UnityEngine.Debug.Log("[VSCode] Integration Enabled"); + } + else + { + UnityEngine.Debug.Log("[VSCode] Integration Disabled"); + } + } + } + + if (GUILayout.Button(new GUIContent("Force Update", "Check for updates to the plugin, right NOW!"))) + { + CheckForUpdate(); + EditorGUILayout.EndVertical(); + return; + } + if (GUILayout.Button(new GUIContent("Write Workspace Settings", "Output a default set of workspace settings for VSCode to use, ignoring many different types of files."))) + { + WriteWorkspaceSettings(); + EditorGUILayout.EndVertical(); + return; + } + EditorGUILayout.Space(); + + if (UseUnityDebugger) + { + EditorGUILayout.HelpBox("In order for the \"Use Unity Debuggger\" option to function above, you need to have installed the Unity Debugger Extension for Visual Studio Code.", MessageType.Warning); + if (GUILayout.Button(new GUIContent("Install Unity Debugger", "Install the Unity Debugger Extension into Code"))) + { + InstallUnityDebugger(); + EditorGUILayout.EndVertical(); + return; + } + } + + } + + /// + /// Asset Open Callback (from Unity) + /// + /// + /// Called when Unity is about to open an asset. + /// + [UnityEditor.Callbacks.OnOpenAssetAttribute()] + static bool OnOpenedAsset(int instanceID, int line) + { + // bail out if we are not using VSCode + if (!Enabled) + { + return false; + } + + // current path without the asset folder + string appPath = ProjectPath; + + // determine asset that has been double clicked in the project view + UnityEngine.Object selected = EditorUtility.InstanceIDToObject(instanceID); + + // additional file extensions + string selectedFilePath = AssetDatabase.GetAssetPath(selected); + string selectedFileExt = Path.GetExtension(selectedFilePath); + if (selectedFileExt == null) { + selectedFileExt = String.Empty; + } + if (!String.IsNullOrEmpty(selectedFileExt)) { + selectedFileExt = selectedFileExt.ToLower(); + } + + // open supported object types + if (selected.GetType().ToString() == "UnityEditor.MonoScript" || + selected.GetType().ToString() == "UnityEngine.Shader" || + VSCode.FileExtensions.IndexOf(selectedFileExt, StringComparison.OrdinalIgnoreCase) >= 0) + { + string completeFilepath = appPath + Path.DirectorySeparatorChar + AssetDatabase.GetAssetPath(selected); + + string args = null; + if (line == -1) + { + args = "\"" + ProjectPath + "\" \"" + completeFilepath + "\" -r"; + } + else + { + args = "\"" + ProjectPath + "\" -g \"" + completeFilepath + ":" + line.ToString() + "\" -r"; + } + // call 'open' + CallVSCode(args); + + return true; + } + + // Didnt find a code file? let Unity figure it out + return false; + + } + + /// + /// Executed when the Editor's playmode changes allowing for capture of required data + /// + static void OnPlaymodeStateChanged() + { + if (UnityEngine.Application.isPlaying && EditorApplication.isPlayingOrWillChangePlaymode) + { + UpdateLaunchFile(); + } + } + + /// + /// Detect when scripts are reloaded and relink playmode detection + /// + [UnityEditor.Callbacks.DidReloadScripts()] + static void OnScriptReload() + { + EditorApplication.playmodeStateChanged -= OnPlaymodeStateChanged; + EditorApplication.playmodeStateChanged += OnPlaymodeStateChanged; + } + + /// + /// Remove extra/erroneous lines from a file. + static void ScrubFile(string path) + { + string[] lines = File.ReadAllLines(path); + System.Collections.Generic.List newLines = new System.Collections.Generic.List(); + for (int i = 0; i < lines.Length; i++) + { + // Check Empty + if (string.IsNullOrEmpty(lines[i].Trim()) || lines[i].Trim() == "\t" || lines[i].Trim() == "\t\t") + { + + } + else + { + newLines.Add(lines[i]); + } + } + File.WriteAllLines(path, newLines.ToArray()); + } + + /// + /// Remove extra/erroneous data from project file (content). + /// + static string ScrubProjectContent(string content) + { + if (content.Length == 0) + return ""; + +#if !UNITY_EDITOR_WIN + // Moved to 3.5, 2.0 is legacy. + if (content.IndexOf("v3.5") != -1) + { + content = Regex.Replace(content, "v3.5", "v2.0"); + } +#endif + + string targetPath = "";// "Temp" + Path.DirectorySeparatorChar + "bin" + Path.DirectorySeparatorChar + "Debug" + Path.DirectorySeparatorChar + ""; //OutputPath + string langVersion = "default"; + + + bool found = true; + int location = 0; + string addedOptions = ""; + int startLocation = -1; + int endLocation = -1; + int endLength = 0; + + while (found) + { + startLocation = -1; + endLocation = -1; + endLength = 0; + addedOptions = ""; + startLocation = content.IndexOf("", startLocation); + endLength = (endLocation - startLocation); + + + if (endLocation == -1) + { + found = false; + continue; + } + else + { + found = true; + location = endLocation; + } + + if (content.Substring(startLocation, endLength).IndexOf("") == -1) + { + addedOptions += "\n\r\t" + targetPath + "\n\r"; + } + + if (content.Substring(startLocation, endLength).IndexOf("") == -1) + { + addedOptions += "\n\r\t" + langVersion + "\n\r"; + } + + if (!string.IsNullOrEmpty(addedOptions)) + { + content = content.Substring(0, endLocation) + addedOptions + content.Substring(endLocation); + } + } + else + { + found = false; + } + } + + return content; + } + + /// + /// Remove extra/erroneous data from solution file (content). + /// + static string ScrubSolutionContent(string content) + { + // Replace Solution Version + content = content.Replace( + "Microsoft Visual Studio Solution File, Format Version 11.00\r\n# Visual Studio 2008\r\n", + "\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio 2012"); + + // Remove Solution Properties (Unity Junk) + int startIndex = content.IndexOf("GlobalSection(SolutionProperties) = preSolution"); + if (startIndex != -1) + { + int endIndex = content.IndexOf("EndGlobalSection", startIndex); + content = content.Substring(0, startIndex) + content.Substring(endIndex + 16); + } + + return content; + } + + /// + /// Update Visual Studio Code Launch file + /// + static void UpdateLaunchFile() + { + if (!VSCode.Enabled) + { + return; + } + else if (VSCode.UseUnityDebugger) + { + if (!Directory.Exists(VSCode.SettingsFolder)) + System.IO.Directory.CreateDirectory(VSCode.SettingsFolder); + + // Write out proper formatted JSON (hence no more SimpleJSON here) + string fileContent = "{\n\t\"version\": \"0.2.0\",\n\t\"configurations\": [\n\t\t{\n\t\t\t\"name\": \"Unity Editor\",\n\t\t\t\"type\": \"unity\",\n\t\t\t\"request\": \"launch\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"Windows Player\",\n\t\t\t\"type\": \"unity\",\n\t\t\t\"request\": \"launch\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"OSX Player\",\n\t\t\t\"type\": \"unity\",\n\t\t\t\"request\": \"launch\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"Linux Player\",\n\t\t\t\"type\": \"unity\",\n\t\t\t\"request\": \"launch\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"iOS Player\",\n\t\t\t\"type\": \"unity\",\n\t\t\t\"request\": \"launch\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"Android Player\",\n\t\t\t\"type\": \"unity\",\n\t\t\t\"request\": \"launch\"\n\n\t\t}\n\t]\n}"; + File.WriteAllText(VSCode.LaunchPath, fileContent); + } + else if (VSCode.WriteLaunchFile) + { + int port = GetDebugPort(); + if (port > -1) + { + if (!Directory.Exists(VSCode.SettingsFolder)) + System.IO.Directory.CreateDirectory(VSCode.SettingsFolder); + + // Write out proper formatted JSON (hence no more SimpleJSON here) + string fileContent = "{\n\t\"version\":\"0.2.0\",\n\t\"configurations\":[ \n\t\t{\n\t\t\t\"name\":\"Unity\",\n\t\t\t\"type\":\"mono\",\n\t\t\t\"request\":\"attach\",\n\t\t\t\"address\":\"localhost\",\n\t\t\t\"port\":" + port + "\n\t\t}\n\t]\n}"; + File.WriteAllText(VSCode.LaunchPath, fileContent); + + if (VSCode.Debug) + { + UnityEngine.Debug.Log("[VSCode] Debug Port Found (" + port + ")"); + } + } + else + { + if (VSCode.Debug) + { + UnityEngine.Debug.LogWarning("[VSCode] Unable to determine debug port."); + } + } + } + } + + /// + /// Update Unity Editor Preferences + /// + /// Should we turn on this party! + static void UpdateUnityPreferences(bool enabled) + { + if (enabled) + { + // App + if (EditorPrefs.GetString("kScriptsDefaultApp") != CodePath) + { + EditorPrefs.SetString("VSCode_PreviousApp", EditorPrefs.GetString("kScriptsDefaultApp")); + } + EditorPrefs.SetString("kScriptsDefaultApp", CodePath); + + // Arguments + if (EditorPrefs.GetString("kScriptEditorArgs") != "-r -g `$(File):$(Line)`") + { + EditorPrefs.SetString("VSCode_PreviousArgs", EditorPrefs.GetString("kScriptEditorArgs")); + } + + EditorPrefs.SetString("kScriptEditorArgs", "-r -g `$(File):$(Line)`"); + EditorPrefs.SetString("kScriptEditorArgs" + CodePath, "-r -g `$(File):$(Line)`"); + + + // MonoDevelop Solution + if (EditorPrefs.GetBool("kMonoDevelopSolutionProperties", false)) + { + EditorPrefs.SetBool("VSCode_PreviousMD", true); + } + EditorPrefs.SetBool("kMonoDevelopSolutionProperties", false); + + // Support Unity Proj (JS) + if (EditorPrefs.GetBool("kExternalEditorSupportsUnityProj", false)) + { + EditorPrefs.SetBool("VSCode_PreviousUnityProj", true); + } + EditorPrefs.SetBool("kExternalEditorSupportsUnityProj", false); + + if (!EditorPrefs.GetBool("AllowAttachedDebuggingOfEditor", false)) + { + EditorPrefs.SetBool("VSCode_PreviousAttach", false); + } + EditorPrefs.SetBool("AllowAttachedDebuggingOfEditor", true); + + } + else + { + // Restore previous app + if (!string.IsNullOrEmpty(EditorPrefs.GetString("VSCode_PreviousApp"))) + { + EditorPrefs.SetString("kScriptsDefaultApp", EditorPrefs.GetString("VSCode_PreviousApp")); + } + + // Restore previous args + if (!string.IsNullOrEmpty(EditorPrefs.GetString("VSCode_PreviousArgs"))) + { + EditorPrefs.SetString("kScriptEditorArgs", EditorPrefs.GetString("VSCode_PreviousArgs")); + } + + // Restore MD setting + if (EditorPrefs.GetBool("VSCode_PreviousMD", false)) + { + EditorPrefs.SetBool("kMonoDevelopSolutionProperties", true); + } + + // Restore MD setting + if (EditorPrefs.GetBool("VSCode_PreviousUnityProj", false)) + { + EditorPrefs.SetBool("kExternalEditorSupportsUnityProj", true); + } + + // Always leave editor attaching on, I know, it solves the problem of needing to restart for this + // to actually work + EditorPrefs.SetBool("AllowAttachedDebuggingOfEditor", true); + + } + + FixUnityPreferences(); + } + + /// + /// Determines if the current path to the code executable is valid or not (exists) + /// + static bool VSCodeExists(string curPath) + { + #if UNITY_EDITOR_OSX + return System.IO.Directory.Exists(curPath); + #else + System.IO.FileInfo code = new System.IO.FileInfo(curPath); + return code.Exists; + #endif + } + + /// + /// Write Default Workspace Settings + /// + static void WriteWorkspaceSettings() + { + if (Debug) + { + UnityEngine.Debug.Log("[VSCode] Workspace Settings Written"); + } + + if (!Directory.Exists(VSCode.SettingsFolder)) + { + System.IO.Directory.CreateDirectory(VSCode.SettingsFolder); + } + + string exclusions = + // Associations + "{\n" + + "\t\"files.associations\":\n" + + "\t{\n" + + "\t\t\"*.bjs\":\"javascript\",\n" + + "\t\t\"*.javascript\":\"javascript\"\n" + + "\t},\n" + + "\t\"files.exclude\":\n" + + "\t{\n" + + // Hidden Files + "\t\t\"**/.DS_Store\":true,\n" + + "\t\t\"**/.git\":true,\n" + + "\t\t\"**/.gitignore\":true,\n" + + "\t\t\"**/.gitattributes\":true,\n" + + "\t\t\"**/.gitmodules\":true,\n" + + "\t\t\"**/.svn\":true,\n" + + + // Compressed Files + "\t\t\"**/*.zip\":true,\n" + + "\t\t\"**/*.gz\":true,\n" + + "\t\t\"**/*.7z\":true,\n" + + + // Project Files + "\t\t\"**/*.booproj\":true,\n" + + "\t\t\"**/*.pidb\":true,\n" + + "\t\t\"**/*.suo\":true,\n" + + "\t\t\"**/*.user\":true,\n" + + "\t\t\"**/*.userprefs\":true,\n" + + "\t\t\"**/*.unityproj\":true,\n" + + "\t\t\"**/*.dll\":true,\n" + + "\t\t\"**/*.exe\":true,\n" + + + // Media Files + "\t\t\"**/*.pdf\":true,\n" + + + // Video + "\t\t\"**/*.mp4\":true,\n" + + + // Audio + "\t\t\"**/*.mid\":true,\n" + + "\t\t\"**/*.midi\":true,\n" + + "\t\t\"**/*.wav\":true,\n" + + "\t\t\"**/*.mp3\":true,\n" + + "\t\t\"**/*.ogg\":true,\n" + + + // Textures + "\t\t\"**/*.gif\":true,\n" + + "\t\t\"**/*.ico\":true,\n" + + "\t\t\"**/*.jpg\":true,\n" + + "\t\t\"**/*.jpeg\":true,\n" + + "\t\t\"**/*.png\":true,\n" + + "\t\t\"**/*.psd\":true,\n" + + "\t\t\"**/*.tga\":true,\n" + + "\t\t\"**/*.tif\":true,\n" + + "\t\t\"**/*.tiff\":true,\n" + + "\t\t\"**/*.hdr\":true,\n" + + "\t\t\"**/*.exr\":true,\n" + + + // Models + "\t\t\"**/*.3ds\":true,\n" + + "\t\t\"**/*.3DS\":true,\n" + + "\t\t\"**/*.fbx\":true,\n" + + "\t\t\"**/*.FBX\":true,\n" + + "\t\t\"**/*.lxo\":true,\n" + + "\t\t\"**/*.LXO\":true,\n" + + "\t\t\"**/*.ma\":true,\n" + + "\t\t\"**/*.MA\":true,\n" + + "\t\t\"**/*.obj\":true,\n" + + "\t\t\"**/*.OBJ\":true,\n" + + + // Unity File Types + "\t\t\"**/*.asset\":true,\n" + + "\t\t\"**/*.cubemap\":true,\n" + + "\t\t\"**/*.flare\":true,\n" + + "\t\t\"**/*.mat\":true,\n" + + "\t\t\"**/*.meta\":true,\n" + + "\t\t\"**/*.prefab\":true,\n" + + "\t\t\"**/*.unity\":true,\n" + + "\t\t\"**/*.anim\":true,\n" + + "\t\t\"**/*.controller\":true,\n" + + + // Folders + "\t\t\"build/\":true,\n" + + "\t\t\"Build/\":true,\n" + + "\t\t\"Library/\":true,\n" + + "\t\t\"library/\":true,\n" + + "\t\t\"obj/\":true,\n" + + "\t\t\"Obj/\":true,\n" + + "\t\t\"ProjectSettings/\":true,\r" + + "\t\t\"temp/\":true,\n" + + "\t\t\"Temp/\":true\n" + + "\t}\n" + + "}"; + + // Dont like the replace but it fixes the issue with the JSON + File.WriteAllText(VSCode.SettingsPath, exclusions); + } + + #endregion + } + + /// + /// VSCode Asset AssetPostprocessor + /// This will ensure any time that the project files are generated the VSCode versions will be made + /// + /// Undocumented Event + public class VSCodeAssetPostprocessor : AssetPostprocessor + { + /// + /// On documented, project generation event callback + /// + private static void OnGeneratedCSProjectFiles() + { + // Force execution of VSCode update + VSCode.UpdateSolution(); + } + } +} diff --git a/Assets/Plugins/Editor/VSCode.cs.meta b/Assets/Plugins/Editor/VSCode.cs.meta new file mode 100644 index 0000000..8cb2e64 --- /dev/null +++ b/Assets/Plugins/Editor/VSCode.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: aa6483bcf1ba342f5bd1c32b66e50582 +timeCreated: 1495875538 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Sample/GameManager/GameMain.cs b/Assets/Sample/GameManager/GameMain.cs index 8987ea6..3c7c5eb 100644 --- a/Assets/Sample/GameManager/GameMain.cs +++ b/Assets/Sample/GameManager/GameMain.cs @@ -9,6 +9,7 @@ public class GameMain : MonoBehaviour { void Start () { TTUIPage.ShowPage(); TTUIPage.ShowPage(); + // var root = TTUIRoot.Instance; } // Update is called once per frame diff --git a/Assets/Sample/Resources/UIPrefab/UIRoot.prefab b/Assets/Sample/Resources/UIPrefab/UIRoot.prefab index 738dd4c4b04b0d5cef211ae1ef09db6ac39d04bf..dac2559e8ccf27f616f03e6c63087f684b3c81d4 100644 GIT binary patch literal 11268 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 YT>CZIcW2+pGNUx*xbIgJK-z!)1Dwk{oB#j- literal 11244 zcmeHNe~ev46`r@-U0Nzsp(66Due3k`TS2fCg?6{y{#aJlaG&SoDxNXh(a*1dha;SdJJTv- zqaCgX&c1WZYKMO4J?!+J0-RU99Zt2F;J#kWUX^DB_#Xog?f*&Q6)T^)9QNc*{8PkR zh&Nh0pN)6epVNrjyV2YL*-f}vKfQ`)4Ee8c{L_h7_)bHL|I@@K;c1+sGPILTD!yreep>tTLfpgHJJAlu3xH^sE9S*NKO~$-J`vb@8 zhE8K9XTj27c}l2~pTXY8ha7%x6&GjS;_&k`ocZR)T5*G7eE*c37rCgY-Ek9#O;> z>kLbAVW^CdVn)oUl}o~~xGE^;`yHh%uaA1l{T<1QFzV|kU9Nw$6hW_JUltS!-9dhH zSDtUT<=P?A7WM{1g>u(m5|u*-jTVD}D4*_I21EYYdCgDXcWZ&jL0__ z-#&J4wrye`4ND9&%Fn(09hWoNFk@P*-jx{1O-_zshVm14_a*VbU|bA~bi$RvXh?5f z6ZCbJ6YTFOx98!?0~=Qd!>htlze=>1TJz;-IJA@aw=?@*`1^xx`{!?d?)25SHoeAJ zbgSz~8J$i!o5lbC65%(nIOw$MZ2sP{0Ju7vkAKPGHWBvrATDCc)7gCag$`#TEO^Ge z0sFV0%0+3N&ErwM*4aE<_0rjVe$`87^Z9eAlAq4z%PYTiwsH9BY`(nm)7g}t!H#<| zgB_hsf=D_j(Afk}2L(Et;xD^k$9~e;1eajPe$bwDHpPACq_YX0G34n-ojl@f88}?p z$+_7OCGPWNtGMI_H=|CyGcA5RPP;P3R@oVsgX%_mi`ha4CZ3B+k;Prz?$l^p(jOIi zu$QB_3>m)l^v*agcO*S1bMofT*|z?Pc-7qtHXc2^a^}|NOMu;paMFM$FFNuaQbb7x zd8`vHPm{+w(bAwCc>Rg?LSWp{*d_+dm=8M9Do_21R(U3RqE((L(A(-wobuFm%@01& ze%|$+i$jyTb+8l{Hl45DkyiubSnY|{a}YWdaXNCw!#f>LE+1dxa5^$^o(SK@LWORt z*E%xW(6R&liJiQ4WDnN`X&u?aHR-jE?BS!1$2zjbCE2bx+&Z#{-{o*RGB3_C`||^o zIbJ_GUkRK^FP5L7BV)6x%Kscy%G=}|Pvv>h;dEqPdNSUJGUYkXyl4lm+rOYpTpYQ@ z@*IHPHyv)1b1R=Q|HktabcS=A#m!3{U)Rq}iwRE0*Y$IL6_<_gT<}n@P0nqb1cbQ` zpAC!`$3;6_4IKZO3oKg07df1IeEW1do{K#B9pLG8I5#3*%0KFOE*97<2?oL>IG!Y; z5C+MzLeOVF1t4!iHMVdpb*^>_N`jT7Yd$liim!I`O!m0EYO;8q&$>_{4la6NczIE1FH45sD$3}h$HPwN2zQ@TPOZ@ za% z;DhD1?>8>CkY7%^aU3haA2or!UxKMtYv!! z$LE1wdNlniAL4vaS~`rQ1*1X-)_oH7gkA7gIaw%D_ofrDCk^`*(gBjD9K>+mS`-|f z{_ciD4?j0Ny7~4Q&s+}dcepuJUUUcMi&7J_9LpUz#m7oh7=~%y`ZQ%Ou;WQnl&2Z* zaf-OixSW({qG^iqOaV{pI6Quu!t=&_$T3ym;oi@)m!ZmWaq0W`3Wtk3WNklpz?RoJ zoVN1iu{Bv9x`XEHJa4Qkb}|^4Fg?l;5T) zp1ktgG{xc>!}G>w@PCx&4V_1(DKd|fQtN@?M{qfB47>53hbLE$hAHJ?nxeSBuCi5J z(u>#7>-l`##?9Gs>Q(IUl zGj+>F!*SWXiBP!yoN1T5zkKK0FLh44Vs7QjYdPD0#?1}q0HRJR`c{nLtR5@+;uZU| z8oiD0yPwhQDVwX2QADCIdE|6|B`~gH-Whv0TJO0QWzJGl6>qTK%-JS?Gx3V$N&QS` zqp!u~Yk>X?NG{=@WY(d)$;hrUg}9Y(GNTwTqXqkUGI4u1+fSm}|x@6>+s9g%Oc zpFR`R*-u7a;crg$lhHTR!go%x{Mt{OjIa&(4d^5LNnS=@pP$}Oen#K%_!)i2<7f00 zo-{wBukzCl-$0#XswE?7J23i+d~<5P7<~no`MMW%_LGN1!5dR{;2}})^nCG)bu z57)csmFz&*$*d|a$>48Yoza)`l}qruNH=n|s24w=xDP*d)oS#~1^58O{A}!xz5Xl6 z6_a={OvI_J`o=k3~?>84kq5A&!9V^D=Wl(JmdP2tGb7 z$5B&$MF!Abk~Y^%n-7>Xo}g9a8p1%mjvko4uBySPG(Ifypuqkl}tN+$Ne(?8+Ej5HcE zh$q)~VtZuF_MyjZ+aa{&P~!h;!dSqod&^oozy(lr9qO;yTR0DImA2c*Y1{KdY5UVS zZ7Uv;wmD5>IbZ*yw2h$cPUt#lZ_)Q+>pI()$uI#A7=GLLGV5*IUbNM}xBDDv+kik@ z-?qK)J!mrK5ZdTZ++W^ogg#DzWrR)cxqB65SiNfVR3<~xv@w3+f^)-t9pAqCH?K52 z^waO+-D6#ecyBz02@JYtM(I7t>!Iug;(9)wQK-P1mT_bzQPwT=i?)rBf$r3mV6_2m8iLeTx3V zOZ|vt>UAg16P^R|_<9c#z@_U_?VD-McuWpd+ZOTGZ#$eTH%k?w#5LHmD=0 ztB&E*ee%QN)4lb>=Btg7rP}%zT-KzI>-r!2Uj4gPg8sz2-h8&vx4*6r+fe;8E~jnP zc%|{1pvDlEbw9DV%M(~W(QBgfo#wCaKkIsnq}nsR=F&1A=Y;L4>)Cr3#fkSPJ>C=R MPg=Lsbe_roAJeHbf&c&j diff --git a/Assets/UIFramework/Source/TTUIRoot.cs b/Assets/UIFramework/Source/TTUIRoot.cs index f757a1e..bfe66ed 100644 --- a/Assets/UIFramework/Source/TTUIRoot.cs +++ b/Assets/UIFramework/Source/TTUIRoot.cs @@ -1,141 +1,139 @@ -namespace TinyTeam.UI -{ - using UnityEngine; - using System.Collections; - using UnityEngine.UI; - using UnityEngine.EventSystems; - - /// - /// Init The UI Root - /// - /// UIRoot - /// -Canvas - /// --FixedRoot - /// --NormalRoot - /// --PopupRoot - /// -Camera - /// - public class TTUIRoot : MonoBehaviour - { - private static TTUIRoot m_Instance = null; - public static TTUIRoot Instance - { - get - { - if(m_Instance == null) - { - InitRoot(); - } - return m_Instance; +/* + * @Author: chiuan wei + * @Date: 2017-05-27 18:14:53 + * @Last Modified by: chiuan wei + * @Last Modified time: 2017-05-27 18:33:48 + */ +namespace TinyTeam.UI { + using System.Collections; + using UnityEngine.EventSystems; + using UnityEngine.UI; + using UnityEngine; + + /// + /// Init The UI Root + /// + /// UIRoot + /// -Canvas + /// --FixedRoot + /// --NormalRoot + /// --PopupRoot + /// -Camera + /// + public class TTUIRoot : MonoBehaviour { + private static TTUIRoot m_Instance = null; + public static TTUIRoot Instance { + get { + if (m_Instance == null) { + InitRoot(); } - } - - public Transform root; - public Transform fixedRoot; - public Transform normalRoot; - public Transform popupRoot; - public Camera uiCamera; - - static void InitRoot() - { - GameObject go = new GameObject("UIRoot"); - go.layer = LayerMask.NameToLayer("UI"); - m_Instance = go.AddComponent(); - go.AddComponent(); - m_Instance.root = go.transform; - - Canvas can = go.AddComponent(); - can.renderMode = RenderMode.ScreenSpaceCamera; - can.pixelPerfect = true; - GameObject camObj = new GameObject("UICamera"); - camObj.layer = LayerMask.NameToLayer("UI"); - camObj.transform.parent = go.transform; - camObj.transform.localPosition = new Vector3(0,0,-100f); - Camera cam = camObj.AddComponent(); - cam.clearFlags = CameraClearFlags.Depth; - cam.orthographic = true; - cam.farClipPlane = 200f; - can.worldCamera = cam; - m_Instance.uiCamera = cam; - cam.cullingMask = 1<<5; - cam.nearClipPlane = -50f; - cam.farClipPlane = 50f; - - //add audio listener - camObj.AddComponent(); - camObj.AddComponent(); - - CanvasScaler cs = go.AddComponent(); - cs.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; - cs.referenceResolution = new Vector2(1136f, 640f); - cs.screenMatchMode = CanvasScaler.ScreenMatchMode.Expand; - - ////add auto scale camera fix size. - //TTCameraScaler tcs = go.AddComponent(); - //tcs.scaler = cs; - - //set the raycaster - //GraphicRaycaster gr = go.AddComponent(); - - GameObject subRoot = CreateSubCanvasForRoot(go.transform,250); - subRoot.name = "FixedRoot"; - m_Instance.fixedRoot = subRoot.transform; - - subRoot = CreateSubCanvasForRoot(go.transform,0); - subRoot.name = "NormalRoot"; - m_Instance.normalRoot = subRoot.transform; - - subRoot = CreateSubCanvasForRoot(go.transform,500); - subRoot.name = "PopupRoot"; - m_Instance.popupRoot = subRoot.transform; - - //add Event System - GameObject esObj = GameObject.Find("EventSystem"); - if(esObj != null) - { - GameObject.DestroyImmediate(esObj); - } - - GameObject eventObj = new GameObject("EventSystem"); - eventObj.layer = LayerMask.NameToLayer("UI"); - eventObj.transform.SetParent(go.transform); - eventObj.AddComponent(); - if (!Application.isMobilePlatform || Application.isEditor) - { - eventObj.AddComponent(); - } - else - { - eventObj.AddComponent(); - } - - - } - - static GameObject CreateSubCanvasForRoot(Transform root,int sort) - { - GameObject go = new GameObject("canvas"); - go.transform.parent = root; - go.layer = LayerMask.NameToLayer("UI"); - - Canvas can = go.AddComponent(); - RectTransform rect = go.GetComponent(); - rect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, 0, 0); - rect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, 0, 0); - rect.anchorMin = Vector2.zero; - rect.anchorMax = Vector2.one; - - can.overrideSorting = true; - can.sortingOrder = sort; - - go.AddComponent(); - - return go; - } - - void OnDestroy() - { - m_Instance = null; - } - } + return m_Instance; + } + } + + public Transform root; + public Transform fixedRoot; + public Transform normalRoot; + public Transform popupRoot; + public Camera uiCamera; + + static void InitRoot() { + GameObject go = new GameObject("UIRoot"); + go.layer = LayerMask.NameToLayer("UI"); + m_Instance = go.AddComponent(); + go.AddComponent(); + + Canvas can = go.AddComponent(); + can.renderMode = RenderMode.ScreenSpaceCamera; + can.pixelPerfect = true; + + go.AddComponent(); + + m_Instance.root = go.transform; + + GameObject camObj = new GameObject("UICamera"); + camObj.layer = LayerMask.NameToLayer("UI"); + camObj.transform.parent = go.transform; + camObj.transform.localPosition = new Vector3(0, 0, -100f); + Camera cam = camObj.AddComponent(); + cam.clearFlags = CameraClearFlags.Depth; + cam.orthographic = true; + cam.farClipPlane = 200f; + can.worldCamera = cam; + cam.cullingMask = 1 << 5; + cam.nearClipPlane = -50f; + cam.farClipPlane = 50f; + + m_Instance.uiCamera = cam; + + //add audio listener + camObj.AddComponent(); + camObj.AddComponent(); + + CanvasScaler cs = go.AddComponent(); + cs.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; + cs.referenceResolution = new Vector2(1136f, 640f); + cs.screenMatchMode = CanvasScaler.ScreenMatchMode.Expand; + + ////add auto scale camera fix size. + //TTCameraScaler tcs = go.AddComponent(); + //tcs.scaler = cs; + + //set the raycaster + //GraphicRaycaster gr = go.AddComponent(); + + GameObject subRoot; + + subRoot = CreateSubCanvasForRoot(go.transform, 0); + subRoot.name = "NormalRoot"; + m_Instance.normalRoot = subRoot.transform; + m_Instance.normalRoot.transform.localScale = Vector3.one; + + subRoot = CreateSubCanvasForRoot(go.transform, 250); + subRoot.name = "FixedRoot"; + m_Instance.fixedRoot = subRoot.transform; + m_Instance.fixedRoot.transform.localScale = Vector3.one; + + subRoot = CreateSubCanvasForRoot(go.transform, 500); + subRoot.name = "PopupRoot"; + m_Instance.popupRoot = subRoot.transform; + m_Instance.popupRoot.transform.localScale = Vector3.one; + + //add Event System + GameObject esObj = GameObject.Find("EventSystem"); + if (esObj != null) { + GameObject.DestroyImmediate(esObj); + } + + GameObject eventObj = new GameObject("EventSystem"); + eventObj.layer = LayerMask.NameToLayer("UI"); + eventObj.transform.SetParent(go.transform); + eventObj.AddComponent(); + eventObj.AddComponent(); + + } + + static GameObject CreateSubCanvasForRoot(Transform root, int sort) { + GameObject go = new GameObject("canvas"); + go.transform.parent = root; + go.layer = LayerMask.NameToLayer("UI"); + + RectTransform rect = go.AddComponent(); + rect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, 0, 0); + rect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, 0, 0); + rect.anchorMin = Vector2.zero; + rect.anchorMax = Vector2.one; + + // Canvas can = go.AddComponent(); + // can.overrideSorting = true; + // can.sortingOrder = sort; + // go.AddComponent(); + + return go; + } + + void OnDestroy() { + m_Instance = null; + } + } } \ No newline at end of file diff --git a/Assets/test.unity b/Assets/test.unity index 786a66df40c3337445de4532f57ef74d6d29c750..8b20f6df4a0ad4481fd80c5b79672ac6f93d7930 100644 GIT binary patch literal 18984 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 KU?zPG!T$vrEX5T7 literal 19820 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 diff --git a/ProjectSettings/ClusterInputManager.asset b/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 0000000000000000000000000000000000000000..3de5a6d933f8d0e55b0c519e81ff666aad7df03f GIT binary patch literal 4104 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 literal 39985 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 Z7fl<-U=gVUIPAc|F>RdV+@F@{{{lmRKhOXG literal 0 HcmV?d00001