com.wallstop-studios.unity-helpers 1.0.1-rc03 → 1.0.1-rc04
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Editor/AnimationCopier.cs +124 -0
- package/Editor/AnimationCopier.cs.meta +3 -0
- package/Editor/AnimationCreator.cs +15 -12
- package/Editor/CustomEditors/MatchColliderToSpriteEditor.cs +31 -0
- package/Editor/CustomEditors/MatchColliderToSpriteEditor.cs.meta +3 -0
- package/Editor/CustomEditors.meta +3 -0
- package/Editor/PrefabCheckWizard.cs +1 -1
- package/Editor/SpriteSettingsApplier.cs +154 -0
- package/Editor/SpriteSettingsApplier.cs.meta +3 -0
- package/Editor/TextureResizerWizard.cs +158 -0
- package/Editor/TextureResizerWizard.cs.meta +3 -0
- package/Editor/TextureSettingsApplier.cs +158 -0
- package/Editor/TextureSettingsApplier.cs.meta +3 -0
- package/Editor/Utils/EditorUtilities.cs +20 -0
- package/Editor/Utils/EditorUtilities.cs.meta +11 -0
- package/Editor/Utils/ReadOnlyPropertyDrawer.cs +28 -0
- package/Editor/Utils/ReadOnlyPropertyDrawer.cs.meta +11 -0
- package/Editor/Utils.meta +8 -0
- package/Runtime/Utils/CenterPointOffset.cs +31 -0
- package/Runtime/Utils/CenterPointOffset.cs.meta +3 -0
- package/Runtime/Utils/CoroutineHandler.cs +4 -0
- package/Runtime/Utils/CoroutineHandler.cs.meta +3 -0
- package/Runtime/Utils/MatchColliderToSprite.cs +80 -0
- package/Runtime/Utils/MatchColliderToSprite.cs.meta +3 -0
- package/Runtime/Utils/RuntimeSingleton.cs +31 -0
- package/Runtime/Utils/RuntimeSingleton.cs.meta +11 -0
- package/Runtime/Utils/SetTextureImportData.cs +4 -2
- package/Runtime/Utils/TextureScale.cs +164 -0
- package/Runtime/Utils/TextureScale.cs.meta +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
namespace UnityHelpers.Editor
|
|
2
|
+
{
|
|
3
|
+
#if UNITY_EDITOR
|
|
4
|
+
using System;
|
|
5
|
+
using System.IO;
|
|
6
|
+
using Core.Attributes;
|
|
7
|
+
using Core.Extension;
|
|
8
|
+
using UnityEditor;
|
|
9
|
+
using UnityEngine;
|
|
10
|
+
using Utils;
|
|
11
|
+
|
|
12
|
+
public sealed class AnimationCopier : ScriptableWizard
|
|
13
|
+
{
|
|
14
|
+
private string _fullSourcePath;
|
|
15
|
+
private string _fullDestinationPath;
|
|
16
|
+
|
|
17
|
+
[ReadOnly]
|
|
18
|
+
public string animationSourcePath;
|
|
19
|
+
|
|
20
|
+
[ReadOnly]
|
|
21
|
+
public string animationDestinationPath;
|
|
22
|
+
|
|
23
|
+
[MenuItem("Tools/Unity Helpers/Animation Copier")]
|
|
24
|
+
public static void CopyAnimations()
|
|
25
|
+
{
|
|
26
|
+
_ = DisplayWizard<AnimationCopier>("Animation Copier", "Copy");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
protected override bool DrawWizardGUI()
|
|
30
|
+
{
|
|
31
|
+
bool returnValue = base.DrawWizardGUI();
|
|
32
|
+
|
|
33
|
+
if (GUILayout.Button("Set Animation Source Path"))
|
|
34
|
+
{
|
|
35
|
+
string sourcePath = EditorUtility.OpenFolderPanel(
|
|
36
|
+
"Select Animation Source Path", EditorUtilities.GetCurrentPathOfProjectWindow(), string.Empty);
|
|
37
|
+
int assetIndex = sourcePath?.IndexOf("Assets", StringComparison.Ordinal) ?? -1;
|
|
38
|
+
if (assetIndex < 0)
|
|
39
|
+
{
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
_fullSourcePath = animationSourcePath = sourcePath ?? string.Empty;
|
|
44
|
+
animationSourcePath = animationSourcePath.Substring(assetIndex);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (GUILayout.Button("Set Animation Destination Path"))
|
|
49
|
+
{
|
|
50
|
+
string sourcePath = EditorUtility.OpenFolderPanel(
|
|
51
|
+
"Select Animation Destination Path", EditorUtilities.GetCurrentPathOfProjectWindow(), string.Empty);
|
|
52
|
+
int assetIndex = sourcePath?.IndexOf("Assets", StringComparison.Ordinal) ?? -1;
|
|
53
|
+
if (assetIndex < 0)
|
|
54
|
+
{
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
_fullDestinationPath = animationDestinationPath = sourcePath ?? string.Empty;
|
|
59
|
+
animationDestinationPath = animationDestinationPath.Substring(assetIndex);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return returnValue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private void OnWizardCreate()
|
|
67
|
+
{
|
|
68
|
+
if (string.IsNullOrEmpty(_fullSourcePath) || string.IsNullOrEmpty(_fullDestinationPath))
|
|
69
|
+
{
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (string.IsNullOrEmpty(animationSourcePath) || string.IsNullOrEmpty(animationDestinationPath))
|
|
74
|
+
{
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
foreach (string assetGuid in AssetDatabase.FindAssets("t:AnimationClip", new[] { animationSourcePath }))
|
|
79
|
+
{
|
|
80
|
+
string path = AssetDatabase.GUIDToAssetPath(assetGuid);
|
|
81
|
+
|
|
82
|
+
AnimationClip animationClip = AssetDatabase.LoadAssetAtPath<AnimationClip>(path);
|
|
83
|
+
if (animationClip == null)
|
|
84
|
+
{
|
|
85
|
+
this.LogError("Invalid AnimationClip (null) found at path '{0}', skipping.", path);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
string prefix = animationClip.name;
|
|
90
|
+
string relativePath = path.Substring(animationSourcePath.Length);
|
|
91
|
+
int animIndex = relativePath.LastIndexOf(prefix, StringComparison.OrdinalIgnoreCase);
|
|
92
|
+
if (animIndex < 0)
|
|
93
|
+
{
|
|
94
|
+
this.LogWarn("Unsupported animation at '{0}', expected to be prefixed by '{1}'.", path, prefix);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
string partialPath = relativePath.Substring(0, animIndex);
|
|
99
|
+
string outputPath = _fullDestinationPath + partialPath;
|
|
100
|
+
|
|
101
|
+
if (!Directory.Exists(outputPath))
|
|
102
|
+
{
|
|
103
|
+
_ = Directory.CreateDirectory(outputPath);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
string destination = animationDestinationPath + partialPath + relativePath.Substring(animIndex);
|
|
107
|
+
bool copySuccessful = AssetDatabase.CopyAsset(path, destination);
|
|
108
|
+
if (copySuccessful)
|
|
109
|
+
{
|
|
110
|
+
bool deleteSuccessful = AssetDatabase.DeleteAsset(path);
|
|
111
|
+
if (!deleteSuccessful)
|
|
112
|
+
{
|
|
113
|
+
this.LogError("Failed to delete asset at path '{0}'.", path);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else
|
|
117
|
+
{
|
|
118
|
+
this.LogError("Failed to copy animation from '{0}' to '{1}'.", path, destination);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
#endif
|
|
124
|
+
}
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
public List<Texture2D> frames;
|
|
18
18
|
public int framesPerSecond = DefaultFramesPerSecond;
|
|
19
19
|
public string animationName;
|
|
20
|
-
public string assetPath;
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
public sealed class AnimationCreator : ScriptableWizard
|
|
@@ -41,7 +40,7 @@
|
|
|
41
40
|
AnimationData data = animationData[0];
|
|
42
41
|
|
|
43
42
|
bool filled = false;
|
|
44
|
-
if (data.frames is {Count: 0} && GUILayout.Button("Fill Sprites From Animation Sources"))
|
|
43
|
+
if (data.frames is { Count: 0 } && GUILayout.Button("Fill Sprites From Animation Sources"))
|
|
45
44
|
{
|
|
46
45
|
List<string> animationPaths = new();
|
|
47
46
|
foreach (Object animationSource in animationSources)
|
|
@@ -49,7 +48,7 @@
|
|
|
49
48
|
string assetPath = AssetDatabase.GetAssetPath(animationSource);
|
|
50
49
|
animationPaths.Add(assetPath);
|
|
51
50
|
}
|
|
52
|
-
|
|
51
|
+
|
|
53
52
|
foreach (string assetGuid in AssetDatabase.FindAssets("t:sprite", animationPaths.ToArray()))
|
|
54
53
|
{
|
|
55
54
|
string path = AssetDatabase.GUIDToAssetPath(assetGuid);
|
|
@@ -63,14 +62,15 @@
|
|
|
63
62
|
filled = true;
|
|
64
63
|
}
|
|
65
64
|
|
|
66
|
-
if (data.frames is { Count: > 0} && (filled || GUILayout.Button("Auto Parse Sprites")))
|
|
65
|
+
if (data.frames is { Count: > 0 } && (filled || GUILayout.Button("Auto Parse Sprites")))
|
|
67
66
|
{
|
|
68
67
|
Dictionary<string, Dictionary<string, List<Texture2D>>> texturesByPrefixAndAssetPath = new();
|
|
69
68
|
foreach (Texture2D frame in data.frames)
|
|
70
69
|
{
|
|
71
70
|
string assetPathWithFrameName = AssetDatabase.GetAssetPath(frame);
|
|
72
71
|
string frameName = frame.name;
|
|
73
|
-
string assetPath = assetPathWithFrameName.Substring(
|
|
72
|
+
string assetPath = assetPathWithFrameName.Substring(
|
|
73
|
+
0, assetPathWithFrameName.LastIndexOf(frameName, StringComparison.Ordinal));
|
|
74
74
|
int lastNumericIndex = frameName.Length - 1;
|
|
75
75
|
for (int i = frameName.Length - 1; 0 <= i; --i)
|
|
76
76
|
{
|
|
@@ -87,7 +87,8 @@
|
|
|
87
87
|
: Math.Max(lastUnderscoreIndex, lastNumericIndex);
|
|
88
88
|
if (0 < lastIndex)
|
|
89
89
|
{
|
|
90
|
-
Dictionary<string, List<Texture2D>> texturesByPrefix =
|
|
90
|
+
Dictionary<string, List<Texture2D>> texturesByPrefix =
|
|
91
|
+
texturesByPrefixAndAssetPath.GetOrAdd(assetPath);
|
|
91
92
|
string key = frameName.Substring(0, lastIndex);
|
|
92
93
|
texturesByPrefix.GetOrAdd(key).Add(frame);
|
|
93
94
|
}
|
|
@@ -100,16 +101,16 @@
|
|
|
100
101
|
if (0 < texturesByPrefixAndAssetPath.Count)
|
|
101
102
|
{
|
|
102
103
|
animationData.Clear();
|
|
103
|
-
foreach (KeyValuePair<string, Dictionary<string, List<Texture2D>>> assetPathAndTextures in
|
|
104
|
+
foreach (KeyValuePair<string, Dictionary<string, List<Texture2D>>> assetPathAndTextures in
|
|
105
|
+
texturesByPrefixAndAssetPath)
|
|
104
106
|
{
|
|
105
|
-
string
|
|
106
|
-
|
|
107
|
+
foreach (KeyValuePair<string, List<Texture2D>> textureAndPrefix in assetPathAndTextures
|
|
108
|
+
.Value)
|
|
107
109
|
{
|
|
108
110
|
AnimationData newData = new()
|
|
109
111
|
{
|
|
110
112
|
frames = textureAndPrefix.Value,
|
|
111
113
|
framesPerSecond = data.framesPerSecond,
|
|
112
|
-
assetPath = assetPath,
|
|
113
114
|
animationName = $"Anim_{textureAndPrefix.Key}"
|
|
114
115
|
};
|
|
115
116
|
animationData.Add(newData);
|
|
@@ -165,7 +166,8 @@
|
|
|
165
166
|
int framesPerSecond = data.framesPerSecond;
|
|
166
167
|
if (framesPerSecond <= 0)
|
|
167
168
|
{
|
|
168
|
-
this.LogWarn(
|
|
169
|
+
this.LogWarn(
|
|
170
|
+
"Ignoring animationData with FPS of {0} with name {1}.", framesPerSecond, animationName);
|
|
169
171
|
continue;
|
|
170
172
|
}
|
|
171
173
|
|
|
@@ -205,7 +207,8 @@
|
|
|
205
207
|
animationClip,
|
|
206
208
|
EditorCurveBinding.PPtrCurve("", typeof(SpriteRenderer), "m_Sprite"), keyFrames.ToArray());
|
|
207
209
|
string assetPathWithFileNameAndExtension = AssetDatabase.GetAssetPath(frames[0]);
|
|
208
|
-
string assetPath = assetPathWithFileNameAndExtension.Substring(
|
|
210
|
+
string assetPath = assetPathWithFileNameAndExtension.Substring(
|
|
211
|
+
0, assetPathWithFileNameAndExtension.LastIndexOf("/", StringComparison.Ordinal) + 1);
|
|
209
212
|
|
|
210
213
|
ProjectWindowUtil.CreateAsset(animationClip, assetPath + animationName + ".anim");
|
|
211
214
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
namespace UnityHelpers.Editor.CustomEditors
|
|
2
|
+
{
|
|
3
|
+
#if UNITY_EDITOR
|
|
4
|
+
using Core.Extension;
|
|
5
|
+
using UnityEditor;
|
|
6
|
+
using UnityEngine;
|
|
7
|
+
using UnityHelpers.Utils;
|
|
8
|
+
|
|
9
|
+
[CustomEditor(typeof(MatchColliderToSprite))]
|
|
10
|
+
public sealed class MatchColliderToSpriteEditor : Editor
|
|
11
|
+
{
|
|
12
|
+
public override void OnInspectorGUI()
|
|
13
|
+
{
|
|
14
|
+
base.OnInspectorGUI();
|
|
15
|
+
|
|
16
|
+
MatchColliderToSprite matchColliderToSprite = target as MatchColliderToSprite;
|
|
17
|
+
if (matchColliderToSprite == null)
|
|
18
|
+
{
|
|
19
|
+
this.LogError(
|
|
20
|
+
"Target was of type {0}, expected {1}.", target?.GetType(), typeof(MatchColliderToSprite));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (GUILayout.Button("MatchColliderToSprite"))
|
|
25
|
+
{
|
|
26
|
+
matchColliderToSprite.OnValidate();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
#endif
|
|
31
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
namespace UnityHelpers.Editor
|
|
2
|
+
{
|
|
3
|
+
#if UNITY_EDITOR
|
|
4
|
+
using System;
|
|
5
|
+
using System.Collections.Generic;
|
|
6
|
+
using System.IO;
|
|
7
|
+
using System.Linq;
|
|
8
|
+
using Core.Extension;
|
|
9
|
+
using UnityEditor;
|
|
10
|
+
using UnityEngine;
|
|
11
|
+
using Object = UnityEngine.Object;
|
|
12
|
+
|
|
13
|
+
[Serializable]
|
|
14
|
+
public sealed class SpriteSettings
|
|
15
|
+
{
|
|
16
|
+
public int pixelsPerUnit = 100;
|
|
17
|
+
public Vector2 pivot = new(0.5f, 0.5f);
|
|
18
|
+
public SpriteImportMode spriteMode = SpriteImportMode.Single;
|
|
19
|
+
public bool applyWrapMode = true;
|
|
20
|
+
public TextureWrapMode wrapMode = TextureWrapMode.Clamp;
|
|
21
|
+
public bool applyFilterMode = true;
|
|
22
|
+
public FilterMode filterMode = FilterMode.Point;
|
|
23
|
+
public string name;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public sealed class SpriteSettingsApplier : ScriptableWizard
|
|
27
|
+
{
|
|
28
|
+
public List<Sprite> sprites = new();
|
|
29
|
+
public List<string> spriteFileExtensions = new() { ".png" };
|
|
30
|
+
|
|
31
|
+
[Tooltip(
|
|
32
|
+
"Drag various sprite settings here, where the name property matches a sprite asset name. The first settings with an empty or matching name will be applied to each and every sprite.")]
|
|
33
|
+
public List<SpriteSettings> spriteSettings = new() { new SpriteSettings() };
|
|
34
|
+
|
|
35
|
+
[Tooltip(
|
|
36
|
+
"Drag a folder from Unity here to apply the configuration to all settings under it. No sprites are modified if no directories are provided.")]
|
|
37
|
+
public List<Object> directories = new();
|
|
38
|
+
|
|
39
|
+
[MenuItem("Tools/Unity Helpers/Sprite Settings Applier")]
|
|
40
|
+
public static void CreateAnimation()
|
|
41
|
+
{
|
|
42
|
+
_ = DisplayWizard<SpriteSettingsApplier>("Sprite Settings Directory Applier", "Set");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private void OnWizardCreate()
|
|
46
|
+
{
|
|
47
|
+
HashSet<string> uniqueDirectories = new();
|
|
48
|
+
foreach (Object directory in directories)
|
|
49
|
+
{
|
|
50
|
+
string assetPath = AssetDatabase.GetAssetPath(directory);
|
|
51
|
+
if (Directory.Exists(assetPath))
|
|
52
|
+
{
|
|
53
|
+
_ = uniqueDirectories.Add(assetPath);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
HashSet<string> processedSpritePaths = new();
|
|
58
|
+
Queue<string> directoriesToCheck = new(uniqueDirectories);
|
|
59
|
+
int spriteCount = 0;
|
|
60
|
+
while (0 < directoriesToCheck.Count)
|
|
61
|
+
{
|
|
62
|
+
string directoryPath = directoriesToCheck.Dequeue();
|
|
63
|
+
foreach (string fullFilePath in Directory.EnumerateFiles(directoryPath))
|
|
64
|
+
{
|
|
65
|
+
if (!spriteFileExtensions.Contains(Path.GetExtension(fullFilePath)))
|
|
66
|
+
{
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
int index = fullFilePath.LastIndexOf(directoryPath, StringComparison.OrdinalIgnoreCase);
|
|
71
|
+
if (index < 0)
|
|
72
|
+
{
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
string filePath = fullFilePath.Substring(index);
|
|
77
|
+
if (processedSpritePaths.Add(fullFilePath) && TryUpdateTextureSettings(filePath))
|
|
78
|
+
{
|
|
79
|
+
++spriteCount;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
foreach (string subDirectory in Directory.EnumerateDirectories(directoryPath))
|
|
84
|
+
{
|
|
85
|
+
int index = subDirectory.LastIndexOf(directoryPath, StringComparison.OrdinalIgnoreCase);
|
|
86
|
+
if (index < 0)
|
|
87
|
+
{
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
directoriesToCheck.Enqueue(subDirectory.Substring(index));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
foreach (Sprite sprite in sprites)
|
|
96
|
+
{
|
|
97
|
+
if (sprite == null)
|
|
98
|
+
{
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
string filePath = AssetDatabase.GetAssetPath(sprite);
|
|
103
|
+
if (processedSpritePaths.Add(Application.dataPath + filePath) && TryUpdateTextureSettings(filePath))
|
|
104
|
+
{
|
|
105
|
+
++spriteCount;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
this.Log("Processed {0} sprites.", spriteCount);
|
|
110
|
+
if (0 < spriteCount)
|
|
111
|
+
{
|
|
112
|
+
AssetDatabase.Refresh();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private bool TryUpdateTextureSettings(string filePath)
|
|
117
|
+
{
|
|
118
|
+
TextureImporter textureImporter = AssetImporter.GetAtPath(filePath) as TextureImporter;
|
|
119
|
+
if (textureImporter == null)
|
|
120
|
+
{
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
SpriteSettings spriteData = spriteSettings.FirstOrDefault(
|
|
125
|
+
settings => string.IsNullOrEmpty(settings.name) || filePath.Contains(settings.name));
|
|
126
|
+
if (spriteData == null)
|
|
127
|
+
{
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
textureImporter.spritePivot = spriteData.pivot;
|
|
132
|
+
textureImporter.spritePixelsPerUnit = spriteData.pixelsPerUnit;
|
|
133
|
+
|
|
134
|
+
TextureImporterSettings settings = new();
|
|
135
|
+
textureImporter.ReadTextureSettings(settings);
|
|
136
|
+
settings.spriteAlignment = (int)SpriteAlignment.Custom;
|
|
137
|
+
settings.spriteMode = (int)spriteData.spriteMode;
|
|
138
|
+
if (spriteData.applyWrapMode)
|
|
139
|
+
{
|
|
140
|
+
settings.wrapMode = spriteData.wrapMode;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (spriteData.applyFilterMode)
|
|
144
|
+
{
|
|
145
|
+
settings.filterMode = spriteData.filterMode;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
textureImporter.SetTextureSettings(settings);
|
|
149
|
+
textureImporter.SaveAndReimport();
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
#endif
|
|
154
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
namespace UnityHelpers.Editor
|
|
2
|
+
{
|
|
3
|
+
#if UNITY_EDITOR
|
|
4
|
+
using System;
|
|
5
|
+
using System.Collections.Generic;
|
|
6
|
+
using System.ComponentModel;
|
|
7
|
+
using System.IO;
|
|
8
|
+
using System.Linq;
|
|
9
|
+
using Core.Extension;
|
|
10
|
+
using Core.Helper;
|
|
11
|
+
using UnityEditor;
|
|
12
|
+
using UnityEngine;
|
|
13
|
+
using UnityEngine.Serialization;
|
|
14
|
+
using UnityHelpers.Utils;
|
|
15
|
+
using Utils;
|
|
16
|
+
using Object = UnityEngine.Object;
|
|
17
|
+
|
|
18
|
+
public sealed class TextureResizerWizard : ScriptableWizard
|
|
19
|
+
{
|
|
20
|
+
public enum ResizeAlgorithm
|
|
21
|
+
{
|
|
22
|
+
Bilinear,
|
|
23
|
+
Point
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public List<Texture2D> textures = new();
|
|
27
|
+
|
|
28
|
+
[FormerlySerializedAs("animationSources")]
|
|
29
|
+
[Tooltip(
|
|
30
|
+
"Drag a folder from Unity here to apply the configuration to all textures under it. No textures are modified if no directories are provided.")]
|
|
31
|
+
public List<Object> textureSourcePaths = new();
|
|
32
|
+
|
|
33
|
+
public int numResizes = 1;
|
|
34
|
+
|
|
35
|
+
[Tooltip("Resize algorithm to use for scaling.")]
|
|
36
|
+
public ResizeAlgorithm scalingResizeAlgorithm = ResizeAlgorithm.Bilinear;
|
|
37
|
+
|
|
38
|
+
public int pixelsPerUnit = 100;
|
|
39
|
+
public float widthMultiplier = 0.54f;
|
|
40
|
+
public float heightMultiplier = 0.245f;
|
|
41
|
+
|
|
42
|
+
[MenuItem("Tools/Unity Helpers/Texture Resizer")]
|
|
43
|
+
public static void ResizeTextures()
|
|
44
|
+
{
|
|
45
|
+
_ = DisplayWizard<TextureResizerWizard>("Texture Resizer", "Resize");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private void OnWizardCreate()
|
|
49
|
+
{
|
|
50
|
+
textures ??= new List<Texture2D>();
|
|
51
|
+
textureSourcePaths ??= new List<Object>();
|
|
52
|
+
HashSet<string> animationPaths = new();
|
|
53
|
+
foreach (Object animationSource in textureSourcePaths)
|
|
54
|
+
{
|
|
55
|
+
string assetPath = AssetDatabase.GetAssetPath(animationSource);
|
|
56
|
+
if (!string.IsNullOrEmpty(assetPath))
|
|
57
|
+
{
|
|
58
|
+
_ = animationPaths.Add(assetPath);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (animationPaths.Any())
|
|
63
|
+
{
|
|
64
|
+
foreach (string assetGuid in AssetDatabase.FindAssets("t:texture2D", animationPaths.ToArray()))
|
|
65
|
+
{
|
|
66
|
+
string path = AssetDatabase.GUIDToAssetPath(assetGuid);
|
|
67
|
+
if (string.IsNullOrEmpty(path))
|
|
68
|
+
{
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
|
|
73
|
+
if (texture != null)
|
|
74
|
+
{
|
|
75
|
+
textures.Add(texture);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
textures = textures.Distinct().OrderBy(texture => texture.name).ToList();
|
|
81
|
+
if (textures.Count <= 0 || numResizes <= 0)
|
|
82
|
+
{
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
for (int i = 0; i < numResizes; ++i)
|
|
87
|
+
{
|
|
88
|
+
foreach (Texture2D inputTexture in textures)
|
|
89
|
+
{
|
|
90
|
+
Texture2D texture = inputTexture;
|
|
91
|
+
string assetPath = AssetDatabase.GetAssetPath(texture);
|
|
92
|
+
if (string.IsNullOrEmpty(assetPath))
|
|
93
|
+
{
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
TextureImporter tImporter = AssetImporter.GetAtPath(assetPath) as TextureImporter;
|
|
98
|
+
if (tImporter == null)
|
|
99
|
+
{
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
bool isReadable = tImporter.isReadable;
|
|
104
|
+
if (!isReadable)
|
|
105
|
+
{
|
|
106
|
+
tImporter.isReadable = true;
|
|
107
|
+
tImporter.SaveAndReimport();
|
|
108
|
+
AssetDatabase.Refresh();
|
|
109
|
+
texture = AssetDatabase.LoadAssetAtPath<Texture2D>(assetPath);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
Texture2D copy = Instantiate(texture);
|
|
113
|
+
try
|
|
114
|
+
{
|
|
115
|
+
int extraWidth = (int)Math.Round(copy.width / (pixelsPerUnit * widthMultiplier));
|
|
116
|
+
int extraHeight = (int)Math.Round(
|
|
117
|
+
copy.height / (pixelsPerUnit * heightMultiplier));
|
|
118
|
+
if (extraWidth == 0 && extraHeight == 0)
|
|
119
|
+
{
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
switch (scalingResizeAlgorithm)
|
|
124
|
+
{
|
|
125
|
+
case ResizeAlgorithm.Bilinear:
|
|
126
|
+
TextureScale.Bilinear(copy, copy.width + extraWidth, copy.height + extraHeight);
|
|
127
|
+
break;
|
|
128
|
+
case ResizeAlgorithm.Point:
|
|
129
|
+
TextureScale.Point(copy, copy.width + extraWidth, copy.height + extraHeight);
|
|
130
|
+
break;
|
|
131
|
+
default:
|
|
132
|
+
throw new InvalidEnumArgumentException(
|
|
133
|
+
nameof(scalingResizeAlgorithm), (int)scalingResizeAlgorithm,
|
|
134
|
+
typeof(ResizeAlgorithm));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
byte[] bytes = copy.EncodeToPNG();
|
|
138
|
+
File.WriteAllBytes(assetPath, bytes);
|
|
139
|
+
this.Log(
|
|
140
|
+
"Resized {0} from [{1}x{2}] to [{3}x{4}]", texture.name, texture.width, texture.height,
|
|
141
|
+
copy.width, copy.height);
|
|
142
|
+
}
|
|
143
|
+
catch (Exception e)
|
|
144
|
+
{
|
|
145
|
+
this.LogError("Failed to resize {0}.", e, texture.name);
|
|
146
|
+
}
|
|
147
|
+
finally
|
|
148
|
+
{
|
|
149
|
+
copy.Destroy();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
AssetDatabase.Refresh();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
#endif
|
|
158
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
namespace UnityHelpers.Editor
|
|
2
|
+
{
|
|
3
|
+
#if UNITY_EDITOR
|
|
4
|
+
using System;
|
|
5
|
+
using System.Collections.Generic;
|
|
6
|
+
using System.IO;
|
|
7
|
+
using System.Linq;
|
|
8
|
+
using Core.Extension;
|
|
9
|
+
using UnityEditor;
|
|
10
|
+
using UnityEngine;
|
|
11
|
+
using UnityHelpers.Utils;
|
|
12
|
+
using Object = UnityEngine.Object;
|
|
13
|
+
|
|
14
|
+
public sealed class TextureSettingsApplier : ScriptableWizard
|
|
15
|
+
{
|
|
16
|
+
public bool applyReadOnly = false;
|
|
17
|
+
public bool isReadOnly = false;
|
|
18
|
+
public bool applyMipMaps = false;
|
|
19
|
+
public bool generateMipMaps = false;
|
|
20
|
+
public bool applyWrapMode = false;
|
|
21
|
+
public TextureWrapMode wrapMode = TextureWrapMode.Clamp;
|
|
22
|
+
public bool applyFilterMode = false;
|
|
23
|
+
public FilterMode filterMode = FilterMode.Trilinear;
|
|
24
|
+
public TextureImporterCompression compression = TextureImporterCompression.CompressedHQ;
|
|
25
|
+
public bool useCrunchCompression = true;
|
|
26
|
+
public TextureResizeAlgorithm textureResizeAlgorithm = TextureResizeAlgorithm.Bilinear;
|
|
27
|
+
public int maxTextureSize = SetTextureImportData.MaxTextureSize;
|
|
28
|
+
public TextureImporterFormat textureFormat = TextureImporterFormat.Automatic;
|
|
29
|
+
public List<string> spriteFileExtensions = new() { ".png" };
|
|
30
|
+
|
|
31
|
+
public List<Texture2D> textures = new();
|
|
32
|
+
|
|
33
|
+
[Tooltip(
|
|
34
|
+
"Drag a folder from Unity here to apply the configuration to all settings under it. No sprites are modified if no directories are provided.")]
|
|
35
|
+
public List<Object> directories = new();
|
|
36
|
+
|
|
37
|
+
[MenuItem("Tools/Unity Helpers/Texture Settings Applier")]
|
|
38
|
+
public static void CreateAnimation()
|
|
39
|
+
{
|
|
40
|
+
_ = DisplayWizard<TextureSettingsApplier>("Texture Settings Directory Applier", "Set");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private void OnWizardCreate()
|
|
44
|
+
{
|
|
45
|
+
HashSet<string> uniqueDirectories = new();
|
|
46
|
+
foreach (Object directory in directories)
|
|
47
|
+
{
|
|
48
|
+
string assetPath = AssetDatabase.GetAssetPath(directory);
|
|
49
|
+
if (Directory.Exists(assetPath))
|
|
50
|
+
{
|
|
51
|
+
_ = uniqueDirectories.Add(assetPath);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
int textureCount = 0;
|
|
56
|
+
HashSet<string> processedPaths = new();
|
|
57
|
+
foreach (Texture2D texture in textures?
|
|
58
|
+
.Distinct()
|
|
59
|
+
.OrderBy(texture => texture != null ? texture.name : string.Empty) ??
|
|
60
|
+
Enumerable.Empty<Texture2D>())
|
|
61
|
+
{
|
|
62
|
+
if (texture == null)
|
|
63
|
+
{
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
string assetPath = AssetDatabase.GetAssetPath(texture);
|
|
68
|
+
if (processedPaths.Add(Application.dataPath + assetPath) && TryUpdateTextureSettings(assetPath))
|
|
69
|
+
{
|
|
70
|
+
++textureCount;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
Queue<string> directoriesToCheck = new(uniqueDirectories);
|
|
75
|
+
while (directoriesToCheck.TryDequeue(out string directoryPath))
|
|
76
|
+
{
|
|
77
|
+
foreach (string fullFilePath in Directory.EnumerateFiles(directoryPath))
|
|
78
|
+
{
|
|
79
|
+
if (!spriteFileExtensions.Contains(Path.GetExtension(fullFilePath)))
|
|
80
|
+
{
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
int index = fullFilePath.LastIndexOf(directoryPath, StringComparison.OrdinalIgnoreCase);
|
|
85
|
+
if (index < 0)
|
|
86
|
+
{
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
string filePath = fullFilePath.Substring(index);
|
|
91
|
+
if (processedPaths.Add(fullFilePath) && TryUpdateTextureSettings(filePath))
|
|
92
|
+
{
|
|
93
|
+
++textureCount;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
foreach (string subDirectory in Directory.EnumerateDirectories(directoryPath))
|
|
98
|
+
{
|
|
99
|
+
int index = subDirectory.LastIndexOf(directoryPath, StringComparison.OrdinalIgnoreCase);
|
|
100
|
+
if (index < 0)
|
|
101
|
+
{
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
directoriesToCheck.Enqueue(subDirectory.Substring(index));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
this.Log("Processed {0} textures.", textureCount);
|
|
110
|
+
if (0 < textureCount)
|
|
111
|
+
{
|
|
112
|
+
AssetDatabase.Refresh();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private bool TryUpdateTextureSettings(string filePath)
|
|
117
|
+
{
|
|
118
|
+
TextureImporter textureImporter = AssetImporter.GetAtPath(filePath) as TextureImporter;
|
|
119
|
+
if (textureImporter == null)
|
|
120
|
+
{
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
textureImporter.SetPlatformTextureSettings(
|
|
125
|
+
new TextureImporterPlatformSettings
|
|
126
|
+
{
|
|
127
|
+
resizeAlgorithm = textureResizeAlgorithm,
|
|
128
|
+
maxTextureSize = maxTextureSize,
|
|
129
|
+
format = textureFormat,
|
|
130
|
+
textureCompression = compression,
|
|
131
|
+
crunchedCompression = useCrunchCompression,
|
|
132
|
+
});
|
|
133
|
+
if (applyReadOnly)
|
|
134
|
+
{
|
|
135
|
+
textureImporter.isReadable = !isReadOnly;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (applyMipMaps)
|
|
139
|
+
{
|
|
140
|
+
textureImporter.mipmapEnabled = generateMipMaps;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (applyWrapMode)
|
|
144
|
+
{
|
|
145
|
+
textureImporter.wrapMode = wrapMode;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (applyFilterMode)
|
|
149
|
+
{
|
|
150
|
+
textureImporter.filterMode = filterMode;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
textureImporter.SaveAndReimport();
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
#endif
|
|
158
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
namespace UnityHelpers.Editor.Utils
|
|
2
|
+
{
|
|
3
|
+
#if UNITY_EDITOR
|
|
4
|
+
using System;
|
|
5
|
+
using System.Reflection;
|
|
6
|
+
using UnityEditor;
|
|
7
|
+
|
|
8
|
+
public static class EditorUtilities
|
|
9
|
+
{
|
|
10
|
+
public static string GetCurrentPathOfProjectWindow()
|
|
11
|
+
{
|
|
12
|
+
Type projectWindowUtilType = typeof(ProjectWindowUtil);
|
|
13
|
+
MethodInfo getActiveFolderPath = projectWindowUtilType.GetMethod(
|
|
14
|
+
"GetActiveFolderPath", BindingFlags.Static | BindingFlags.NonPublic);
|
|
15
|
+
object obj = getActiveFolderPath?.Invoke(null, Array.Empty<object>());
|
|
16
|
+
return obj?.ToString() ?? string.Empty;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
#endif
|
|
20
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
namespace UnityHelpers.Editor.Utils
|
|
2
|
+
{
|
|
3
|
+
#if UNITY_EDITOR
|
|
4
|
+
using Core.Attributes;
|
|
5
|
+
using UnityEditor;
|
|
6
|
+
using UnityEngine;
|
|
7
|
+
|
|
8
|
+
// https://www.patrykgalach.com/2020/01/20/readonly-attribute-in-unity-editor/
|
|
9
|
+
[CustomPropertyDrawer(typeof(ReadOnlyAttribute))]
|
|
10
|
+
public sealed class ReadOnlyPropertyDrawer : PropertyDrawer
|
|
11
|
+
{
|
|
12
|
+
public override float GetPropertyHeight(
|
|
13
|
+
SerializedProperty property,
|
|
14
|
+
GUIContent label)
|
|
15
|
+
{
|
|
16
|
+
return EditorGUI.GetPropertyHeight(property, label, true);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
|
20
|
+
{
|
|
21
|
+
bool previousGUIState = GUI.enabled;
|
|
22
|
+
GUI.enabled = false;
|
|
23
|
+
_ = EditorGUI.PropertyField(position, property, label);
|
|
24
|
+
GUI.enabled = previousGUIState;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
#endif
|
|
28
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
namespace UnityHelpers.Utils
|
|
2
|
+
{
|
|
3
|
+
using Core.Attributes;
|
|
4
|
+
using UnityEngine;
|
|
5
|
+
|
|
6
|
+
[DisallowMultipleComponent]
|
|
7
|
+
public sealed class CenterPointOffset : MonoBehaviour
|
|
8
|
+
{
|
|
9
|
+
public Vector2 offset = Vector2.zero;
|
|
10
|
+
|
|
11
|
+
public bool spriteUsesOffset = true;
|
|
12
|
+
|
|
13
|
+
[SiblingComponent]
|
|
14
|
+
private Transform _transform;
|
|
15
|
+
|
|
16
|
+
private void Awake()
|
|
17
|
+
{
|
|
18
|
+
this.AssignSiblingComponents();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public Vector2 CenterPoint
|
|
22
|
+
{
|
|
23
|
+
get
|
|
24
|
+
{
|
|
25
|
+
Vector2 scaledOffset = offset * _transform.localScale;
|
|
26
|
+
return (Vector2)_transform.position + scaledOffset;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
namespace UnityHelpers.Utils
|
|
2
|
+
{
|
|
3
|
+
using System;
|
|
4
|
+
using System.Collections.Generic;
|
|
5
|
+
using Core.Attributes;
|
|
6
|
+
using Core.Extension;
|
|
7
|
+
using UnityEngine;
|
|
8
|
+
|
|
9
|
+
[DisallowMultipleComponent]
|
|
10
|
+
public sealed class MatchColliderToSprite : MonoBehaviour
|
|
11
|
+
{
|
|
12
|
+
[SerializeField]
|
|
13
|
+
[SiblingComponent]
|
|
14
|
+
private SpriteRenderer _spriteRenderer;
|
|
15
|
+
|
|
16
|
+
[SerializeField]
|
|
17
|
+
[SiblingComponent]
|
|
18
|
+
private PolygonCollider2D _collider;
|
|
19
|
+
|
|
20
|
+
private Sprite _lastHandled;
|
|
21
|
+
|
|
22
|
+
private void Awake()
|
|
23
|
+
{
|
|
24
|
+
this.AssignSiblingComponents();
|
|
25
|
+
OnValidate();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private void Update()
|
|
29
|
+
{
|
|
30
|
+
if (_lastHandled == _spriteRenderer.sprite)
|
|
31
|
+
{
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
OnValidate();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Visible for testing
|
|
39
|
+
public void OnValidate()
|
|
40
|
+
{
|
|
41
|
+
if (_spriteRenderer == null)
|
|
42
|
+
{
|
|
43
|
+
_spriteRenderer = GetComponent<SpriteRenderer>();
|
|
44
|
+
if (_spriteRenderer == null)
|
|
45
|
+
{
|
|
46
|
+
this.LogError("No SpriteRenderer detected - cannot match collider shape.");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (_collider == null)
|
|
52
|
+
{
|
|
53
|
+
_collider = GetComponent<PolygonCollider2D>();
|
|
54
|
+
if (_collider == null)
|
|
55
|
+
{
|
|
56
|
+
this.LogError("No PolygonCollider2D detected - cannot match collider shape.");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
_lastHandled = _spriteRenderer.sprite;
|
|
62
|
+
_collider.points = Array.Empty<Vector2>();
|
|
63
|
+
if (_lastHandled == null)
|
|
64
|
+
{
|
|
65
|
+
_collider.pathCount = 0;
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
int physicsShapes = _lastHandled.GetPhysicsShapeCount();
|
|
70
|
+
_collider.pathCount = physicsShapes;
|
|
71
|
+
List<Vector2> buffer = Buffers<Vector2>.List;
|
|
72
|
+
for (int i = 0; i < physicsShapes; ++i)
|
|
73
|
+
{
|
|
74
|
+
buffer.Clear();
|
|
75
|
+
_ = _lastHandled.GetPhysicsShape(i, buffer);
|
|
76
|
+
_collider.SetPath(i, buffer);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
namespace UnityHelpers.Utils
|
|
2
|
+
{
|
|
3
|
+
using UnityEngine;
|
|
4
|
+
|
|
5
|
+
[DisallowMultipleComponent]
|
|
6
|
+
public abstract class RuntimeSingleton<T> : MonoBehaviour where T : RuntimeSingleton<T>
|
|
7
|
+
{
|
|
8
|
+
private static T _instance;
|
|
9
|
+
|
|
10
|
+
protected virtual bool DontDestroyOnLoad => true;
|
|
11
|
+
|
|
12
|
+
public static T Instance
|
|
13
|
+
{
|
|
14
|
+
get
|
|
15
|
+
{
|
|
16
|
+
if (_instance != null)
|
|
17
|
+
{
|
|
18
|
+
return _instance;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
GameObject instance = new(typeof(T).Name + "Singleton", typeof(T));
|
|
22
|
+
_ = instance.TryGetComponent(out _instance);
|
|
23
|
+
if (_instance.DontDestroyOnLoad)
|
|
24
|
+
{
|
|
25
|
+
DontDestroyOnLoad(instance);
|
|
26
|
+
}
|
|
27
|
+
return _instance;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
{
|
|
18
18
|
return;
|
|
19
19
|
}
|
|
20
|
+
|
|
20
21
|
TextureImporter tImporter = AssetImporter.GetAtPath(assetPath) as TextureImporter;
|
|
21
22
|
|
|
22
23
|
if (tImporter == null)
|
|
@@ -42,13 +43,14 @@
|
|
|
42
43
|
{
|
|
43
44
|
return;
|
|
44
45
|
}
|
|
46
|
+
|
|
45
47
|
TextureImporter tImporter = AssetImporter.GetAtPath(assetPath) as TextureImporter;
|
|
46
48
|
|
|
47
49
|
if (tImporter == null)
|
|
48
50
|
{
|
|
49
51
|
return;
|
|
50
52
|
}
|
|
51
|
-
|
|
53
|
+
|
|
52
54
|
tImporter.isReadable = isReadable;
|
|
53
55
|
|
|
54
56
|
TextureImporterPlatformSettings importerSettings = new TextureImporterPlatformSettings
|
|
@@ -64,4 +66,4 @@
|
|
|
64
66
|
# endif
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
|
-
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
namespace UnityHelpers.Utils
|
|
2
|
+
{
|
|
3
|
+
using System.Threading;
|
|
4
|
+
using UnityEngine;
|
|
5
|
+
|
|
6
|
+
// https://answers.unity.com/questions/348163/resize-texture2d-comes-out-grey.html
|
|
7
|
+
// http://wiki.unity3d.com/index.php/TextureScale
|
|
8
|
+
public static class TextureScale
|
|
9
|
+
{
|
|
10
|
+
private sealed class ThreadData
|
|
11
|
+
{
|
|
12
|
+
public readonly int start;
|
|
13
|
+
public readonly int end;
|
|
14
|
+
|
|
15
|
+
public ThreadData(int s, int e)
|
|
16
|
+
{
|
|
17
|
+
start = s;
|
|
18
|
+
end = e;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private static Color[] texColors;
|
|
23
|
+
private static Color[] newColors;
|
|
24
|
+
private static int w;
|
|
25
|
+
private static float ratioX;
|
|
26
|
+
private static float ratioY;
|
|
27
|
+
private static int w2;
|
|
28
|
+
private static int finishCount;
|
|
29
|
+
private static Mutex mutex;
|
|
30
|
+
|
|
31
|
+
public static void Point(Texture2D tex, int newWidth, int newHeight)
|
|
32
|
+
{
|
|
33
|
+
ThreadedScale(tex, newWidth, newHeight, false);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public static void Bilinear(Texture2D tex, int newWidth, int newHeight)
|
|
37
|
+
{
|
|
38
|
+
ThreadedScale(tex, newWidth, newHeight, true);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private static void ThreadedScale(Texture2D tex, int newWidth, int newHeight, bool useBilinear)
|
|
42
|
+
{
|
|
43
|
+
texColors = tex.GetPixels();
|
|
44
|
+
newColors = new Color[newWidth * newHeight];
|
|
45
|
+
if (useBilinear)
|
|
46
|
+
{
|
|
47
|
+
ratioX = 1.0f / ((float)newWidth / (tex.width - 1));
|
|
48
|
+
ratioY = 1.0f / ((float)newHeight / (tex.height - 1));
|
|
49
|
+
}
|
|
50
|
+
else
|
|
51
|
+
{
|
|
52
|
+
ratioX = ((float)tex.width) / newWidth;
|
|
53
|
+
ratioY = ((float)tex.height) / newHeight;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
w = tex.width;
|
|
57
|
+
w2 = newWidth;
|
|
58
|
+
int cores = Mathf.Min(SystemInfo.processorCount, newHeight);
|
|
59
|
+
int slice = newHeight / cores;
|
|
60
|
+
|
|
61
|
+
finishCount = 0;
|
|
62
|
+
mutex ??= new Mutex(false);
|
|
63
|
+
if (1 < cores)
|
|
64
|
+
{
|
|
65
|
+
int i;
|
|
66
|
+
ThreadData threadData;
|
|
67
|
+
for (i = 0; i < cores - 1; i++)
|
|
68
|
+
{
|
|
69
|
+
threadData = new ThreadData(slice * i, slice * (i + 1));
|
|
70
|
+
ParameterizedThreadStart ts = useBilinear ? BilinearScale : PointScale;
|
|
71
|
+
Thread thread = new(ts);
|
|
72
|
+
thread.Start(threadData);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
threadData = new ThreadData(slice * i, newHeight);
|
|
76
|
+
if (useBilinear)
|
|
77
|
+
{
|
|
78
|
+
BilinearScale(threadData);
|
|
79
|
+
}
|
|
80
|
+
else
|
|
81
|
+
{
|
|
82
|
+
PointScale(threadData);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
while (finishCount < cores)
|
|
86
|
+
{
|
|
87
|
+
Thread.Sleep(1);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else
|
|
91
|
+
{
|
|
92
|
+
ThreadData threadData = new(0, newHeight);
|
|
93
|
+
if (useBilinear)
|
|
94
|
+
{
|
|
95
|
+
BilinearScale(threadData);
|
|
96
|
+
}
|
|
97
|
+
else
|
|
98
|
+
{
|
|
99
|
+
PointScale(threadData);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
_ = tex.Reinitialize(newWidth, newHeight);
|
|
104
|
+
tex.SetPixels(newColors);
|
|
105
|
+
tex.Apply();
|
|
106
|
+
|
|
107
|
+
texColors = null;
|
|
108
|
+
newColors = null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private static void BilinearScale(System.Object obj)
|
|
112
|
+
{
|
|
113
|
+
ThreadData threadData = (ThreadData)obj;
|
|
114
|
+
for (int y = threadData.start; y < threadData.end; y++)
|
|
115
|
+
{
|
|
116
|
+
int yFloor = (int)Mathf.Floor(y * ratioY);
|
|
117
|
+
int y1 = yFloor * w;
|
|
118
|
+
int y2 = (yFloor + 1) * w;
|
|
119
|
+
int yw = y * w2;
|
|
120
|
+
|
|
121
|
+
for (var x = 0; x < w2; x++)
|
|
122
|
+
{
|
|
123
|
+
int xFloor = (int)Mathf.Floor(x * ratioX);
|
|
124
|
+
float xLerp = x * ratioX - xFloor;
|
|
125
|
+
newColors[yw + x] = ColorLerpUnclamped(
|
|
126
|
+
ColorLerpUnclamped(texColors[y1 + xFloor], texColors[y1 + xFloor + 1], xLerp),
|
|
127
|
+
ColorLerpUnclamped(texColors[y2 + xFloor], texColors[y2 + xFloor + 1], xLerp),
|
|
128
|
+
y * ratioY - yFloor);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
_ = mutex.WaitOne();
|
|
133
|
+
finishCount++;
|
|
134
|
+
mutex.ReleaseMutex();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private static void PointScale(System.Object obj)
|
|
138
|
+
{
|
|
139
|
+
ThreadData threadData = (ThreadData)obj;
|
|
140
|
+
for (int y = threadData.start; y < threadData.end; y++)
|
|
141
|
+
{
|
|
142
|
+
int thisY = (int)(ratioY * y) * w;
|
|
143
|
+
int yw = y * w2;
|
|
144
|
+
for (var x = 0; x < w2; x++)
|
|
145
|
+
{
|
|
146
|
+
newColors[yw + x] = texColors[(int)(thisY + ratioX * x)];
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
_ = mutex.WaitOne();
|
|
151
|
+
finishCount++;
|
|
152
|
+
mutex.ReleaseMutex();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private static Color ColorLerpUnclamped(Color c1, Color c2, float value)
|
|
156
|
+
{
|
|
157
|
+
return new Color(
|
|
158
|
+
c1.r + (c2.r - c1.r) * value,
|
|
159
|
+
c1.g + (c2.g - c1.g) * value,
|
|
160
|
+
c1.b + (c2.b - c1.b) * value,
|
|
161
|
+
c1.a + (c2.a - c1.a) * value);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|