#if UNITY_EDITOR using System.IO; using UnityEditor; using UnityEngine; using ShaderType = AllIn1SpriteShader.AllIn1Shader.ShaderTypes; namespace AllIn1SpriteShader { public class AllIn1ShaderWindow : EditorWindow { private const string versionString = "4.2"; [MenuItem("Tools/AllIn1/SpriteShaderWindow")] public static void ShowAllIn1ShaderWindowWindow() { GetWindow("All In 1 Shader Window"); } public static readonly string CUSTOM_EDITOR_HEADER = "AllIn1SpriteShaderEditorImage"; private static string basePath = "Assets/Plugins/AllIn1SpriteShader"; public static readonly string materialsSavesRelativePath = "/Materials"; public static readonly string renderImagesSavesRelativePath = "/Textures"; public static readonly string normalMapSavesRelativePath = "/Textures/NormalMaps"; public static readonly string gradientSavesRelativePath = "/Textures/GradientTextures"; public Vector2 scrollPosition = Vector2.zero; private Texture2D imageInspector; private DefaultAsset materialTargetFolder = null; private GUIStyle style, bigLabel = new GUIStyle(), titleStyle = new GUIStyle(); private const int bigFontSize = 16; AllIn1Shader.ShaderTypes shaderTypes = AllIn1Shader.ShaderTypes.Default; bool showUrpWarning = false; double warningTime = 0f; private Texture2D targetNormalImage; private float normalStrength = 5f; private int normalSmoothing = 1; private int isComputingNormals = 0; private enum TextureSizes { _2 = 2, _4 = 4, _8 = 8, _16 = 16, _32 = 32, _64 = 64, _128 = 128, _256 = 256, _512 = 512, _1024 = 1024, _2048 = 2048 } private TextureSizes textureSizes = TextureSizes._128; [SerializeField] private Gradient gradient = new Gradient(); private FilterMode gradientFiltering = FilterMode.Bilinear; private enum ImageType { ShowImage, HideInComponent, HideEverywhere } private ImageType imageType; private void OnGUI() { style = new GUIStyle(EditorStyles.helpBox); style.margin = new RectOffset(0, 0, 0, 0); bigLabel = new GUIStyle(EditorStyles.boldLabel); bigLabel.fontSize = bigFontSize; titleStyle.alignment = TextAnchor.MiddleLeft; using (var scrollView = new EditorGUILayout.ScrollViewScope(scrollPosition, GUILayout.Width(position.width), GUILayout.Height(position.height))) { scrollPosition = scrollView.scrollPosition; ShowImageAndSetImageEditorPref(); ShowAssetImageOptionsToggle(); DefaultAssetShader(); DrawLine(Color.grey, 1, 3); GUILayout.Label("Material Save Path", bigLabel); GUILayout.Space(20); GUILayout.Label("Select the folder where new Materials will be saved when the Save Material To Folder button of the asset component is pressed", EditorStyles.boldLabel); HandleSaveFolderEditorPref("All1ShaderMaterials", basePath + materialsSavesRelativePath, "Material"); DrawLine(Color.grey, 1, 3); GUILayout.Label("Render Material to Image Save Path", bigLabel); GUILayout.Space(20); EditorGUILayout.BeginHorizontal(); { float scaleSlider = 1; if (PlayerPrefs.HasKey("All1ShaderRenderImagesScale")) scaleSlider = PlayerPrefs.GetFloat("All1ShaderRenderImagesScale"); GUILayout.Label("Rendered Image Texture Scale", GUILayout.MaxWidth(190)); scaleSlider = EditorGUILayout.Slider(scaleSlider, 0.2f, 5f, GUILayout.MaxWidth(200)); if (GUILayout.Button("Default Value", GUILayout.MaxWidth(100))) PlayerPrefs.SetFloat("All1ShaderRenderImagesScale", 1f); else PlayerPrefs.SetFloat("All1ShaderRenderImagesScale", scaleSlider); } EditorGUILayout.EndVertical(); GUILayout.Label("Select the folder where new Images will be saved when the Render Material To Image button of the asset component is pressed", EditorStyles.boldLabel); HandleSaveFolderEditorPref("All1ShaderRenderImages", basePath + renderImagesSavesRelativePath, "Images"); DrawLine(Color.grey, 1, 3); NormalMapCreator(); DrawLine(Color.grey, 1, 3); GradientCreator(); DrawLine(Color.grey, 1, 3); GUILayout.Space(10); SceneNotificationsToggle(); DrawLine(Color.grey, 1, 3); GUILayout.Space(10); RefreshLitShader(); GUILayout.Space(10); DrawLine(Color.grey, 1, 3); GUILayout.Label("Current asset version is " + versionString, EditorStyles.boldLabel); } } private void ShowImageAndSetImageEditorPref() { if(!EditorPrefs.HasKey("allIn1ImageConfig")) { EditorPrefs.SetInt("allIn1ImageConfig", (int) ImageType.ShowImage); } imageType = (ImageType) EditorPrefs.GetInt("allIn1ImageConfig"); if(imageType == ImageType.HideEverywhere) return; switch(imageType) { case ImageType.ShowImage: case ImageType.HideInComponent: if(imageInspector == null) imageInspector = GetInspectorImage(); break; } if(imageInspector) { Rect rect = EditorGUILayout.GetControlRect(GUILayout.Height(50)); GUI.DrawTexture(rect, imageInspector, ScaleMode.ScaleToFit, true); } DrawLine(Color.grey, 1, 3); } public static Texture2D GetInspectorImage() => GetImage(CUSTOM_EDITOR_HEADER); private static Texture2D GetImage(string textureName) { string[] guids = AssetDatabase.FindAssets($"{textureName} t:texture"); if(guids.Length > 0) { string path = AssetDatabase.GUIDToAssetPath(guids[0]); return AssetDatabase.LoadAssetAtPath(path); } return null; } private void ShowAssetImageOptionsToggle() { GUILayout.Label("Asset Image Display Options", bigLabel); GUILayout.Space(20); int previousImageType = (int) imageType; imageType = (ImageType) EditorGUILayout.EnumPopup(imageType, GUILayout.MaxWidth(200)); if((int) imageType != previousImageType) EditorPrefs.SetInt("allIn1ImageConfig", (int) imageType); DrawLine(Color.grey, 1, 3); } private void DefaultAssetShader() { GUILayout.Label("Default Asset Shader", bigLabel); GUILayout.Space(20); GUILayout.Label("This is the shader variant that will be assigned by default to Sprites and UI Images when the asset component is added", EditorStyles.boldLabel); bool isUrp = false; Shader temp = FindShader("AllIn1Urp2dRenderer"); if (temp != null) isUrp = true; shaderTypes = (AllIn1Shader.ShaderTypes)PlayerPrefs.GetInt("allIn1DefaultShader"); int previousShaderType = (int)shaderTypes; shaderTypes = (AllIn1Shader.ShaderTypes)EditorGUILayout.EnumPopup(shaderTypes, GUILayout.MaxWidth(200)); if (previousShaderType != (int)shaderTypes) { if (!isUrp && shaderTypes == AllIn1Shader.ShaderTypes.Urp2dRenderer) { showUrpWarning = true; warningTime = EditorApplication.timeSinceStartup + 5; } else { PlayerPrefs.SetInt("allIn1DefaultShader", (int)shaderTypes); showUrpWarning = false; } } if (warningTime < EditorApplication.timeSinceStartup) showUrpWarning = false; if (isUrp) showUrpWarning = false; if (!isUrp && !showUrpWarning && shaderTypes == AllIn1Shader.ShaderTypes.Urp2dRenderer) { showUrpWarning = true; warningTime = EditorApplication.timeSinceStartup + 5; shaderTypes = AllIn1Shader.ShaderTypes.Default; PlayerPrefs.SetInt("allIn1DefaultShader", (int)shaderTypes); } if (showUrpWarning) EditorGUILayout.HelpBox( "You can't set the URP 2D Renderer variant since you didn't import the URP package available in the asset root folder (SEE DOCUMENTATION)", MessageType.Error, true); } private void NormalMapCreator() { GUILayout.Label("Normal Map Creator", bigLabel); GUILayout.Space(20); GUILayout.Label("Select the folder where new Normal Maps will be saved when the Create Normal Map button of the asset component is pressed (URP only)", EditorStyles.boldLabel); HandleSaveFolderEditorPref("All1ShaderNormals", basePath + normalMapSavesRelativePath, "Normal Maps"); GUILayout.Space(20); GUILayout.Label("Assign a sprite you want to create a normal map from. Choose the normal map settings and press the 'Create And Save Normal Map' button", EditorStyles.boldLabel); targetNormalImage = (Texture2D)EditorGUILayout.ObjectField("Target Image", targetNormalImage, typeof(Texture2D), false, GUILayout.MaxWidth(225)); EditorGUILayout.BeginHorizontal(); { GUILayout.Label("Normal Strength:", GUILayout.MaxWidth(150)); normalStrength = EditorGUILayout.Slider(normalStrength, 1f, 20f, GUILayout.MaxWidth(400)); } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); { GUILayout.Label("Normal Smoothing:", GUILayout.MaxWidth(150)); normalSmoothing = EditorGUILayout.IntSlider(normalSmoothing, 0, 3, GUILayout.MaxWidth(400)); } EditorGUILayout.EndHorizontal(); if (isComputingNormals == 0) { if (targetNormalImage != null) { if (GUILayout.Button("Create And Save Normal Map")) { isComputingNormals = 1; return; } } else { GUILayout.Label("Add a Target Image to use this feature", EditorStyles.boldLabel); } } else { GUILayout.Label("Normal Map is currently being created, be patient", EditorStyles.boldLabel, GUILayout.Height(40)); Repaint(); isComputingNormals++; if (isComputingNormals > 5) { string assetPath = AssetDatabase.GetAssetPath(targetNormalImage); var tImporter = AssetImporter.GetAtPath(assetPath) as TextureImporter; if (tImporter != null) { tImporter.isReadable = true; tImporter.SaveAndReimport(); } Texture2D normalToSave = CreateNormalMap(targetNormalImage, normalStrength, normalSmoothing); string prefSavedPath = PlayerPrefs.GetString("All1ShaderNormals") + "/"; string path = prefSavedPath + "NormalMap.png"; if(System.IO.File.Exists(path)) path = GetNewValidPath(path); string texName = path.Replace(prefSavedPath, ""); path = EditorUtility.SaveFilePanel("Save texture as PNG", prefSavedPath, texName, "png"); //If you are reading this you might have encountered an error in Unity 2022 Mac builds, if that's the case comment the line above and uncomment the line below //path = prefSavedPath + texName + ".png"; if (path.Length != 0) { byte[] pngData = normalToSave.EncodeToPNG(); if (pngData != null) File.WriteAllBytes(path, pngData); AssetDatabase.Refresh(); if (path.IndexOf("Assets/") >= 0) { string subPath = path.Substring(path.IndexOf("Assets/")); TextureImporter importer = AssetImporter.GetAtPath(subPath) as TextureImporter; if (importer != null) { Debug.Log("Normal Map saved inside the project: " + subPath); importer.filterMode = FilterMode.Bilinear; importer.textureType = TextureImporterType.NormalMap; importer.wrapMode = TextureWrapMode.Repeat; importer.SaveAndReimport(); EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath(subPath, typeof(Texture))); } } else Debug.Log("Normal Map saved outside the project: " + path); } isComputingNormals = 0; } } GUILayout.Label("*This process will freeze the editor for some seconds, larger images will take longer", EditorStyles.boldLabel); } private void HandleSaveFolderEditorPref(string keyName, string defaultPath, string logsFeatureName) { if (!PlayerPrefs.HasKey(keyName)) PlayerPrefs.SetString(keyName, defaultPath); materialTargetFolder = (DefaultAsset)AssetDatabase.LoadAssetAtPath(PlayerPrefs.GetString(keyName), typeof(DefaultAsset)); if (materialTargetFolder == null) { PlayerPrefs.SetString(keyName, defaultPath); materialTargetFolder = (DefaultAsset)AssetDatabase.LoadAssetAtPath(PlayerPrefs.GetString(keyName), typeof(DefaultAsset)); if (materialTargetFolder == null) { materialTargetFolder = (DefaultAsset)AssetDatabase.LoadAssetAtPath("Assets/", typeof(DefaultAsset)); if(materialTargetFolder == null) { EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(600)); EditorGUILayout.HelpBox("Folder is invalid, please select a valid one", MessageType.Error, true); EditorGUILayout.EndHorizontal(); } else PlayerPrefs.SetString("Assets/", defaultPath); } } materialTargetFolder = (DefaultAsset)EditorGUILayout.ObjectField("New " + logsFeatureName + " Folder", materialTargetFolder, typeof(DefaultAsset), false, GUILayout.MaxWidth(500)); if (materialTargetFolder != null && IsAssetAFolder(materialTargetFolder)) { string path = AssetDatabase.GetAssetPath(materialTargetFolder); PlayerPrefs.SetString(keyName, path); EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(600)); EditorGUILayout.HelpBox("Valid folder! " + logsFeatureName + " save path: " + path, MessageType.Info); EditorGUILayout.EndHorizontal(); } else { EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(600)); EditorGUILayout.HelpBox("Select the new " + logsFeatureName + " Folder", MessageType.Warning, true); EditorGUILayout.EndHorizontal(); } } private void GradientCreator() { GUILayout.Label("Gradient Creator", bigLabel); GUILayout.Space(20); GUILayout.Label("This feature can be used to create textures for the Color Ramp Effect", EditorStyles.boldLabel); EditorGUILayout.GradientField("Gradient", gradient, GUILayout.Height(25), GUILayout.MaxWidth(500)); EditorGUILayout.BeginHorizontal(); { GUILayout.Label("Texture Size:", GUILayout.MaxWidth(145)); textureSizes = (TextureSizes)EditorGUILayout.EnumPopup(textureSizes, GUILayout.MaxWidth(200)); } EditorGUILayout.EndHorizontal(); int textureSize = (int)textureSizes; Texture2D gradTex = new Texture2D(textureSize, 1, TextureFormat.RGBA32, false); for (int i = 0; i < textureSize; i++) gradTex.SetPixel(i, 0, gradient.Evaluate((float)i / (float)textureSize)); gradTex.Apply(); GUILayout.Space(20); GUILayout.Label("Select the folder where new Gradient Textures will be saved", EditorStyles.boldLabel); HandleSaveFolderEditorPref("All1ShaderGradients", basePath + gradientSavesRelativePath, "Gradient"); string prefSavedPath = PlayerPrefs.GetString("All1ShaderGradients") + "/"; if (Directory.Exists(prefSavedPath)) { EditorGUILayout.BeginHorizontal(); { GUILayout.Label("Gradient Texture Filtering: ", GUILayout.MaxWidth(170)); gradientFiltering = (FilterMode)EditorGUILayout.EnumPopup(gradientFiltering, GUILayout.MaxWidth(200)); } EditorGUILayout.EndHorizontal(); if (GUILayout.Button("Save Gradient Texture", GUILayout.MaxWidth(500))) { string path = prefSavedPath + "ColorGradient.png"; if(System.IO.File.Exists(path)) path = GetNewValidPath(path); string texName = path.Replace(prefSavedPath, ""); path = EditorUtility.SaveFilePanel("Save texture as PNG", prefSavedPath, texName, "png"); if (path.Length != 0) { byte[] pngData = gradTex.EncodeToPNG(); if (pngData != null) File.WriteAllBytes(path, pngData); AssetDatabase.Refresh(); if (path.IndexOf("Assets/") >= 0) { string subPath = path.Substring(path.IndexOf("Assets/")); TextureImporter importer = AssetImporter.GetAtPath(subPath) as TextureImporter; if (importer != null) { Debug.Log("Gradient saved inside the project: " + subPath); importer.filterMode = gradientFiltering; importer.SaveAndReimport(); EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath(subPath, typeof(Texture))); } } else Debug.Log("Gradient saved outside the project: " + path); } } } } private static bool IsAssetAFolder(Object obj) { string path = ""; if (obj == null) return false; path = AssetDatabase.GetAssetPath(obj.GetInstanceID()); if (path.Length > 0) { if (Directory.Exists(path)) return true; else return false; } return false; } private static string GetNewValidPath(string path, string extension = ".png", int i = 1) { int number = i; path = path.Replace(extension, ""); string newPath = path + "_" + number.ToString(); string fullPath = newPath + extension; if(File.Exists(fullPath)) { number++; fullPath = GetNewValidPath(path, extension, number); } return fullPath; } private void DrawLine(Color color, int thickness = 2, int padding = 10) { Rect r = EditorGUILayout.GetControlRect(GUILayout.Height(padding + thickness)); r.height = thickness; r.y += (padding / 2); r.x -= 2; r.width += 6; EditorGUI.DrawRect(r, color); } public static Texture2D CreateNormalMap(Texture2D t, float normalMult = 5f, int normalSmooth = 0) { int width = t.width; int height = t.height; Color[] sourcePixels = t.GetPixels(); Color[] resultPixels = new Color[width * height]; Vector3 vScale = new Vector3(0.3333f, 0.3333f, 0.3333f); for(int y = 0; y < height; y++) { for(int x = 0; x < width; x++) { int index = x + y * width; Vector3 cSampleNegXNegY = GetPixelClamped(sourcePixels, x - 1, y - 1, width, height); Vector3 cSampleZerXNegY = GetPixelClamped(sourcePixels, x, y - 1, width, height); Vector3 cSamplePosXNegY = GetPixelClamped(sourcePixels, x + 1, y - 1, width, height); Vector3 cSampleNegXZerY = GetPixelClamped(sourcePixels, x - 1, y, width, height); Vector3 cSamplePosXZerY = GetPixelClamped(sourcePixels, x + 1, y, width, height); Vector3 cSampleNegXPosY = GetPixelClamped(sourcePixels, x - 1, y + 1, width, height); Vector3 cSampleZerXPosY = GetPixelClamped(sourcePixels, x, y + 1, width, height); Vector3 cSamplePosXPosY = GetPixelClamped(sourcePixels, x + 1, y + 1, width, height); float fSampleNegXNegY = Vector3.Dot(cSampleNegXNegY, vScale); float fSampleZerXNegY = Vector3.Dot(cSampleZerXNegY, vScale); float fSamplePosXNegY = Vector3.Dot(cSamplePosXNegY, vScale); float fSampleNegXZerY = Vector3.Dot(cSampleNegXZerY, vScale); float fSamplePosXZerY = Vector3.Dot(cSamplePosXZerY, vScale); float fSampleNegXPosY = Vector3.Dot(cSampleNegXPosY, vScale); float fSampleZerXPosY = Vector3.Dot(cSampleZerXPosY, vScale); float fSamplePosXPosY = Vector3.Dot(cSamplePosXPosY, vScale); float edgeX = (fSampleNegXNegY - fSamplePosXNegY) * 0.25f + (fSampleNegXZerY - fSamplePosXZerY) * 0.5f + (fSampleNegXPosY - fSamplePosXPosY) * 0.25f; float edgeY = (fSampleNegXNegY - fSampleNegXPosY) * 0.25f + (fSampleZerXNegY - fSampleZerXPosY) * 0.5f + (fSamplePosXNegY - fSamplePosXPosY) * 0.25f; Vector2 vEdge = new Vector2(edgeX, edgeY) * normalMult; Vector3 norm = new Vector3(vEdge.x, vEdge.y, 1.0f).normalized; resultPixels[index] = new Color(norm.x * 0.5f + 0.5f, norm.y * 0.5f + 0.5f, norm.z * 0.5f + 0.5f, 1); } } if(normalSmooth > 0) { resultPixels = SmoothNormals(resultPixels, width, height, normalSmooth); } Texture2D texNormal = new Texture2D(width, height, TextureFormat.RGB24, false, false); texNormal.SetPixels(resultPixels); texNormal.Apply(); return texNormal; } private static Vector3 GetPixelClamped(Color[] pixels, int x, int y, int width, int height) { x = Mathf.Clamp(x, 0, width - 1); y = Mathf.Clamp(y, 0, height - 1); Color c = pixels[x + y * width]; return new Vector3(c.r, c.g, c.b); } private static Color[] SmoothNormals(Color[] pixels, int width, int height, int normalSmooth) { Color[] smoothedPixels = new Color[pixels.Length]; float step = 0.00390625f * normalSmooth; for(int y = 0; y < height; y++) { for(int x = 0; x < width; x++) { float pixelsToAverage = 0.0f; Color c = pixels[x + y * width]; pixelsToAverage++; for(int offsetY = -normalSmooth; offsetY <= normalSmooth; offsetY++) { for(int offsetX = -normalSmooth; offsetX <= normalSmooth; offsetX++) { if(offsetX == 0 && offsetY == 0) continue; int sampleX = Mathf.Clamp(x + offsetX, 0, width - 1); int sampleY = Mathf.Clamp(y + offsetY, 0, height - 1); c += pixels[sampleX + sampleY * width]; pixelsToAverage++; } } smoothedPixels[x + y * width] = c / pixelsToAverage; } } return smoothedPixels; } [MenuItem("Assets/Create/AllIn1Shader Materials/CreateDefaultMaterial")] public static void CreateDefaultMaterial() { CreateMaterial("AllIn1SpriteShader"); } [MenuItem("Assets/Create/AllIn1Shader Materials/CreateScaledTimeMaterial")] public static void CreateScaledTimeMaterial() { CreateMaterial("AllIn1SpriteShaderScaledTime"); } [MenuItem("Assets/Create/AllIn1Shader Materials/CreateUiMaskMaterial")] public static void CreateUiMaskMaterial() { CreateMaterial("AllIn1SpriteShaderUiMask"); } private static void CreateMaterial(string shaderName) { string selectedPath = AssetDatabase.GetAssetPath(Selection.activeObject); if(!string.IsNullOrEmpty(selectedPath) && Directory.Exists(selectedPath)) { Material material = new Material(FindShader(shaderName)); string fullPath = selectedPath + "/Mat-" + shaderName + ".mat"; if(File.Exists(fullPath)) fullPath = GetNewValidPath(fullPath, ".mat"); AssetDatabase.CreateAsset(material, fullPath); AssetDatabase.Refresh(); } else { Debug.LogWarning("Please select a valid folder in the Project Window."); } } private void OnEnable() => GetBasePath(); private static void GetBasePath() { string[] guids = AssetDatabase.FindAssets("t:folder AllIn1SpriteShader"); if(guids.Length > 0) { basePath = AssetDatabase.GUIDToAssetPath(guids[0]); } else { Debug.LogError("AllIn1SpriteShader folder not found in the project."); basePath = "Assets/Plugins/AllIn1SpriteShader"; } } public static string GetMaterialSavePath() { if(!PlayerPrefs.HasKey("All1ShaderMaterials")) { GetBasePath(); return basePath + materialsSavesRelativePath; } return PlayerPrefs.GetString("All1ShaderMaterials"); } public static string GetRenderImageSavePath() { if(!PlayerPrefs.HasKey("All1ShaderRenderImages")) { GetBasePath(); return basePath + renderImagesSavesRelativePath; } return PlayerPrefs.GetString("All1ShaderRenderImages"); } public static string GetNormalMapSavePath() { if(!PlayerPrefs.HasKey("All1ShaderNormals")) { GetBasePath(); return basePath + normalMapSavesRelativePath; } return PlayerPrefs.GetString("All1ShaderNormals"); } private void SceneNotificationsToggle() { float previousLabelWidth = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = 200f; bool areNotificationsEnabled = EditorPrefs.GetInt("DisplaySceneViewNotifications", 1) == 1; areNotificationsEnabled = EditorGUILayout.Toggle("Display Scene View Notifications", areNotificationsEnabled); EditorPrefs.SetInt("DisplaySceneViewNotifications", areNotificationsEnabled ? 1 : 0); EditorGUIUtility.labelWidth = previousLabelWidth; } private static void RefreshLitShader() { GUILayout.Label("Force the Lit Shader to be reconfigured"); GUILayout.Label("If you are getting some error or have changed the render pipeline press the button below"); if (GUILayout.Button("Refresh Lit Shader", GUILayout.MaxWidth(500f))) { AllIn1ShaderImporter.ForceReimport(); } } public static void SceneViewNotificationAndLog(string message) { Debug.Log(message); ShowSceneViewNotification(message); } public static void ShowSceneViewNotification(string message) { bool showNotification = EditorPrefs.GetInt("DisplaySceneViewNotifications", 1) == 1; if(!showNotification) return; GUIContent content = new GUIContent(message); #if UNITY_2019_1_OR_NEWER SceneView.lastActiveSceneView.ShowNotification(content, 1.5f); #else SceneView.lastActiveSceneView.ShowNotification(content); #endif } public static Shader FindShader(string shaderName) { string[] guids = AssetDatabase.FindAssets($"{shaderName} t:shader"); foreach(string guid in guids) { string path = AssetDatabase.GUIDToAssetPath(guid); Shader shader = AssetDatabase.LoadAssetAtPath(path); if(shader != null) { string fullShaderName = shader.name; string actualShaderName = fullShaderName.Substring(fullShaderName.LastIndexOf('/') + 1); if(actualShaderName.Equals(shaderName)) return shader; } } return null; } } } #endif