随记
using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; public class PackEditor : EditorWindow { [MenuItem("Window/发布 _F4")] static void PackagerWindow() { luaFilesPath = Application.dataPath + "/GameLua"; luaZipPath = Application.streamingAssetsPath + "/lua.zip"; PackEditor packagerEditor = (PackEditor)EditorWindow.GetWindow(typeof(PackEditor), false, "发布", true); packagerEditor.minSize = new Vector2(300, 220); packagerEditor.maxSize = new Vector2(300, 220); packagerEditor.Show(); } public void OnGUI() { buildTarget = (BuildTarget)EditorGUI.EnumPopup(new Rect(80, 10, 150, 20), buildTarget); if (GUI.Button(new Rect(80, 50, 150, 20), "设置保存位置")) { fabuPath = EditorUtility.SaveFilePanel("文件保存路径", "c", Application.productName, JudgePlatform()); } if (GUI.Button(new Rect(80, 90, 150, 20), "开始打包")) { MyBuild(); } GUI.TextArea(new Rect(10, 120, 280, 90), "当前保存位置:" + fabuPath); } string JudgePlatform() { if (buildTarget == BuildTarget.Android) return "apk"; else if (buildTarget == BuildTarget.StandaloneWindows64) return "exe"; else if (buildTarget == BuildTarget.WebGL) return "htm"; return "apk"; } static string luaFilesPath = string.Empty; static string luaZipPath = string.Empty; static BuildTarget buildTarget = BuildTarget.Android; static string fabuPath = string.Empty; static void MyBuild() { if (!string.IsNullOrEmpty(fabuPath)) { EditorBuildSettingsScene[] sceneArray = EditorBuildSettings.scenes; if (sceneArray != null && sceneArray.Length > 0) { List<string> outScenes = new List<string>(); for (int i = 0; i < sceneArray.Length; i++) { if (sceneArray[i].enabled) outScenes.Add(sceneArray[i].path); } if (outScenes.Count > 0) { GenerateZip(luaFilesPath); AssetDatabase.Refresh(); BuildPipeline.BuildPlayer(outScenes.ToArray(), fabuPath, buildTarget, BuildOptions.None); AssetDatabase.Refresh(); System.Diagnostics.Process.Start(Path.GetDirectoryName(fabuPath)); } } } } static void GenerateZip(string _orignalFloder) { allFiles = new List<string>(); GetAllFiles(_orignalFloder); if (File.Exists(luaZipPath)) File.Delete(luaZipPath); AssetDatabase.Refresh(); CSharpUtils.CompressFiles(luaZipPath, allFiles, _orignalFloder.LastIndexOf('/') + 1, ""); } static List<string> allFiles = null; public static void GetAllFiles(string path) { string[] dir = Directory.GetDirectories(path); DirectoryInfo fdir = new DirectoryInfo(path); FileInfo[] file = fdir.GetFiles(); if (file.Length != 0 || dir.Length != 0) { foreach (FileInfo f in file) { if (!f.FullName.Contains(".meta")) allFiles.Add(f.FullName); } foreach (string d in dir) { GetAllFiles(d);//递归 } } } }
using System.Collections; using System.Collections.Generic; using UnityEditor; using UnityEngine; public class LayerTagSpawn : AssetPostprocessor { static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { foreach (string s in importedAssets) { if (s.Contains("LayerTagSpawn")) { AddLayer("dragLayer"); AddLayer("terrain"); return; } } } static void AddTag(string tag) { if (!isHasTag(tag)) { SerializedObject tagManager = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0]); SerializedProperty it = tagManager.GetIterator(); while (it.NextVisible(true)) { if (it.name == "tags") { for (int i = 0; i < it.arraySize; i++) { SerializedProperty dataPoint = it.GetArrayElementAtIndex(i); if (string.IsNullOrEmpty(dataPoint.stringValue)) { dataPoint.stringValue = tag; tagManager.ApplyModifiedProperties(); return; } } } } } } static void AddLayer(string layer) { if (!isHasLayer(layer)) { SerializedObject tagManager = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0]); SerializedProperty it = tagManager.GetIterator(); while (it.NextVisible(true)) { if (it.name.StartsWith("User Layer")) { if (it.type == "string") { if (string.IsNullOrEmpty(it.stringValue)) { it.stringValue = layer; tagManager.ApplyModifiedProperties(); return; } } } } } } static bool isHasTag(string tag) { for (int i = 0; i < UnityEditorInternal.InternalEditorUtility.tags.Length; i++) { if (UnityEditorInternal.InternalEditorUtility.tags[i].Contains(tag)) return true; } return false; } static bool isHasLayer(string layer) { for (int i = 0; i < UnityEditorInternal.InternalEditorUtility.layers.Length; i++) { if (UnityEditorInternal.InternalEditorUtility.layers[i].Contains(layer)) return true; } return false; } }
using System.IO; using UnityEditor; using UnityEngine; using UnityEngine.SceneManagement; public class DecoratorEditors : EditorWindow { [MenuItem("Window/打包场景 _F3")] static void PackagerWindow() { DecoratorEditors packagerEditor = (DecoratorEditors)EditorWindow.GetWindow(typeof(DecoratorEditors), false, "打包", true); packagerEditor.minSize = new Vector2(300, 220); packagerEditor.maxSize = new Vector2(300, 220); packagerEditor.Show(); } public void OnGUI() { buildTarget = (BuildTarget)EditorGUI.EnumPopup(new Rect(80, 50, 150, 20), buildTarget); if (GUI.Button(new Rect(80, 80, 150, 20), "生成场景AB")) { MyBuild(); } } static BuildTarget buildTarget = BuildTarget.Android; static void MyBuild() { string dir = Application.streamingAssetsPath + "/"; if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } string activeSceneName = SceneManager.GetActiveScene().name; string saveSceneABPath = dir + activeSceneName + "-" + buildTarget +".unity3d"; string[] levels = { SceneManager.GetActiveScene().path }; //生成场景的AB包 BuildPipeline.BuildPlayer(levels, saveSceneABPath, buildTarget, BuildOptions.BuildAdditionalStreamedScenes); AssetDatabase.Refresh(); System.Diagnostics.Process.Start(dir); } }
using System; using System.IO; using UnityEngine; using XLua; public class LuaEnvGlobal : MonoBehaviour { public static LuaEnvGlobal instance; LuaEnv luaEnv; void Awake() { instance = this; luaEnv = new LuaEnv(); luaEnv.AddLoader((ref string filename) => { string luaPath = AppConfig.SearchGameLuaPath + "/" + filename + ".lua"; if (!File.Exists(luaPath)) return null; string script = File.ReadAllText(luaPath); return System.Text.Encoding.UTF8.GetBytes(script); }); DontDestroyOnLoad(gameObject); } void Start() { } private Action luaUpdate; private Action luaOnDestroy; public void StartUp() { print("启动Lua"); luaEnv.DoString("require 'GameLua/Others/Main'"); luaEnv.Global.Get("GlobalUpdate", out luaUpdate); luaEnv.Global.Get("GlobalOnDestory", out luaOnDestroy); } public void DoString(string luaStr) { luaEnv.DoString(luaStr); } public T GlobalGet<T>(string key) { T result; luaEnv.Global.Get(key, out result); return result; } void Update() { if (luaUpdate != null) luaUpdate(); if (luaEnv != null) luaEnv.Tick(); } void OnDestroy() { if (luaOnDestroy != null) luaOnDestroy(); Clear(); if (luaEnv != null) luaEnv.Dispose(); } void Clear() { luaUpdate = null; luaOnDestroy = null; } }
/* * Tencent is pleased to support the open source community by making xLua available. * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at * http://opensource.org/licenses/MIT * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ using System.Collections.Generic; using System; using XLua; using System.Reflection; using System.Linq; using UnityEngine; //配置的详细介绍请看Doc下《XLua的配置.doc》 public static class ExampleConfig { /***************如果你全lua编程,可以参考这份自动化配置***************/ //--------------begin 纯lua编程配置参考---------------------------- static List<string> exclude = new List<string> { "HideInInspector", "ExecuteInEditMode", "AddComponentMenu", "ContextMenu", "RequireComponent", "DisallowMultipleComponent", "SerializeField", "AssemblyIsEditorAssembly", "Attribute", "Types", "UnitySurrogateSelector", "TrackedReference", "TypeInferenceRules", "FFTWindow", "RPC", "Network", "MasterServer", "BitStream", "HostData", "ConnectionTesterStatus", "GUI", "EventType", "EventModifiers", "FontStyle", "TextAlignment", "TextEditor", "TextEditorDblClickSnapping", "TextGenerator", "TextClipping", "Gizmos", "ADBannerView", "ADInterstitialAd", "Android", "Tizen", "jvalue", "iPhone", "iOS", "Windows", "CalendarIdentifier", "CalendarUnit", "CalendarUnit", "ClusterInput", "FullScreenMovieControlMode", "FullScreenMovieScalingMode", "Handheld", "LocalNotification", "NotificationServices", "RemoteNotificationType", "RemoteNotification", "SamsungTV", "TextureCompressionQuality", "TouchScreenKeyboardType", "TouchScreenKeyboard", "MovieTexture", "UnityEngineInternal", "Terrain", "Tree", "SplatPrototype", "DetailPrototype", "DetailRenderMode", "MeshSubsetCombineUtility", "AOT", "Social", "Enumerator", "SendMouseEvents", "Cursor", "Flash", "ActionScript", "OnRequestRebuild", "Ping", "ShaderVariantCollection", "SimpleJson.Reflection", "CoroutineTween", "GraphicRebuildTracker", "Advertisements", "UnityEditor", "WSA", "EventProvider", "Apple", "ClusterInput", "Motion", "UnityEngine.UI.ReflectionMethodsCache", "NativeLeakDetection", "NativeLeakDetectionMode", "WWWAudioExtensions", "UnityEngine.Experimental", "UnityEngine.Purchasing","AnimatorControllerParameter","UnityEngine.Tilemaps", "UnityEngine.Analytics","UnityEngine.Caching", "UnityEngine.EventSystems.HoloLensInput","UnityEngine.NScreenBridge", "UnityEngine.TestTools.LogAssert","UnityEngine.Microphone", "UnityEngine.CanvasRenderer","UnityEngine.Audio.AudioSpatializerMicrosoft", "UnityEngine.ProceduralMaterial","UnityEngine.ProceduralPropertyDescription", "UnityEngine.ProceduralTexture","UnityEngine.SpatialTracking", "FFmpeg.AutoGen","UnityEngine.PostProcessing","EntriesDrawer","SerializedProperty", "AQUAS_Screenshotter","TiltInputAxisStylePropertyDrawer","ReplacementListDrawer","WaypointListDrawer","ReplacementListDrawer","TransformNameComparer", "BatchMaterialConverter","BatchModelImporter","BatchTextureImporter","EditorHotkeysTracker","EditorPrefsX","LB_LightingSettingsHepler","HighlightingSystem.Demo", "FrameWork.Meta","FrameWork.vComment" }; static bool isExcluded(Type type) { var fullName = type.FullName; for (int i = 0; i < exclude.Count; i++) { if (fullName.Contains(exclude[i])) { return true; } } return false; } [LuaCallCSharp] public static IEnumerable<Type> LuaCallCSharp { get { var unityTypes = (from assembly in AppDomain.CurrentDomain.GetAssemblies() where !(assembly.ManifestModule is System.Reflection.Emit.ModuleBuilder) from type in assembly.GetExportedTypes() where type.Namespace != null && type.Namespace.StartsWith("UnityEngine") && !isExcluded(type) && type.BaseType != typeof(MulticastDelegate) && !type.IsInterface && !type.IsEnum select type); string[] customAssemblys = new string[] { "Assembly-CSharp", }; var customTypes = (from assembly in customAssemblys.Select(s => Assembly.Load(s)) from type in assembly.GetExportedTypes() where (type.Namespace == null || !type.Namespace.StartsWith("XLua")) && !isExcluded(type) && type.BaseType != typeof(MulticastDelegate) && !type.IsInterface && !type.IsEnum select type); return unityTypes.Concat(customTypes); } } //自动把LuaCallCSharp涉及到的delegate加到CSharpCallLua列表,后续可以直接用lua函数做callback [CSharpCallLua] public static List<Type> CSharpCallLua { get { var lua_call_csharp = LuaCallCSharp; var delegate_types = new List<Type>(); var flag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly; foreach (var field in (from type in lua_call_csharp select type).SelectMany(type => type.GetFields(flag))) { if (typeof(Delegate).IsAssignableFrom(field.FieldType)) { if (!isExcluded(field.FieldType)) delegate_types.Add(field.FieldType); } } foreach (var method in (from type in lua_call_csharp select type).SelectMany(type => type.GetMethods(flag))) { if (typeof(Delegate).IsAssignableFrom(method.ReturnType)) { if (!isExcluded(method.ReturnType)) delegate_types.Add(method.ReturnType); } foreach (var param in method.GetParameters()) { var paramType = param.ParameterType.IsByRef ? param.ParameterType.GetElementType() : param.ParameterType; if (typeof(Delegate).IsAssignableFrom(paramType)) { if (!isExcluded(paramType)) delegate_types.Add(paramType); } } } return delegate_types.Distinct().ToList(); } } //--------------end 纯lua编程配置参考---------------------------- /***************热补丁可以参考这份自动化配置***************/ [Hotfix] static IEnumerable<Type> HotfixInject { get { List<Type> ll = (from type in Assembly.Load("Assembly-CSharp").GetExportedTypes() where (type.Namespace == null || !type.Namespace.StartsWith("XLua")) && !isExcluded(type) select type).ToList(); return (from type in Assembly.Load("Assembly-CSharp").GetExportedTypes() where (type.Namespace == null || !type.Namespace.StartsWith("XLua")) && !isExcluded(type) select type); } } //--------------begin 热补丁自动化配置------------------------- static bool hasGenericParameter(Type type) { if (type.IsGenericTypeDefinition) return true; if (type.IsGenericParameter) return true; if (type.IsByRef || type.IsArray) { return hasGenericParameter(type.GetElementType()); } if (type.IsGenericType) { foreach (var typeArg in type.GetGenericArguments()) { if (hasGenericParameter(typeArg)) { return true; } } } return false; } //// 配置某Assembly下所有涉及到的delegate到CSharpCallLua下,Hotfix下拿不准那些delegate需要适配到lua function可以这么配置 //[CSharpCallLua] //static IEnumerable<Type> AllDelegate //{ // get // { // BindingFlags flag = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; // List<Type> allTypes = new List<Type>(); // var allAssemblys = new Assembly[] // { // Assembly.Load("Assembly-CSharp") // }; // foreach (var t in (from assembly in allAssemblys from type in assembly.GetTypes() select type)) // { // var p = t; // while (p != null) // { // allTypes.Add(p); // p = p.BaseType; // } // } // allTypes = allTypes.Distinct().ToList(); // var allMethods = from type in allTypes // from method in type.GetMethods(flag) // select method; // var returnTypes = from method in allMethods // select method.ReturnType; // var paramTypes = allMethods.SelectMany(m => m.GetParameters()).Select(pinfo => pinfo.ParameterType.IsByRef ? pinfo.ParameterType.GetElementType() : pinfo.ParameterType); // var fieldTypes = from type in allTypes // from field in type.GetFields(flag) // select field.FieldType; // return (returnTypes.Concat(paramTypes).Concat(fieldTypes)).Where(t => t.BaseType == typeof(MulticastDelegate) && !hasGenericParameter(t)).Distinct(); // } //} //--------------end 热补丁自动化配置------------------------- //黑名单 [BlackList] public static List<List<string>> BlackList = new List<List<string>>() { new List<string>(){"System.Xml.XmlNodeList", "ItemOf"}, new List<string>(){"UnityEngine.WWW", "movie"}, #if UNITY_WEBGL new List<string>(){"UnityEngine.WWW", "threadPriority"}, #endif new List<string>(){"UnityEngine.Texture2D", "alphaIsTransparency"}, new List<string>(){"UnityEngine.Security", "GetChainOfTrustValue"}, new List<string>(){"UnityEngine.CanvasRenderer", "onRequestRebuild"}, new List<string>(){"UnityEngine.Light", "areaSize"}, new List<string>(){"UnityEngine.Light", "lightmapBakeType"}, new List<string>(){"UnityEngine.WWW", "MovieTexture"}, new List<string>(){"UnityEngine.WWW", "GetMovieTexture"}, new List<string>(){"UnityEngine.AnimatorOverrideController", "PerformOverrideClipListCleanup"}, #if !UNITY_WEBPLAYER new List<string>(){"UnityEngine.Application", "ExternalEval"}, #endif new List<string>(){"UnityEngine.GameObject", "networkView"}, //4.6.2 not support new List<string>(){"UnityEngine.Component", "networkView"}, //4.6.2 not support new List<string>(){"System.IO.FileInfo", "GetAccessControl", "System.Security.AccessControl.AccessControlSections"}, new List<string>(){"System.IO.FileInfo", "SetAccessControl", "System.Security.AccessControl.FileSecurity"}, new List<string>(){"System.IO.DirectoryInfo", "GetAccessControl", "System.Security.AccessControl.AccessControlSections"}, new List<string>(){"System.IO.DirectoryInfo", "SetAccessControl", "System.Security.AccessControl.DirectorySecurity"}, new List<string>(){"System.IO.DirectoryInfo", "CreateSubdirectory", "System.String", "System.Security.AccessControl.DirectorySecurity"}, new List<string>(){"System.IO.DirectoryInfo", "Create", "System.Security.AccessControl.DirectorySecurity"}, new List<string>(){"UnityEngine.MonoBehaviour", "runInEditMode"}, new List<string>(){"UnityEngine.UI.Text", "OnRebuildRequested"}, new List<string>(){ "UnityEngine.Input", "IsJoystickPreconfigured","System.String"}, new List<string>(){ "UnityEngine.AudioSettings", "GetSpatializerPluginNames"}, new List<string>(){ "UnityEngine.AudioSettings", "SetSpatializerPluginName","System.String"}, new List<string>(){ "UnityEngine.UI.Graphic", "OnRebuildRequested"}, new List<string>(){ "MediaPlayerCtrl", "Interrupt1"}, new List<string>(){ "MediaPlayerCtrl", "MediaPlayerCtrl"}, new List<string>(){ "MediaPlayerCtrl", "Call_SetUnityActivity"}, }; } /// <summary> /// xlua自定义导出 /// </summary> public static class XLuaCustomExport { /// <summary> /// dotween的扩展方法在lua中调用 /// </summary> [LuaCallCSharp] [ReflectionUse] public static List<Type> dotween_lua_call_cs_list = new List<Type>() { typeof(DG.Tweening.AutoPlay), typeof(DG.Tweening.AxisConstraint), typeof(DG.Tweening.Ease), typeof(DG.Tweening.LogBehaviour), typeof(DG.Tweening.LoopType), typeof(DG.Tweening.PathMode), typeof(DG.Tweening.PathType), typeof(DG.Tweening.RotateMode), typeof(DG.Tweening.ScrambleMode), typeof(DG.Tweening.TweenType), typeof(DG.Tweening.UpdateType), typeof(DG.Tweening.Ease), typeof(DG.Tweening.DOTween), typeof(DG.Tweening.DOVirtual), typeof(DG.Tweening.EaseFactory), typeof(DG.Tweening.Tweener), typeof(DG.Tweening.Tween), typeof(DG.Tweening.Sequence), typeof(DG.Tweening.TweenParams), typeof(DG.Tweening.Core.ABSSequentiable), typeof(DG.Tweening.Core.TweenerCore<Vector3, Vector3, DG.Tweening.Plugins.Options.VectorOptions>), typeof(DG.Tweening.TweenCallback), typeof(DG.Tweening.TweenExtensions), typeof(DG.Tweening.TweenSettingsExtensions), typeof(DG.Tweening.ShortcutExtensions), typeof(DG.Tweening.ShortcutExtensions43), typeof(DG.Tweening.ShortcutExtensions46), typeof(DG.Tweening.ShortcutExtensions50), //dotween pro 的功能 //typeof(DG.Tweening.DOTweenPath), //typeof(DG.Tweening.DOTweenVisualManager), }; }
using System; using System.Collections; using System.IO; using UnityEngine; public class ExtractZipHelper : MonoBehaviour { public static ExtractZipHelper instance; void Awake() { instance = this; zipExtractFloder = Application.persistentDataPath + "/"; } public void StartExtract(string zipPath) { print("执行解压压缩包任务:" + zipPath); StartCoroutine(Extract(zipPath)); } public Action onSuccess; public Action<string> onError; string zipExtractFloder = string.Empty; IEnumerator Extract(string zipfile) { using (WWW www = new WWW(zipfile)) { yield return www; if (www.error != null) { print("加载压缩包出错:" + www.error); if (onError != null) onError(www.error); } else { print("加载压缩包成功:" + zipfile); string zipExtractPath = zipExtractFloder + Path.GetFileName(zipfile); using (FileStream fs = File.Create(zipExtractPath)) { fs.Write(www.bytes, 0, www.bytes.Length); } ExtractToPersi(zipExtractPath); } } } void ExtractToPersi(string filePath) { print("开始解压压缩包" + filePath); byte[] bytes = File.ReadAllBytes(filePath); try { CSharpUtils.UncompressMemory(zipExtractFloder, bytes); if (onSuccess != null) onSuccess(); } catch (Exception ex) { print("解压出错:" + ex.Message); if (onError != null) onError(ex.Message); } } }
只有在编辑器模式下用的是非PC平台的AB包才会出现shader丢失的问题,解决方案如下,但是最好的解决方案是打个PC端的AB包给程序开发时用,然后在对应发布其他端的AB包
1、内置的shader通过Shader.Find去获取,然后代码重新设置下
2、自定义shader通过ab加载去获取,然后代码重新设置下
3、在Lighting面板中skybox对应的材质shader,要在Project Settings下Graphics的面板中Always Included Shaders里面包含进去,这个仅仅是为了修复编辑器模式下天空盒材质shader丢失的问题而产生的解决方案,但是如果发布到android版本是不能设置这个的,不然安卓平台会产生天空盒shader丢失的问题,为了同时满足两者的需求,我们通过代码设置天空盒材质的shader,已在ABFixShader.cs中添加进去了
4、如果打包的是PC平台的场景ab包,切记把ProjectSettings -> Other Settings里的PC平台的Auto Graphics API for ***全部勾选上,不然材质会丢失
using UnityEngine; using System.Collections; using System.Collections.Generic; using System; using System.Threading; using System.Linq; public class Loom : MonoBehaviour { public static int maxThreads = 8; static int numThreads; private static Loom _current; public static Loom Current { get { Initialize(); return _current; } } //####去除Awake // void Awake() // { // _current = this; // initialized = true; // } static bool initialized; //####作为初始化方法自己调用,可在初始化场景调用一次即可 public static void Initialize() { if (!initialized) { if (!Application.isPlaying) return; initialized = true; GameObject g = new GameObject("Loom"); //####永不销毁 DontDestroyOnLoad(g); _current = g.AddComponent<Loom>(); } } private List<Action> _actions = new List<Action>(); public struct DelayedQueueItem { public float time; public Action action; } private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>(); List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>(); public static void QueueOnMainThread(Action action) { QueueOnMainThread(action, 0f); } public static void QueueOnMainThread(Action action, float time) { if (time != 0) { if (Current != null) { lock (Current._delayed) { Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = action }); } } } else { if (Current != null) { lock (Current._actions) { Current._actions.Add(action); } } } } public static Thread RunAsync(Action a) { Initialize(); while (numThreads >= maxThreads) { Thread.Sleep(1); } Interlocked.Increment(ref numThreads); ThreadPool.QueueUserWorkItem(RunAction, a); return null; } private static void RunAction(object action) { try { ((Action)action)(); } catch { } finally { Interlocked.Decrement(ref numThreads); } } void OnDisable() { if (_current == this) { _current = null; } } // Use this for initialization void Start() { } List<Action> _currentActions = new List<Action>(); // Update is called once per frame void Update() { lock (_actions) { _currentActions.Clear(); _currentActions.AddRange(_actions); _actions.Clear(); } foreach (var a in _currentActions) { a(); } lock (_delayed) { _currentDelayed.Clear(); _currentDelayed.AddRange(_delayed.Where(d => d.time <= Time.time)); foreach (var item in _currentDelayed) _delayed.Remove(item); } foreach (var delayed in _currentDelayed) { delayed.action(); } } }
using System.Collections; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; using UnityEngine.SceneManagement; public class DecoratorEditors : EditorWindow { [MenuItem("Window/打包 _F2")] static void PackagerWindow() { DecoratorEditors packagerEditor = (DecoratorEditors)EditorWindow.GetWindow(typeof(DecoratorEditors), false, "打包", true); packagerEditor.minSize = new Vector2(300, 220); packagerEditor.maxSize = new Vector2(300, 220); packagerEditor.Show(); } public void OnGUI() { buildTarget = (BuildTarget)EditorGUI.EnumPopup(new Rect(80, 50, 150, 20), buildTarget); if (GUI.Button(new Rect(80, 80, 150, 20), "生成场景AB")) { MyBuild(); } } static string sceneABPath = string.Empty; static string shaderABSavePath = string.Empty; static BuildTarget buildTarget = BuildTarget.Android; static void MyBuild() { string saveABPath = Application.dataPath.Replace("Assets", "AB"); if (!Directory.Exists(saveABPath)) Directory.CreateDirectory(saveABPath); string activeSceneName = SceneManager.GetActiveScene().name; string saveSceneABPath = saveABPath + "/" + activeSceneName + ".unity3d"; string[] levels = { SceneManager.GetActiveScene().path }; //生成场景的AB包 BuildPipeline.BuildPlayer(levels, saveSceneABPath, buildTarget, BuildOptions.BuildAdditionalStreamedScenes); ////生成场景对应的Shader的AB包 //string shaderABName = "shader.unity3d"; //Shader[] shaders = Resources.FindObjectsOfTypeAll<Shader>(); //AssetBundleBuild[] abArrary = new AssetBundleBuild[shaders.Length]; //for (int i = 0; i < shaders.Length; i++) //{ // AssetBundleBuild ab = new AssetBundleBuild(); // string assetPath = AssetDatabase.GetAssetPath(shaders[i]); // ab.assetBundleName = shaderABName; // ab.assetNames = new string[] { assetPath }; // ab.assetBundleVariant = ""; // string shaderName = shaders[i].name; // if (shaderName.Contains("/")) // shaderName = shaderName.Split('/')[1]; // ab.addressableNames = new string[] { shaderName }; // abArrary[i] = ab; //} //BuildPipeline.BuildAssetBundles(saveABPath, abArrary, BuildAssetBundleOptions.UncompressedAssetBundle, buildTarget); //避免让美术设置手动ab,所以不采取下面这种方式 //BuildPipeline.BuildAssetBundles(shaderABSavePath, BuildAssetBundleOptions.UncompressedAssetBundle, buildTarget); AssetDatabase.Refresh(); System.Diagnostics.Process.Start(saveABPath); } }
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; public class ABFixShader : MonoBehaviour { #if UNITY_EDITOR string shaderABPath = string.Empty; void Awake() { string activeSceneName = SceneManager.GetActiveScene().name; shaderABPath = Application.streamingAssetsPath + "/" + activeSceneName +"-shader.unity3d"; StartCoroutine(Download()); } IEnumerator Download() { using (WWW www = new WWW(shaderABPath)) { yield return www; if (www.error != null) { Debug.Log("shader下载失败:" + www.error); } else { Debug.Log("开始修复shader丢失问题"); FixShader(www.assetBundle); } } } Dictionary<string, Shader> shaderDic = null; void FixShader(AssetBundle shaderBundle) { List<GameObject> rendererList = new List<GameObject>(); GetAllChildren(transform, rendererList); if (rendererList != null && rendererList.Count > 0) { shaderDic = new Dictionary<string, Shader>(); rendererList.ForEach((go) => { ResetShader(go, shaderBundle); }); } //代码修复天空盒材质shader丢失的问题 ChangeMaterialShader(RenderSettings.skybox, shaderBundle); } void GetAllChildren(Transform root, List<GameObject> rendererList) { foreach (Transform item in root) { if (item.GetComponent<Renderer>() != null) { rendererList.Add(item.gameObject); } else if (item.GetComponent<Skybox>() != null) { rendererList.Add(item.gameObject); } GetAllChildren(item, rendererList); } } void ResetShader(GameObject target, AssetBundle shaderBundle) { Material[] materials = null; Renderer renderer = target.GetComponent<Renderer>(); if (renderer != null) { materials = renderer.materials; ParticleSystemRenderer psr = GetComponent<ParticleSystemRenderer>(); if (psr != null) { materials[materials.Length] = psr.trailMaterial; } } else { Skybox skybox = target.GetComponent<Skybox>(); if (skybox != null) { materials = new Material[] { skybox.material }; } } if (materials != null && materials.Length > 0) { for (int i = 0; i < materials.Length; i++) { ChangeMaterialShader(materials[i], shaderBundle); } } } void ChangeMaterialShader(Material material, AssetBundle shaderBundle) { if (material != null && shaderBundle != null) { string shaderNode = string.Empty; string shaderName = string.Empty; shaderNode = material.shader.name; shaderName = shaderNode; if (shaderNode.Contains("/")) shaderName = shaderNode.Split('/')[1]; Shader resultShader = null; if (shaderDic.ContainsKey(shaderName)) resultShader = shaderDic[shaderName]; else { resultShader = shaderBundle.LoadAsset<Shader>(shaderName); if (resultShader == null) { if (shaderDic.ContainsKey(shaderNode)) resultShader = shaderDic[shaderNode]; else { resultShader = Shader.Find(shaderNode); if (resultShader != null) shaderDic[shaderNode] = resultShader; } } else { shaderDic[shaderName] = resultShader; } } material.shader = resultShader; } } #endif }
using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using UnityEngine; using UnityEngine.UI; public class DownloadManager : MonoBehaviour { public static DownloadManager instance; void Awake() { instance = this; } #region Properties /// <summary> /// 正在执行和正在等待的任务数量总和 /// </summary> public int WaitingAndRunningCount { get { return mWaiting.Count + mRunning.Count; } } /// <summary> /// 最多可同时执行多少个任务 /// </summary> public int MaxTaskCountInSameTime { get; private set; } /// <summary> /// 当前正在执行的任务数量 /// </summary> public int RunningCount { get { return mRunning.Count; } } /// <summary> /// 已经完成的任务数量 /// </summary> public int FinishedCount { get { return mFinished.Count; } } /// <summary> /// 任务是否成功完成,并且不存在失败的任务 /// </summary> public bool IsCompleted { get { return WaitingAndRunningCount == 0; } } /// <summary> /// 是否正在下载 /// </summary> public bool IsDownloading { get; private set; } /// <summary> /// 任务执行完时的回调 /// </summary> public event Action WorkDone; #endregion #region Fields List<DownloadSocketFileHelper> mWaiting = new List<DownloadSocketFileHelper>(); List<DownloadSocketFileHelper> mRunning = new List<DownloadSocketFileHelper>(); List<DownloadSocketFileHelper> mFinished = new List<DownloadSocketFileHelper>(); #endregion void Update() { if (!IsDownloading) { return; } if (mRunning.Count > 0) { for (int i = mRunning.Count - 1; i >= 0; i--) { if (mRunning[i].IsSuccess) { mFinished.Add(mRunning[i]); mRunning.RemoveAt(i); } else if (mRunning[i].IsFailed) { mWaiting.Add(mRunning[i]); mRunning.RemoveAt(i); } } } if (mWaiting.Count > 0) { if (mRunning.Count < MaxTaskCountInSameTime) { DownloadSocketFileHelper urlData = FindEnabledTask(); if (urlData != null) { urlData.Start(); mRunning.Add(urlData); } } } if (mRunning.Count == 0) { IsDownloading = false; if (WorkDone != null) { WorkDone(); } } } private DownloadSocketFileHelper FindEnabledTask() { DownloadSocketFileHelper urlData = null; foreach (var item in mWaiting) { if (item.TryTimes < AppConfig.MAX_TRY_TIMES) { urlData = item; mWaiting.Remove(item); break; } } return urlData; } public void Init() { mWaiting.Clear(); mRunning.Clear(); mFinished.Clear(); IsDownloading = false; MaxTaskCountInSameTime = AppConfig.MAX_DOWNLOAD_TASKS; WorkDone = null; } public void StartDownload() { if (IsDownloading == false) { IsDownloading = true; } } public void Retry() { if (IsDownloading == false) { //重试就要把所有失败的任务重试次数重置为0 foreach (var item in mWaiting) { item.TryTimes = 0; } IsDownloading = true; } } public void PushTask(string filePath, Action<string> onSuccess, Action<string> onFailed) { mWaiting.Add(new DownloadSocketFileHelper(filePath, onSuccess, onFailed)); } } public class DownloadSocketFileHelper { int serverPort = 12345; int buffSize = 1024 * 1024; string serverIp = string.Empty; public int TryTimes = 0; public string filePath = string.Empty; string wholeFilePath = string.Empty; public int status = 0;//0为默认状态1为成功2为失败 public DownloadSocketFileHelper(string _filePath, Action<string> _OnSuccess, Action<string> _OnError) { serverIp = AppConfig.serverIP; filePath = _filePath; OnSuccess = _OnSuccess; OnError = _OnError; TryTimes = 0; wholeFilePath = AppConfig.HotAssetsPath + filePath; } string errorMsg = string.Empty; Socket clientSocket; public void Start() { try { status = 0; errorMsg = string.Empty; IsSuccess = false; IsFailed = false; DownloadManager.instance.StartCoroutine(CheckStatus()); clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse(serverIp), serverPort); clientSocket.Connect(ipEndPoint); Thread newThread = new Thread(ReceiveFile); newThread.Start(); SendMsgToServer(Encoding.UTF8.GetBytes(filePath)); Debug.Log("开始下载 = " + filePath); } catch (Exception ex) { errorMsg = ex.Message + " = " + filePath; status = 2; } } Action<string> OnSuccess; Action<string> OnError; void ReceiveFile() { try { string dir = Path.GetDirectoryName(wholeFilePath); if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); using (FileStream writer = new FileStream(wholeFilePath, FileMode.OpenOrCreate, FileAccess.Write)) { int len = 0; byte[] buff = new byte[buffSize]; while ((len = clientSocket.Receive(buff)) != 0) { string msg = Encoding.UTF8.GetString(buff, len - 3, 3); if (msg == "END") { writer.Write(buff, 0, len - 3); clientSocket.Close(); status = 1; break; } writer.Write(buff, 0, len); } } } catch (Exception ex) { clientSocket.Close(); errorMsg = ex.Message + " = " + filePath; status = 2; } } public bool IsSuccess = false; public bool IsFailed = false; IEnumerator CheckStatus() { yield return new WaitUntil(() => { return status != 0; }); if (status == 1) { if (OnSuccess != null) OnSuccess(filePath); IsSuccess = true; } else if (status == 2) { TryTimes++; if (TryTimes == AppConfig.MAX_TRY_TIMES) { errorMsg = StaticText.DownloadOverTimes; } if (OnError != null) OnError(errorMsg); IsFailed = true; } } public void SendMsgToServer(byte[] bytes) { clientSocket.Send(bytes); } }
using System.Collections.Generic; using System.IO; using UnityEngine; using UnityEngine.UI; class MyFileClient : MonoBehaviour { public Slider slider; public static MyFileClient instance; DownloadManager downloadManager; void Awake() { GetServerIP(); downloadManager = GetComponent<DownloadManager>(); instance = this; } void GetServerIP() { AppConfig.serverIP = File.ReadAllText(AppConfig.HotAssetsPath + AppConfig.serverIpFileName); } string LocalMD5FilePath = string.Empty; string hotAssetsPath = string.Empty; void Start() { LocalMD5FilePath = AppConfig.LocalMD5FilePath; hotAssetsPath = AppConfig.HotAssetsPath; downloadManager.Init(); downloadManager.WorkDone += onDownloadFileListDone; downloadManager.PushTask(AppConfig.LIST_FILENAME, onDownLoadFileListSuccess, onDownLoadFileListFailed); downloadManager.StartDownload(); } void StartGame() { } List<FileData> mServerFileList = null; void onDownLoadFileListSuccess(string filePath) { Debug.Log("文件列表下载成功"); string[] downloadFileStringList = File.ReadAllLines(hotAssetsPath + filePath); mServerFileList = new List<FileData>(); foreach (var item in downloadFileStringList) { if (string.IsNullOrEmpty(item.Trim())) { continue; } string[] tokens = item.Split('|'); if (tokens.Length >= 3) { FileData data = new FileData(); data.needUpdate = true; data.name = tokens[0].Trim(); data.md5 = tokens[1].Trim(); data.size = 0; int.TryParse(tokens[2], out data.size); if (data.size > 0) { mServerFileList.Add(data); } } } } void onDownLoadFileListFailed(string msg) { Debug.Log(msg + "下载失败"); ErrorHandle(msg); } void ErrorHandle(string msg) { MFMessageBox.Instance.PopYesNo(msg, () => { Application.Quit(); }, () => { downloadManager.Retry(); }, StaticText.QuitGame, StaticText.STR_RETRY); } void onDownloadFileListDone() { Debug.Log("下载任务进程结束"); if (downloadManager.IsCompleted) { print("文件列表下载成功"); CheckFileMD5(); } else { print("文件列表下载失败"); } } void CheckFileMD5() { //得到本地的文件列表 string[] oldLines = null; Debug.Log("比较MD5值 = " + LocalMD5FilePath); if (File.Exists(LocalMD5FilePath)) { oldLines = File.ReadAllLines(LocalMD5FilePath); } if (oldLines != null && oldLines.Length > 0) { //去除不需要更新的文件 foreach (var item in oldLines) { if (string.IsNullOrEmpty(item.Trim())) continue; string[] tokens = item.Split('|'); if (tokens.Length < 2) continue; string name = tokens[0].Trim(); string md5 = tokens[1].Trim(); if (!File.Exists(hotAssetsPath + name)) { continue; } for (int i = 0; i < mServerFileList.Count; i++) { if (mServerFileList[i].name == name && mServerFileList[i].md5 == md5) { mServerFileList[i].needUpdate = false; break; } } } } StartDownloadAssets(); } public void StartDownloadAssets() { downloadManager.Init(); downloadManager.WorkDone += onDownloadAllAssetDown; foreach (var item in mServerFileList) { if (item.needUpdate) { print("需要下载文件 = " + item.name); downloadManager.PushTask(item.name, onDownloadAssetSuccess, onDownloadAssetFailed); } } downloadManager.StartDownload(); } void onDownloadAssetSuccess(string filePath) { Debug.Log(filePath + "下载成功"); for (int i = 0; i < mServerFileList.Count; i++) { if (mServerFileList[i].name == filePath) { mServerFileList[i].needUpdate = false; break; } } } void onDownloadAssetFailed(string filePath) { Debug.Log(filePath + "下载失败"); ErrorHandle(StaticText.DownloadAssetsUpdateError); } void onDownloadAllAssetDown() { Debug.Log("下载任务进程结束"); if (downloadManager.IsCompleted) { print("所有ab资源下载成功"); StartGame(); } else { print("存在ab资源下载失败"); } UpdateFileList(); } void Update() { slider.value = (float)downloadManager.FinishedCount / downloadManager.WaitingAndRunningCount; } void OnApplicationQuit() { UpdateFileList(); } void UpdateFileList() { if (mServerFileList != null && mServerFileList.Count > 0) { string reslut = string.Empty; FileData fileData = null; for (int i = 0; i < mServerFileList.Count; i++) { fileData = mServerFileList[i]; if (fileData.needUpdate == false) { reslut += fileData.name + "|" + fileData.md5 + "|" + fileData.size + "\n"; } } reslut = reslut.TrimEnd('\n'); File.WriteAllText(AppConfig.LocalMD5FilePath, reslut); print("保存已经下载的文件的MD5值"); } } }
using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using UnityEngine; public class MyFileServer : MonoBehaviour { void Start() { StartUp(); } int serverPort = 12345; int listenCount = 10000; int buffSize = 1024 * 1024; Socket serverSocket; void StartUp() { try { serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint ipEndPoint = new IPEndPoint(GetMyIP(), serverPort); serverSocket.Bind(ipEndPoint); serverSocket.Listen(listenCount); Thread receiveThread = new Thread(ReceiveClientMsg); receiveThread.Start(); Debug.Log("启动成功"); } catch (SocketException ex) { Debug.LogError(ex.Message); } } void ReceiveClientMsg() { while (true) { Socket servicesSocket = serverSocket.Accept(); if (servicesSocket != null) { Thread newThread = new Thread(new ParameterizedThreadStart(HandleClientMsg)); newThread.Start(servicesSocket); } } } void HandleClientMsg(object _serivesSocket) { Socket serivesSocket = (Socket)_serivesSocket; byte[] buff = new byte[buffSize]; int len; while ((len = serivesSocket.Receive(buff)) != 0) { string msg = Encoding.UTF8.GetString(buff, 0, len); print("客户端返回消息 = " + msg); SentFileToClient(serivesSocket, "V:/Upload/" + msg); } } void SendMsgToClient(Socket serivesSocket, byte[] bytes) { serivesSocket.Send(bytes, 0, bytes.Length, SocketFlags.None); } void SentFileToClient(Socket serivesSocket, string filePath) { using (FileStream read = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { byte[] buff = new byte[buffSize]; int len = 0; while ((len = read.Read(buff, 0, buff.Length)) != 0) { Array.Resize(ref buff, len); SendMsgToClient(serivesSocket, buff); } } SendMsgToClient(serivesSocket, Encoding.UTF8.GetBytes("END")); serivesSocket.Close(); } void OnApplicationQuit() { print("关闭socket"); serverSocket.Close(); } IPAddress GetMyIP() { IPHostEntry IpEntry = Dns.GetHostEntry(Dns.GetHostName()); IPAddress ip = null; for (int i = 0; i < IpEntry.AddressList.Length; i++) { if (IpEntry.AddressList[i].AddressFamily == AddressFamily.InterNetwork)//检查是不是IPv4,IPv6是InterNetworkv6 { ip = IpEntry.AddressList[i]; break; } } return ip; } }
--默认值可以不传 local ConfigHelpers = {} --设置物体高亮 target:设置对象 isLigth:是否高亮 seeThrough:是否穿透(默认为true,穿透) startColor:高亮开始颜色(默认初始化) endColor:高亮结束颜色(默认初始化) flashingFrequency:闪烁频率(默认为2) flashingDelay:闪烁延迟(默认为0) nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.SetHeightLight( target , isLight , seeThrough , startColor , endColor , flashingFrequency , flashingDelay , nextConfigIdList ) local target = ConfigHelpers.FindGameObject(target) if target then flashingFrequency = flashingFrequency or 0.5 flashingDelay = flashingDelay or 0 seeThrough = (seeThrough == nil and {true} or {seeThrough})[1] startColor = startColor or Color.green endColor = endColor or Color.red GameUtils.SetHeightLight( target , isLight, seeThrough, startColor , endColor , flashingFrequency , flashingDelay ) local shla = target:GetComponent("SyncHighLightAction") if shla == nil then shla = target:AddComponent(typeof(SyncHighLightAction)) end shla:UpdateStatus(isLight, seeThrough , startColor , endColor , flashingFrequency , flashingDelay) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end end --播放动画 target:设置对象 clipName:动画名称 nextConfigIdList:完成过后要执行的id集合 speed:速度(默认为1) normalizedTime:动画从哪播放(默认为0,从头开始播放) function ConfigHelpers.PlayAnimation( target , clipName , nextConfigIdList , speed , normalizedTime ) if target then target = ConfigHelpers.FindGameObject(target) if target then local anima = target:GetComponent('Animation') if anima then local saa = target:GetComponent("SyncAnimationAction") if saa == nil then saa = target:AddComponent(typeof(SyncAnimationAction)) end speed = speed or 1 normalizedTime = normalizedTime or 0 local clip = anima:GetClip(clipName) if clip then anima.clip = clip local tClip = anima:get_Item(clipName) tClip.speed = speed tClip.normalizedTime = normalizedTime anima:Play() saa:SendMsgToServer() if nextConfigIdList then local allTime = anima.clip.length / math.abs(speed) GameUtils.StartCoroutineDelaySec(function ( ... ) ConfigHelpers.ExecIdList(nextConfigIdList) end,allTime) end else Print_t('<color=red>error:</color>Cannot find animation object = '..clipName) end end end end end --播放动画 target:设置对象 clipName:动画名称 nextConfigIdList:完成过后要执行的id集合 speed:速度(默认为1) normalizedTime:动画从哪播放(默认为0,从头开始播放) function ConfigHelpers.PlayAnimator( target , clipName , layer , speed , nextConfigIdList ) if target then target = ConfigHelpers.FindGameObject(target) if target then local anima = target:GetComponent('Animator') if anima then local saa = target:GetComponent("SyncAnimatorAction") if saa == nil then target:AddComponent(typeof(SyncAnimatorAction)) end speed = speed or 1 if clipName then anima:Play(clipName,layer) anima.speed = speed if nextConfigIdList then local allTime = anima:GetCurrentAnimatorStateInfo(layer).length / math.abs(speed) GameUtils.StartCoroutineDelaySec(function ( ... ) ConfigHelpers.ExecIdList(nextConfigIdList) end,allTime) end else Print_t('<color=red>error:</color>Cannot find animation object = '..clipName) end end end end end --延迟几秒执行 sec:延迟秒数 nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.DelaySec( sec , nextConfigIdList ) GameUtils.StartCoroutineDelaySec(function ( ... ) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end,sec) end --播放声音 audioName:音频名称 delaySec:延迟几秒播放(默认为0) nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.PlayAudio( audioName , delaySec , nextConfigIdList ) local audioLibrary = AudioLibrary.instance local audioClip = audioLibrary:GetAudioClip(audioName) if audioClip then delaySec = delaySec or 0 AudioSourceGlobal.clip = audioClip AudioSourceGlobal:PlayDelayed(delaySec) audioLibrary:SendSyncAudio(audioName,delaySec) if nextConfigIdList then GameUtils.StartCoroutineDelaySec(function ( ... ) ConfigHelpers.ExecIdList(nextConfigIdList) end,delaySec + audioClip.length) end else Print_t('<color=red>error:</color>Cannot find audio object = '..audioName) end end --关闭音频播放 nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.CloseAudio( nextConfigIdList ) AudioSourceGlobal.clip = nil AudioSourceGlobal:Stop() if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end --设置组件状态 target:设置对象 enabled:是否关闭 nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.SetComponentEnabled( target , componentName , enabled , nextConfigIdList ) if target then target = ConfigHelpers.FindGameObject(target) if target then local component = target:GetComponent(componentName) if component then component.enabled = enabled if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end else Print_t('<color=red>error:</color>Cannot find component object = '..clipName) end end end end --开始计时 nextConfigIdList:完成过后要执行的id集合 local isEndTime = false local tempTime = 0 function ConfigHelpers.StartTime( nextConfigIdList ) tempTime = 0 isEndTime = false GameUtils.StartCoroutineWaitUntil(function ( ) tempTime = tempTime + Time.deltaTime return isEndTime end) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end --计时结束 nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.EndTime( target , nextConfigIdList ) isEndTime = true if target then target = ConfigHelpers.FindGameObject(target) if target then local textObj = target:GetComponent('Text') if textObj then textObj.text = ConfigHelpers.FormatSecond(tempTime) local sta = target:GetComponent("SyncTextAction") if sta == nil then target:AddComponent(typeof(SyncTextAction)) end if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end end end end --初始化总分 nextConfigIdList:完成过后要执行的id集合 local allScore = 100 function ConfigHelpers.InitScore( score , nextConfigIdList ) allScore = score if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end --更新分数 nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.UpdateScore( value , nextConfigIdList ) allScore = allScore + value if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end --获取分数并显示 nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.GetScore( nextConfigIdList ) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end return allScore end --计时器 target:设置对象 direction:(1为正计时,-1为倒计时) startCount:起始点 endCount:目标点 nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.Timer( target , direction , startCount , endCount , nextConfigIdList ) if target then target = ConfigHelpers.FindGameObject(target) if target then local textObj = target:GetComponent('Text') if textObj then GameUtils.StartCoroutineWaitUntil(function ( ) if direction > 0 then startCount = startCount + Time.deltaTime if startCount >= endCount then if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end Print_t('正计时结束') end textObj.text = tostring(math.floor(startCount)) else startCount = startCount - Time.deltaTime if startCount <= endCount then if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end Print_t('倒计时结束') end textObj.text = tostring(math.ceil(startCount)) end local sta = target:GetComponent("SyncTextAction") if sta == nil then target:AddComponent(typeof(SyncTextAction)) end local result = (direction > 0 and {startCount >= endCount} or {startCount <= endCount})[1] return result end) end end end end --淡入淡出 finishNextConfigIdList:淡出后要执行的id集合 stayNextConfigIdList:黑屏时要执行的集合 fadeInTime:淡入花费时间 stayTime:黑屏花费时间 fadeOutTime:弹出花费时间 function ConfigHelpers.FadeInOut( finishNextConfigIdList , stayNextConfigIdList , fadeInTime , stayTime , fadeOutTime ) fadeInTime = fadeInTime or 1.5 stayTime = stayTime or 0.5 fadeOutTime = fadeOutTime or 1.5 GameUtils.FadeInOut(BlackBgGlobal,fadeInTime,stayTime,fadeOutTime,function ( ... ) if finishNextConfigIdList then ConfigHelpers.ExecIdList(finishNextConfigIdList) end end,function ( ... ) if stayNextConfigIdList then ConfigHelpers.ExecIdList(stayNextConfigIdList) end end) end --设置对象激活状态 target:设置对象 isActive:是否激活 nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.SetGameObjectActive( target , isActive , nextConfigIdList ) if target then target = ConfigHelpers.FindGameObject(target) if target then local saa = target:GetComponent("SyncActiveAction") if saa == nil then saa = target:AddComponent(typeof(SyncActiveAction)) end target.gameObject:SetActive(isActive) saa:UpdateStatus() if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end end end --设置物体FillAmount function ConfigHelpers.SetFillAmount( path , value , nextConfigIdList ) if path then target = ConfigHelpers.FindGameObject(path) if target then local v = target:GetComponent('Image') if v then v.fillAmount = value GameUtils.SyncFillAmount(path,value) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end end end end --实例化物体 target:要实例化的目标对象 name:为实例化的对象重命名 function ConfigHelpers.CloneGo( path , name , nextConfigIdList ) if path then local target = ConfigHelpers.FindGameObject(path) local result = nil if target then GameUtils.SyncCloneGo(path,name) result = GameObject.Instantiate(target) result.transform.parent = target.transform.parent result.transform.localEulerAngles = target.transform.localEulerAngles result.transform.localPosition = target.transform.localPosition result.transform.localScale = target.transform.localScale result.name = name if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end return result end end return result end --销毁物体 target:要销毁的对象 function ConfigHelpers.DestoryGo( path , isImmediate , nextConfigIdList ) if path then local target = ConfigHelpers.FindGameObject(path) if target then isImmediate = (isImmediate == nil and {false} or {isImmediate})[1] if isImmediate then CS.UnityEngine.Object.DestroyImmediate(target) else CS.UnityEngine.Object.Destroy(target) end GameUtils.SyncDestoryGo(path,isImmediate) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end end end --设置对象旋转 target:设置对象 isLocal:是否本地坐标旋转 time:旋转所需时间 nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.Rotation( target , isLocal , time , rx , ry , rz , nextConfigIdList ) if target then target = ConfigHelpers.FindGameObject(target) if target then local tween if isLocal then tween = target.transform:DOLocalRotate(Vector3(rx, ry, rz), time):SetEase(Ease.Linear) else tween = target.transform:DORotate(Vector3(rx, ry, rz), time):SetEase(Ease.Linear) end local sta = target:GetComponent("SyncTransformAction") if sta == nil then target:AddComponent(typeof(SyncTransformAction)) end tween:OnComplete(function ( ... ) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end) end end end --设置对象缩放 target:设置对象 time:缩放所需时间 nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.Scale( target , time , sx , sy , sz , nextConfigIdList ) if target then target = ConfigHelpers.FindGameObject(target) if target then local tween tween = target.transform:DOScale(Vector3(sx, sy, sz), time):SetEase(Ease.Linear) local sta = target:GetComponent("SyncTransformAction") if sta == nil then target:AddComponent(typeof(SyncTransformAction)) end tween:OnComplete(function ( ... ) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end) end end end --设置主角位置 target:设置对象 px:x值 py:y值 pz:z值 lookTarget:面对的对象 nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.SetPlayerTransform( px , py , pz , lookTarget , nextConfigIdList ) local target = nil if lookTarget ~= nil then target = ConfigHelpers.FindGameObject(lookTarget) end GameUtils.SetPlayerTransform(px,py,pz,target and target.transform or nil) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end --设置文本内容 target:设置对象 content:内容 nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.SetText( target , content , nextConfigIdList ) if target then target = ConfigHelpers.FindGameObject(target) if target then local text = target:GetComponent('Text') if text then local sta = target:GetComponent("SyncTextAction") if sta == nil then target:AddComponent(typeof(SyncTextAction)) end text.text = tostring(content) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end end end end --移动位置 target:设置对象 isLocal:是否是本地坐标 time:移动所需时间 nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.MoveToTarget( target , isLocal , px , py , pz , rx , ry , rz , time , nextConfigIdList ) if target then target = ConfigHelpers.FindGameObject(target) if target then rx = rx or 0 ry = ry or 0 rz = rz or 0 local sta = target:GetComponent("SyncTransformAction") if sta == nil then target:AddComponent(typeof(SyncTransformAction)) end GameUtils.MoveToTarget(target,isLocal,px,py,pz,rx,ry,rz ,time,function ( ... ) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end) end end end local dataLibrary = {} --设置数据 dataName:数据名称 dataValue:数据的值 nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.SetData( dataName , dataValue , nextConfigIdList ) dataLibrary[dataName] = dataValue if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end --获取数据 dataName:数据名称 nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.GetData( dataName , nextConfigIdList ) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end return dataLibrary[dataName] end -- local needExecNextConfigIdList -- function ConfigHelpers.SceneLoaded( scene , mode ) -- LoadConfig(scene.name,needExecNextConfigIdList) -- SceneManager.sceneLoaded('-', ConfigHelpers.SceneLoaded) -- end --切换场景 sceneName:要切换的场景 nextConfigIdList:完成过后要执行的id集合 function ChangseSceneClearData() -- needExecNextConfigIdList = nextConfigIdList -- SceneManager.sceneLoaded('+', ConfigHelpers.SceneLoaded) -- SceneManager.LoadSceneAsync(sceneName) ConfigHelpers.CloseAudio() ConfigHelpers.ClearLongPressMove() followMeDic = {} lookAtDic = {} MF.Route.ClearUpdate() ConfigHelpers.SetRaySelectListenerStatus(true) ConfigHelpers.StopAllCoroutine() end --切换场景 sceneName:要切换的场景 nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.ChangeScene( sceneName , nextConfigIdList ) -- needExecNextConfigIdList = nextConfigIdList -- SceneManager.sceneLoaded('+', ConfigHelpers.SceneLoaded) -- SceneManager.LoadSceneAsync(sceneName) GameUtils.LoadScene(sceneName,nextConfigIdList,nil,nil) end --停止已经开始的所有流程 function ConfigHelpers.StopAllCoroutine( nextConfigIdList ) GameUtils.StopAllCoroutine() if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end --设置摄像机远近 function ConfigHelpers.SetCameraNearFar( near , far , nextConfigIdList ) GameUtils.SetNearFar(near,far) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end --切换操作模式(手柄或者凝视) function ConfigHelpers.ChangeEventMode( isHandle , nextConfigIdList ) GameUtils.ChangeEventMode(isHandle) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end --为按钮注册事件 target:设置对象 nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.RegisterClick( target , nextConfigIdList ) if target then target = ConfigHelpers.FindGameObject(target) if target and nextConfigIdList then GameUtils.RegisterClick(target,function ( ... ) ConfigHelpers.ExecIdList(nextConfigIdList) end) end end end function ConfigHelpers.RemoveClick( target , nextConfigIdList ) if target then target = ConfigHelpers.FindGameObject(target) if target then GameUtils.RemoveClick(target) end if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end end function ConfigHelpers.OnMouseEnter( target , nextConfigIdList ) if target then target = ConfigHelpers.FindGameObject(target) if target and nextConfigIdList then GameUtils.OnMouseEnter(target,function ( ... ) ConfigHelpers.ExecIdList(nextConfigIdList) end) end end end function ConfigHelpers.RemoveMouseEnter( target , nextConfigIdList ) if target then target = ConfigHelpers.FindGameObject(target) if target then GameUtils.RemoveMouseEnter(target) end if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end end function ConfigHelpers.OnMouseExit( target , nextConfigIdList ) if target then target = ConfigHelpers.FindGameObject(target) if target and nextConfigIdList then GameUtils.OnMouseExit(target,function ( ... ) ConfigHelpers.ExecIdList(nextConfigIdList) end) end end end function ConfigHelpers.RemoveMouseExit( target , nextConfigIdList ) if target then target = ConfigHelpers.FindGameObject(target) if target then GameUtils.RemoveMouseExit(target) end if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end end function ConfigHelpers.SetRaySelectListenerStatus( active , nextConfigIdList ) GameUtils.SetRaySelectListenerStatus(active) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end --设置父物体 target:设置对象 parent:父物体 nextConfigIdList:完成过后要执行的id集合 function ConfigHelpers.SetParent( target , parent , px , py , pz , rx , ry , rz , sx , sy , sz , nextConfigIdList ) if target and parent then target = ConfigHelpers.FindGameObject(target) if target then parent = ConfigHelpers.FindGameObject(parent) if parent then local sta = target:GetComponent("SyncTransformAction") if sta == nil then target:AddComponent(typeof(SyncTransformAction)) end local t = target.transform t:SetParent(parent.transform) t.localPosition = Vector3( px , py , pz ) t.localEulerAngles = Vector3( rx , ry , rz ) t.localScale = Vector3( sx , sy , sz ) sta:UpdateTargetPath() if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end end end end --跟随摄像机 target:目标对象 isFollow:是否跟随 distance:面向的距离 nextConfigIdList:执行的id集合 local followMeDic = {} function ConfigHelpers.FollowMe( target , isFollow , ver , hor , distance , nextConfigIdList) if target then target = ConfigHelpers.FindGameObject(target) if target then if isFollow then local sta = target:GetComponent("SyncTransformAction") if sta == nil then target:AddComponent(typeof(SyncTransformAction)) end local player = GameObject.Find('FrameWork/Customs/ViveFocus/WaveVR/head').transform distance = distance or 4 ver = ver or 0 hor = hor or 0 local spanEulerAngles = target.transform.eulerAngles - player.eulerAngles followMeDic[target] = true GameUtils.StartCoroutineWaitUntil(function ( ... ) local v = player.position local result = v + (player.forward * distance) + (player.right * hor) + (player.up * ver) target.transform.position = result target.transform.eulerAngles = spanEulerAngles + player.eulerAngles return not followMeDic[target] end) else followMeDic[target] = nil end if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end end end --跟随摄像机 target:目标对象 isStop:是否停止面向自己(默认为false) local lookAtDic = {} function ConfigHelpers.LookMe( target , isStop , nextConfigIdList) if target then target = ConfigHelpers.FindGameObject(target) if target then local sta = target:GetComponent("SyncTransformAction") if sta == nil then target:AddComponent(typeof(SyncTransformAction)) end local player = GameObject.Find('FrameWork/Customs/ViveFocus/WaveVR/head').transform if not isStop then lookAtDic[target] = true local isUI = GameUtils.IsUI(target) GameUtils.StartCoroutineWaitUntil(function ( ... ) target.transform:LookAt(player.position) if isUI then target.transform:Rotate(Vector3.up, 180) end return not lookAtDic[target] end) else lookAtDic[target] = nil end if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end end end --播放视频 target:目标对象 videoName:视频名称(视频放到StreamingAssets文件夹下才有效) isLoop:是否重复播放(默认重复) nextConfigIdList:如果isLoop是true的话,那么会立马执行id集合,否则会等视频播放完才执行id集合 function ConfigHelpers.PlayVedio( target , videoName , isLoop , nextConfigIdList ) if target then target = ConfigHelpers.FindGameObject(target) if target then target:SetActive(false) isLoop = (isLoop == nil and {false} or {isLoop})[1] local mp = target:GetComponent('MediaPlayerCtrl') if mp == nil then mp = target:AddComponent(typeof(MediaPlayerCtrl)) end mp.m_TargetMaterial = { target } -- mp.m_objResize = { target } mp:Load(videoName) mp.m_bLoop = isLoop mp:Play() target:SetActive(true) if isLoop then if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end else mp.OnEnd = function ( ... ) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end end end end end --获取手柄在圆形区域的触摸点 function ConfigHelpers.GetTouchPadPosition( nextConfigIdList ) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end return GameUtils.GetTouchPadPosition() end --判断手柄上某键是否按下或者鼠标左键是否按下 function ConfigHelpers.GetKeyDown( keycode , nextConfigIdList ) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end return GameUtils.GetKeyDown(keycode) end --判断手柄上某键是否抬起或者鼠标左键是否抬起 function ConfigHelpers.GetKeyUp( keycode , nextConfigIdList ) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end return GameUtils.GetKeyUp(keycode) end --判断手柄上某键是否长按 function ConfigHelpers.GetKey( keycode , nextConfigIdList ) if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end return GameUtils.GetKey(keycode) end --增加物体长按拖动 function ConfigHelpers.AddLongPressMove( target , nextConfigIdList ) if target then target = ConfigHelpers.FindGameObject(target) if target then local sta = target:GetComponent("SyncTransformAction") if sta == nil then target:AddComponent(typeof(SyncTransformAction)) end GameUtils.AddLongPressMove(target) end end if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end --移除物体长按拖动 function ConfigHelpers.RemoveLongPressMove( target , nextConfigIdList ) if target then target = ConfigHelpers.FindGameObject(target) if target then GameUtils.RemoveLongPressMove(target) end end if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end --清除所有能拖拽物体的状态,使之不能被拖拽 function ConfigHelpers.ClearLongPressMove( nextConfigIdList ) GameUtils.ClearLongPressMove() if nextConfigIdList then ConfigHelpers.ExecIdList(nextConfigIdList) end end function ConfigHelpers.ExecId( id ) if id == nil then Print_t('<color=red>This ID is illegal</color>') return end if ConfigGlobal[id] == nil or ConfigGlobal[id].action == nil then Print_t('<color=red>This ID does not exist = '..id..'</color>') return end Print_t(id..' = '..ConfigGlobal[id].action) if ConfigGlobal[id].action then -- assert(load(ConfigGlobal[id].action))() xpcall(load(ConfigGlobal[id].action),function ( ... ) Print_t(debug.traceback(),'<color=red>error</color>') end) end end function ConfigHelpers.ExecIdList( idListOrFunc ) if idListOrFunc then if type(idListOrFunc) == 'table' then for i,id in ipairs(idListOrFunc) do ConfigHelpers.ExecId(id) end else idListOrFunc() end end end function ConfigHelpers.FindGameObject( path ) if path and path ~= '' then local target = GameObject.Find(path) if target then return target else local startIndex = string.find(path,'/') if startIndex then local rootPath = string.sub(path,0,startIndex-1) local root = GameObject.Find('/'..rootPath) if root then local childPath = string.sub(path,startIndex+1,path.length) local result = root.transform:Find(childPath) if result then return result.gameObject end end end end end Print_t('<color=red>error:</color>No object was found = '..(path or '')) return nil end --总秒数格式化 传总秒数 返回的时间格式为 59:34 第二个参数表示是否显示小时 function ConfigHelpers.FormatSecond( senconds , isShowHour ) local min,sec = math.modf(senconds / 60) local hour,sec = math.modf(senconds / 3600) sec = math.floor(senconds - min*60) if sec < 10 then sec = '0'..sec end if hour < 10 then hour = '0'..hour end local t = min ..':'..sec return isShowHour and hour..t or t end return ConfigHelpers