diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 839c884..c79c6ca 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,9 +7,9 @@ run-name: ๐Ÿงช Test (${{ github.event.pull_request.title || github.ref_name }}) env: # MINIMUM_VERSION: The minimum version of Unity. - MINIMUM_VERSION: 2019.4 + MINIMUM_VERSION: 2020.3 # EXCLUDE_FILTER: The excluded versions of Unity. - EXCLUDE_FILTER: "(2020.2.0|2021.1|2023.3)" + EXCLUDE_FILTER: "(2017|2018|2023.3)" PROJECT_PATH: . on: @@ -46,9 +46,9 @@ jobs: id: setup run: | echo "==== Target Unity Versions ====" - LATEST_VERSIONS=`npx unity-changeset@latest list --versions --latest-patch --min ${MINIMUM_VERSION} --json --all` + LATEST_VERSIONS=`npx unity-changeset@latest list --versions --latest-patch --min ${MINIMUM_VERSION} --json --all --ignore-alpha` if [ "${{ inputs.usePeriodVersions }}" = "true" ]; then - ADDITIONAL_VERSIONS=`npx unity-changeset list --versions --grep '0f' --min ${MINIMUM_VERSION} --json` + ADDITIONAL_VERSIONS=`npx unity-changeset list --versions --grep '0f' --min ${MINIMUM_VERSION} --json --ignore-alpha` else ADDITIONAL_VERSIONS=[] fi @@ -72,11 +72,11 @@ jobs: steps: - name: ๐Ÿšš Checkout ($${{ github.ref }}) if: github.event_name == 'push' - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: ๐Ÿšš Checkout pull request (pull_request_target) if: github.event_name == 'pull_request_target' - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 @@ -90,7 +90,7 @@ jobs: git log --oneline -n 10 - name: ๐Ÿ“ฅ Cache library - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ${{ env.PROJECT_PATH }}/Library key: ${{ env.PROJECT_PATH }}-Library-${{ matrix.unityVersion }}-${{ github.event.pull_request.head.sha || github.sha }} @@ -99,7 +99,7 @@ jobs: ${{ env.PROJECT_PATH }}-Library- - name: ๐Ÿ› ๏ธ Build Unity Project (Test) - uses: game-ci/unity-builder@main + uses: game-ci/unity-builder@v5 timeout-minutes: 45 with: customImage: ghcr.io/mob-sakai/unity3d:${{ matrix.unityVersion }} diff --git a/Packages/src/Editor/AnimatablePropertyEditor.cs b/Packages/src/Editor/AnimatablePropertyEditor.cs index 38bdec9..f3e5de1 100644 --- a/Packages/src/Editor/AnimatablePropertyEditor.cs +++ b/Packages/src/Editor/AnimatablePropertyEditor.cs @@ -78,10 +78,19 @@ namespace Coffee.UIExtensions var mat = mats[j]; if (mat == null || mat.shader == null) continue; +#if UNITY_6000_5_OR_NEWER + for (var i = 0; i < mat.shader.GetPropertyCount(); i++) +#else for (var i = 0; i < ShaderUtil.GetPropertyCount(mat.shader); i++) +#endif { +#if UNITY_6000_5_OR_NEWER + var name = mat.shader.GetPropertyName(i); + var type = (AnimatableProperty.ShaderPropertyType)mat.shader.GetPropertyType(i); +#else var name = ShaderUtil.GetPropertyName(mat.shader, i); var type = (AnimatableProperty.ShaderPropertyType)ShaderUtil.GetPropertyType(mat.shader, i); +#endif if (!s_Names.Add(name)) continue; AddMenu(gm, sp, new ShaderProperty(name, type), true); diff --git a/Packages/src/Editor/UIParticleMenu.cs b/Packages/src/Editor/UIParticleMenu.cs index 256a4f9..283c9ef 100644 --- a/Packages/src/Editor/UIParticleMenu.cs +++ b/Packages/src/Editor/UIParticleMenu.cs @@ -6,11 +6,22 @@ namespace Coffee.UIExtensions { internal class UIParticleMenu { - [MenuItem("GameObject/UI/Particle System (Empty)", false, 2018)] +#if UNITY_6000_5_OR_NEWER + private const string k_MenuPathToCreateParticleSystem = "GameObject/Visual Effects/Particle System"; +#else + private const string k_MenuPathToCreateParticleSystem = "GameObject/Effects/Particle System"; +#endif +#if UNITY_6000_3_OR_NEWER + private const string k_MenuPathForUgui = "GameObject/UI (Canvas)"; +#else + private const string k_MenuPathForUgui = "GameObject/UI"; +#endif + + [MenuItem(k_MenuPathForUgui + "/Particle System (Empty)", false, 2018)] private static void AddParticleEmpty(MenuCommand menuCommand) { // Create empty UI element. - EditorApplication.ExecuteMenuItem("GameObject/UI/Image"); + EditorApplication.ExecuteMenuItem(k_MenuPathForUgui + "/Image"); var ui = Selection.activeGameObject; Object.DestroyImmediate(ui.GetComponent()); @@ -21,7 +32,7 @@ namespace Coffee.UIExtensions uiParticle.rectTransform.sizeDelta = Vector2.zero; } - [MenuItem("GameObject/UI/Particle System", false, 2019)] + [MenuItem(k_MenuPathForUgui + "/Particle System", false, 2019)] private static void AddParticle(MenuCommand menuCommand) { // Create empty UIEffect. @@ -29,7 +40,7 @@ namespace Coffee.UIExtensions var uiParticle = Selection.activeGameObject.GetComponent(); // Create ParticleSystem. - EditorApplication.ExecuteMenuItem("GameObject/Effects/Particle System"); + EditorApplication.ExecuteMenuItem(k_MenuPathToCreateParticleSystem); var ps = Selection.activeGameObject; ps.transform.SetParent(uiParticle.transform, false); ps.transform.localPosition = Vector3.zero; diff --git a/Packages/src/Runtime/AnimatableProperty.cs b/Packages/src/Runtime/AnimatableProperty.cs index 2793395..b0b892a 100644 --- a/Packages/src/Runtime/AnimatableProperty.cs +++ b/Packages/src/Runtime/AnimatableProperty.cs @@ -37,37 +37,17 @@ namespace Coffee.UIExtensions switch (type) { case ShaderPropertyType.Color: - var color = mpb.GetColor(id); - if (color != default) - { - material.SetColor(id, color); - } - + material.SetColor(id, mpb.GetColor(id)); break; case ShaderPropertyType.Vector: - var vector = mpb.GetVector(id); - if (vector != default) - { - material.SetVector(id, vector); - } - + material.SetVector(id, mpb.GetVector(id)); break; case ShaderPropertyType.Float: case ShaderPropertyType.Range: - var value = mpb.GetFloat(id); - if (!Mathf.Approximately(value, 0)) - { - material.SetFloat(id, value); - } - + material.SetFloat(id, mpb.GetFloat(id)); break; case ShaderPropertyType.Texture: - var tex = mpb.GetTexture(id); - if (tex != default(Texture)) - { - material.SetTexture(id, tex); - } - + material.SetTexture(id, mpb.GetTexture(id)); break; } } diff --git a/Packages/src/Runtime/Internal/Extensions/ComponentExtensions.cs b/Packages/src/Runtime/Internal/Extensions/ComponentExtensions.cs index 82314bf..e95c5f6 100644 --- a/Packages/src/Runtime/Internal/Extensions/ComponentExtensions.cs +++ b/Packages/src/Runtime/Internal/Extensions/ComponentExtensions.cs @@ -184,7 +184,7 @@ namespace Coffee.UIParticleInternal /// /// Verify whether it can be converted to the specified component. /// - internal static bool CanConvertTo(this Object context) where T : MonoBehaviour + internal static bool CanConvertTo(this Object context) where T : MonoBehaviour { return context != null && context.GetType() != typeof(T); } diff --git a/Packages/src/Runtime/Internal/ProjectSettings/PreloadedProjectSettings.cs b/Packages/src/Runtime/Internal/ProjectSettings/PreloadedProjectSettings.cs index 2f469cb..fd09928 100644 --- a/Packages/src/Runtime/Internal/ProjectSettings/PreloadedProjectSettings.cs +++ b/Packages/src/Runtime/Internal/ProjectSettings/PreloadedProjectSettings.cs @@ -158,6 +158,8 @@ namespace Coffee.UIParticleInternal private void OnPlayModeStateChanged(PlayModeStateChange state) { + if (!this) return; + switch (state) { case PlayModeStateChange.ExitingEditMode: diff --git a/Packages/src/Runtime/Internal/Utilities/Logging.cs b/Packages/src/Runtime/Internal/Utilities/Logger.cs similarity index 99% rename from Packages/src/Runtime/Internal/Utilities/Logging.cs rename to Packages/src/Runtime/Internal/Utilities/Logger.cs index 12c5b79..ef8593a 100644 --- a/Packages/src/Runtime/Internal/Utilities/Logging.cs +++ b/Packages/src/Runtime/Internal/Utilities/Logger.cs @@ -11,7 +11,7 @@ using Conditional = System.Diagnostics.ConditionalAttribute; namespace Coffee.UIParticleInternal { - internal static class Logging + internal static class Logger { #if !ENABLE_COFFEE_LOGGER private const string k_DisableSymbol = "DISABLE_COFFEE_LOGGER"; diff --git a/Packages/src/Runtime/Internal/Utilities/Logger.cs.meta b/Packages/src/Runtime/Internal/Utilities/Logger.cs.meta new file mode 100644 index 0000000..5ce2425 --- /dev/null +++ b/Packages/src/Runtime/Internal/Utilities/Logger.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4f9f22bb079324476b1473030ad9fec3 \ No newline at end of file diff --git a/Packages/src/Runtime/Internal/Utilities/Logging.cs.meta b/Packages/src/Runtime/Internal/Utilities/Logging.cs.meta deleted file mode 100644 index 6db7856..0000000 --- a/Packages/src/Runtime/Internal/Utilities/Logging.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 8255313895da84e7cbdc876be3795334 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Packages/src/Runtime/Internal/Utilities/Misc.cs b/Packages/src/Runtime/Internal/Utilities/Misc.cs index 58769e9..b6654cc 100644 --- a/Packages/src/Runtime/Internal/Utilities/Misc.cs +++ b/Packages/src/Runtime/Internal/Utilities/Misc.cs @@ -20,7 +20,9 @@ namespace Coffee.UIParticleInternal { public static T[] FindObjectsOfType() where T : Object { -#if UNITY_2023_1_OR_NEWER +#if UNITY_6000_4_OR_NEWER + return Object.FindObjectsByType(FindObjectsInactive.Include); +#elif UNITY_2023_1_OR_NEWER return Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); #else return Object.FindObjectsOfType(); diff --git a/Packages/src/Runtime/Internal/Utilities/ObjectPool.cs b/Packages/src/Runtime/Internal/Utilities/ObjectPool.cs index 141cccf..67c94f4 100644 --- a/Packages/src/Runtime/Internal/Utilities/ObjectPool.cs +++ b/Packages/src/Runtime/Internal/Utilities/ObjectPool.cs @@ -34,7 +34,7 @@ namespace Coffee.UIParticleInternal } // If there are no instances in the pool, create a new one. - Logging.Log(this, $"A new instance is created (pooled: {_pool.CountInactive}, created: {_pool.CountAll})."); + Logger.Log(this, $"A new instance is created (pooled: {_pool.CountInactive}, created: {_pool.CountAll})."); return _pool.Get(); } @@ -47,7 +47,7 @@ namespace Coffee.UIParticleInternal if (instance == null) return; // Ignore if already pooled or null. _pool.Release(instance); - Logging.Log(this, $"An instance is released (pooled: {_pool.CountInactive}, created: {_pool.CountAll})."); + Logger.Log(this, $"An instance is released (pooled: {_pool.CountInactive}, created: {_pool.CountAll})."); instance = default; // Set the reference to null. } #else @@ -80,7 +80,7 @@ namespace Coffee.UIParticleInternal } // If there are no instances in the pool, create a new one. - Logging.Log(this, $"A new instance is created (pooled: {_pool.Count}, created: {++_count})."); + Logger.Log(this, $"A new instance is created (pooled: {_pool.Count}, created: {++_count})."); return _onCreate(); } @@ -94,7 +94,7 @@ namespace Coffee.UIParticleInternal _onReturn(instance); // Return the instance to the pool. _pool.Push(instance); - Logging.Log(this, $"An instance is released (pooled: {_pool.Count}, created: {_count})."); + Logger.Log(this, $"An instance is released (pooled: {_pool.Count}, created: {_count})."); instance = default; // Set the reference to null. } #endif diff --git a/Packages/src/Runtime/Internal/Utilities/ObjectRepository.cs b/Packages/src/Runtime/Internal/Utilities/ObjectRepository.cs index 9797789..037b0d7 100644 --- a/Packages/src/Runtime/Internal/Utilities/ObjectRepository.cs +++ b/Packages/src/Runtime/Internal/Utilities/ObjectRepository.cs @@ -103,7 +103,7 @@ namespace Coffee.UIParticleInternal Release(ref obj); ++entry.reference; obj = entry.storedObject; - Logging.Log(_name, $"Get(total#{count}): {entry}"); + Logger.Log(_name, $"Get(total#{count}): {entry}"); } Profiler.EndSample(); @@ -130,8 +130,8 @@ namespace Coffee.UIParticleInternal newEntry.hash = hash; newEntry.reference = 1; _cache[hash] = newEntry; - _objectKey[newObject.GetInstanceID()] = hash; - Logging.Log(_name, $"Add(total#{count}): {newEntry}"); + _objectKey[newObject.GetHashCode()] = hash; + Logger.Log(_name, $"Add(total#{count}): {newEntry}"); Release(ref obj); obj = newObject; Profiler.EndSample(); @@ -146,7 +146,7 @@ namespace Coffee.UIParticleInternal // Find and release the entry. Profiler.BeginSample("(COF)[ObjectRepository] Release"); - var id = obj.GetInstanceID(); + var id = obj.GetHashCode(); if (_objectKey.TryGetValue(id, out var hash) && _cache.TryGetValue(hash, out var entry)) { @@ -157,12 +157,12 @@ namespace Coffee.UIParticleInternal } else { - Logging.Log(_name, $"Release(total#{_cache.Count}): {entry}"); + Logger.Log(_name, $"Release(total#{_cache.Count}): {entry}"); } } else { - Logging.Log(_name, $"Release(total#{_cache.Count}): Already released: {obj}"); + Logger.Log(_name, $"Release(total#{_cache.Count}): Already released: {obj}"); } obj = null; @@ -175,10 +175,10 @@ namespace Coffee.UIParticleInternal Profiler.BeginSample("(COF)[ObjectRepository] Remove"); _cache.Remove(entry.hash); - _objectKey.Remove(entry.storedObject.GetInstanceID()); + _objectKey.Remove(entry.storedObject.GetHashCode()); _pool.Push(entry); entry.reference = 0; - Logging.Log(_name, $"Remove(total#{_cache.Count}): {entry}"); + Logger.Log(_name, $"Remove(total#{_cache.Count}): {entry}"); entry.Release(_onRelease); Profiler.EndSample(); } diff --git a/Packages/src/Runtime/Internal/Utilities/UIExtraCallbacks.cs b/Packages/src/Runtime/Internal/Utilities/UIExtraCallbacks.cs index 6ad33ed..758536a 100755 --- a/Packages/src/Runtime/Internal/Utilities/UIExtraCallbacks.cs +++ b/Packages/src/Runtime/Internal/Utilities/UIExtraCallbacks.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using UnityEditor; using UnityEngine; using UnityEngine.UI; @@ -20,7 +21,7 @@ namespace Coffee.UIParticleInternal static UIExtraCallbacks() { Canvas.willRenderCanvases += OnBeforeCanvasRebuild; - Logging.LogMulticast(typeof(Canvas), "willRenderCanvases", message: "ctor"); + Logger.LogMulticast(typeof(Canvas), "willRenderCanvases", message: "ctor"); } /// @@ -67,9 +68,17 @@ namespace Coffee.UIParticleInternal if (s_IsInitializedAfterCanvasRebuild) return; s_IsInitializedAfterCanvasRebuild = true; + // Explicitly set `Canvas.willRenderCanvases += CanvasUpdateRegistry.PerformUpdate`. CanvasUpdateRegistry.IsRebuildingLayout(); +#if TMP_ENABLE + // Explicitly set `Canvas.willRenderCanvases += TMP_UpdateManager.DoRebuilds`. + typeof(TMPro.TMP_UpdateManager) + .GetProperty("instance", BindingFlags.NonPublic | BindingFlags.Static) + .GetValue(null); +#endif + Canvas.willRenderCanvases -= OnAfterCanvasRebuild; Canvas.willRenderCanvases += OnAfterCanvasRebuild; - Logging.LogMulticast(typeof(Canvas), "willRenderCanvases", + Logger.LogMulticast(typeof(Canvas), "willRenderCanvases", message: "InitializeAfterCanvasRebuild"); } diff --git a/Packages/src/Runtime/UIParticle.cs b/Packages/src/Runtime/UIParticle.cs index d157ba3..55e5215 100644 --- a/Packages/src/Runtime/UIParticle.cs +++ b/Packages/src/Runtime/UIParticle.cs @@ -386,6 +386,7 @@ namespace Coffee.UIExtensions _isScaleStored = false; UIParticleUpdater.Unregister(this); + _renderers.RemoveAll(r => r == null); _renderers.ForEach(r => r.Reset()); _canvas = null; } diff --git a/Packages/src/Runtime/UIParticleRenderer.cs b/Packages/src/Runtime/UIParticleRenderer.cs index 814c137..f6c0162 100644 --- a/Packages/src/Runtime/UIParticleRenderer.cs +++ b/Packages/src/Runtime/UIParticleRenderer.cs @@ -30,6 +30,7 @@ namespace Coffee.UIExtensions private int _index; private bool _isPrevStored; private bool _isTrail; + private bool _meshCleared; private Bounds _lastBounds; private Material _materialForRendering; private Material _modifiedMaterial; @@ -205,9 +206,9 @@ namespace Coffee.UIExtensions } var hash = new Hash128( - modifiedMaterial ? (uint)modifiedMaterial.GetInstanceID() : 0, - texture ? (uint)texture.GetInstanceID() : 0, - 0 < _parent.m_AnimatableProperties.Length ? (uint)GetInstanceID() : 0, + modifiedMaterial ? (uint)modifiedMaterial.GetHashCode() : 0, + texture ? (uint)texture.GetHashCode() : 0, + 0 < _parent.m_AnimatableProperties.Length ? (uint)GetHashCode() : 0, #if UNITY_EDITOR (uint)EditorJsonUtility.ToJson(modifiedMaterial).GetHashCode() #else @@ -285,10 +286,14 @@ namespace Coffee.UIExtensions || (_isTrail && !_particleSystem.trails.enabled) // Trail, but it is not enabled. ) { + // Skip clearing the mesh if it's already cleared. + if (_meshCleared) return; + Profiler.BeginSample("[UIParticleRenderer] Clear Mesh"); workerMesh.Clear(); canvasRenderer.SetMesh(workerMesh); _lastBounds = new Bounds(); + _meshCleared = true; Profiler.EndSample(); return; @@ -312,6 +317,7 @@ namespace Coffee.UIExtensions // customData.SetVector(ParticleSystemCustomData.Custom2, 3, 0); // } + _meshCleared = false; var main = _particleSystem.main; var scale = GetWorldScale(); var psPos = _particleSystem.transform.position; diff --git a/Packages/src/Runtime/Utilities/ParticleSystemExtensions.cs b/Packages/src/Runtime/Utilities/ParticleSystemExtensions.cs index ee59b7d..292078f 100644 --- a/Packages/src/Runtime/Utilities/ParticleSystemExtensions.cs +++ b/Packages/src/Runtime/Utilities/ParticleSystemExtensions.cs @@ -92,7 +92,7 @@ namespace Coffee.UIParticleInternal if (sortByMaterial) { - return aMat.GetInstanceID() - bMat.GetInstanceID(); + return aMat.GetHashCode() - bMat.GetHashCode(); } if (aMat.renderQueue != bMat.renderQueue) @@ -131,7 +131,7 @@ namespace Coffee.UIParticleInternal { for (var i = 0; i < list.Count; i++) { - if (list[i].GetInstanceID() == ps.GetInstanceID()) + if (list[i].GetHashCode() == ps.GetHashCode()) { return i; }