You've already forked ParticleEffectForUGUI
mirror of
https://github.com/mob-sakai/ParticleEffectForUGUI.git
synced 2026-06-26 08:23:45 +00:00
Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51a4708b7b | ||
|
|
ba79bcca56 | ||
|
|
b9b664a11c | ||
|
|
bc5a1f4435 | ||
|
|
733901436d | ||
|
|
f0b036de65 | ||
|
|
d93cacc70d | ||
|
|
5aff2b480a | ||
|
|
de80249552 | ||
|
|
c0a8e322b1 | ||
|
|
1ac1b347af | ||
|
|
6b956b92e0 | ||
|
|
d03bf19b08 | ||
|
|
00be26d4ce | ||
|
|
34588b6e5c | ||
|
|
c2fb60a988 | ||
|
|
babd2a7275 | ||
|
|
92fb173507 | ||
|
|
298dc9a093 | ||
|
|
143face565 | ||
|
|
319ab5fe06 | ||
|
|
04c1ca72cd | ||
|
|
f2df47aeec | ||
|
|
1dfd756721 | ||
|
|
c54f63cb18 | ||
|
|
7fb6cda06d | ||
|
|
9441f4100c | ||
|
|
ba95fe74ad | ||
|
|
4532e358db | ||
|
|
4b98abd746 | ||
|
|
63ec8f61e3 | ||
|
|
29eebf79fa | ||
|
|
4b6da7c218 | ||
|
|
7ea0a436d1 | ||
|
|
803af8113d | ||
|
|
8b5f7ff57e | ||
|
|
3d0284c630 | ||
|
|
9832485c04 | ||
|
|
201bd9180e | ||
|
|
12d604fedd | ||
|
|
4f42996514 | ||
|
|
a0a2f4aece | ||
|
|
23cd448766 | ||
|
|
1c33dac125 | ||
|
|
f4b28b68b1 | ||
|
|
ff179f0271 | ||
|
|
abdf260352 | ||
|
|
847af6397e | ||
|
|
ac3e147bd9 | ||
|
|
598c85e0f4 | ||
|
|
5f479902aa | ||
|
|
2a66393cb0 |
117
CHANGELOG.md
117
CHANGELOG.md
@@ -1,3 +1,120 @@
|
|||||||
|
# [4.13.0](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.12.2...v4.13.0) (2026-06-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* `UI/Additive` shader does not support RectMask2D softness. ([83145d3](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/83145d3a6ede3831d1f7c49afde6ef7d8a498d21))
|
||||||
|
* rename `UIParticleProjectSettings.enableLinearToGamma` to `autoColorCorrection` ([10f82a8](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/10f82a8579a2d055f897e7a8d6f3b5ba1fc43ae8))
|
||||||
|
* Support for skipping "reload domain" ([b7bb112](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/b7bb1124f6921ebca1b48991462637767b95e504)), closes [#406](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/406)
|
||||||
|
* there is a compilation error in Unity 2019.2 or earlier ([c327632](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/c327632e1eb203351ba1a1087a19eb7838c22530)), closes [#407](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/407)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add fields located outside the `Properties` block in the shader as `Animatable Properties` ([ea2fcfd](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/ea2fcfd8099a24b92624d34600beb519fb9d2b38)), closes [#399](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/399)
|
||||||
|
* preview the ParticleSystem playback when selecting a UIParticle in the Editor ([108bcfb](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/108bcfbd246960dc730546dc334424cf2e17bfd4))
|
||||||
|
* project-wide default view size for baking ([8e0ff10](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/8e0ff10c7344d9f53d2f4f096ddeb7392b67f930)), closes [#360](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/360)
|
||||||
|
|
||||||
|
## [4.12.2](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.12.1...v4.12.2) (2026-06-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add `meshCleared` flag to optimize mesh clearing ([859fa20](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/859fa20d297c3f44e3361f20dbb7ce966407e03e))
|
||||||
|
* fix Unity6.5 compile errors and warnings ([a5ee687](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/a5ee6878212be2fc4d7b48879426f239e8753009)), closes [#400](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/400)
|
||||||
|
* potential access to UIParticleRenderer that has already been destroyed ([b740dd6](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/b740dd662d423c6bef849662ce1b0bfbb4940ed4)), closes [#403](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/403)
|
||||||
|
* updated support for some changed menu paths ([f8ac986](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/f8ac9869f141238169730e74f5d65c4fc6081f51)), closes [#397](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/397)
|
||||||
|
|
||||||
|
## [4.12.1](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.12.0...v4.12.1) (2026-03-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* ignore "EditorOnly" tagged gameObjects on refresh ([031d46a](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/031d46a3216c942d2d1a6ccfadf5f0b9e3ce3006))
|
||||||
|
|
||||||
|
# [4.12.0](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.11.4...v4.12.0) (2026-03-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* explicit null checks ([5384f61](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/5384f61c569e9f78ff9d5b45acfc6f5c2f021a87))
|
||||||
|
|
||||||
|
## [4.11.4](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.11.3...v4.11.4) (2025-12-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add early return for case where subEmitter module is disabled ([d1386a1](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/d1386a12216743a6e09f1b9b87bea1dfcf7702e4))
|
||||||
|
* avoid endless loop ([eb2e862](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/eb2e862e80e549c8cf16ddfed776c101c2413bac)), closes [#392](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/392)
|
||||||
|
|
||||||
|
## [4.11.3](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.11.2...v4.11.3) (2025-10-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix icon ([a9461ec](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/a9461ecb4d40d7fe878e12465d6e38faae7ae65b))
|
||||||
|
* fix URL link in README ([1c8c65d](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/1c8c65d25e7f6fe7b1d20da4461333df8fc7578e)), closes [#376](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/376)
|
||||||
|
* fix: second and subsequent bursts not displayed when world simulation and non-looping ([df2f3ca](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/df2f3caafbe279f1457d74f8183cb561ac14aa17)), closes [#326](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/326)
|
||||||
|
* UIParticle in canvas with 0f-0.01f alpha value does not start to play until alpha value is greater than 0.01f, causes play calls to be delayed unintentionally if canvas alpha value is set to mentioned value range ([38aec2e](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/38aec2ea1afd77677d629c86665a3342d92e49d9))
|
||||||
|
|
||||||
|
## [4.11.2](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.11.1...v4.11.2) (2025-03-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* IL2CPP build fails on older versions of Unity ([0da6525](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/0da652520cd165b43de7404c0b0ab1fbcf9349d1))
|
||||||
|
* NRE on enable ([0cff50e](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/0cff50ef696aa53fb7c46a9a737b7cf3a05b7b9b)), closes [#359](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/359)
|
||||||
|
|
||||||
|
## [4.11.1](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.11.0...v4.11.1) (2025-02-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* component icons will no longer be displayed in the scene view ([6dfbdae](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/6dfbdae38d3822ab9c2c6f0e4ca1ca32ee98a239))
|
||||||
|
|
||||||
|
# [4.11.0](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.7...v4.11.0) (2025-02-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add 'TimeScaleMultiplier' option ([925af0b](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/925af0b6046f65f23a778f67cefa8ff9cbedb513))
|
||||||
|
|
||||||
|
## [4.10.7](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.6...v4.10.7) (2025-01-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* editor crashed on exit play mode (editor, windows) ([47ee45c](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/47ee45cbbe651a8f87ca2b8a3948f8b88db8211e)), closes [#351](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/351)
|
||||||
|
|
||||||
|
## [4.10.6](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.5...v4.10.6) (2025-01-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* sub-emitter particles may not render correctly in certain scenarios ([8276684](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/8276684c3b1646f0490ed64557547ba15281664a)), closes [#348](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/348)
|
||||||
|
* sub-emitter's `inherit velocity` module doubles at runtime ([67de3d1](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/67de3d1bd3e16dc9b564625cb990c53d75769506)), closes [#349](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/349)
|
||||||
|
|
||||||
|
## [4.10.5](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.4...v4.10.5) (2024-12-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* '3D' scale toggle in the inspector does not keep on reload ([934f4b8](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/934f4b8f1c61f8ff20228d0ebcea9f636a3758ed)), closes [#346](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/346)
|
||||||
|
|
||||||
|
## [4.10.4](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.3...v4.10.4) (2024-12-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* rendering issues when playing with opening a prefab stage ([95235a9](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/95235a929b82cf681365ed6eba837d857f83e3d2)), closes [#345](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/345)
|
||||||
|
|
||||||
|
## [4.10.3](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.2...v4.10.3) (2024-11-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* if not configured as a preloaded asset, the project settings asset will be regenerated ([abe0948](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/abe09485f65dd4efd18e74675e752e0213bdf3be)), closes [#342](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/342)
|
||||||
|
|
||||||
## [4.10.2](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.1...v4.10.2) (2024-11-01)
|
## [4.10.2](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.1...v4.10.2) (2024-11-01)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,15 +3,19 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using ShaderPropertyType = Coffee.UIExtensions.AnimatableProperty.ShaderPropertyType;
|
||||||
|
|
||||||
namespace Coffee.UIExtensions
|
namespace Coffee.UIExtensions
|
||||||
{
|
{
|
||||||
internal static class AnimatablePropertyEditor
|
internal static class AnimatablePropertyEditor
|
||||||
{
|
{
|
||||||
private static readonly GUIContent s_ContentNothing = new GUIContent("Nothing");
|
private static readonly GUIContent s_ContentNothing = new GUIContent("Nothing");
|
||||||
|
private static readonly GUIContent s_ContentCustom = new GUIContent("Add Custom...");
|
||||||
private static readonly List<string> s_ActiveNames = new List<string>();
|
private static readonly List<string> s_ActiveNames = new List<string>();
|
||||||
private static readonly StringBuilder s_Sb = new StringBuilder();
|
private static readonly StringBuilder s_Sb = new StringBuilder();
|
||||||
private static readonly HashSet<string> s_Names = new HashSet<string>();
|
private static readonly HashSet<string> s_Names = new HashSet<string>();
|
||||||
|
private static ShaderProperty s_CustomProperty = new ShaderProperty("", ShaderPropertyType.None);
|
||||||
|
private static bool s_ShowCustomProperty = false;
|
||||||
|
|
||||||
private static string CollectActiveNames(SerializedProperty sp, List<string> result)
|
private static string CollectActiveNames(SerializedProperty sp, List<string> result)
|
||||||
{
|
{
|
||||||
@@ -51,8 +55,19 @@ namespace Coffee.UIExtensions
|
|||||||
? "-"
|
? "-"
|
||||||
: CollectActiveNames(sp, s_ActiveNames);
|
: CollectActiveNames(sp, s_ActiveNames);
|
||||||
|
|
||||||
if (!GUI.Button(rect, text, EditorStyles.popup)) return;
|
if (GUI.Button(rect, text, EditorStyles.popup))
|
||||||
|
{
|
||||||
|
ShowMenu(sp, mats);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_ShowCustomProperty)
|
||||||
|
{
|
||||||
|
DrawCustomProperty(sp, ref s_CustomProperty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ShowMenu(SerializedProperty sp, List<Material> mats)
|
||||||
|
{
|
||||||
var gm = new GenericMenu();
|
var gm = new GenericMenu();
|
||||||
gm.AddItem(s_ContentNothing, s_ActiveNames.Count == 0, x =>
|
gm.AddItem(s_ContentNothing, s_ActiveNames.Count == 0, x =>
|
||||||
{
|
{
|
||||||
@@ -61,13 +76,20 @@ namespace Coffee.UIExtensions
|
|||||||
current.serializedObject.ApplyModifiedProperties();
|
current.serializedObject.ApplyModifiedProperties();
|
||||||
}, sp);
|
}, sp);
|
||||||
|
|
||||||
|
gm.AddItem(s_ContentCustom, s_ShowCustomProperty, () =>
|
||||||
|
{
|
||||||
|
s_ShowCustomProperty = !s_ShowCustomProperty;
|
||||||
|
s_CustomProperty.Reset();
|
||||||
|
});
|
||||||
|
gm.AddSeparator("");
|
||||||
|
|
||||||
if (!sp.hasMultipleDifferentValues)
|
if (!sp.hasMultipleDifferentValues)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < sp.arraySize; i++)
|
for (var i = 0; i < sp.arraySize; i++)
|
||||||
{
|
{
|
||||||
var p = sp.GetArrayElementAtIndex(i);
|
var p = sp.GetArrayElementAtIndex(i);
|
||||||
var name = p.FindPropertyRelative("m_Name").stringValue;
|
var name = p.FindPropertyRelative("m_Name").stringValue;
|
||||||
var type = (AnimatableProperty.ShaderPropertyType)p.FindPropertyRelative("m_Type").intValue;
|
var type = (ShaderPropertyType)p.FindPropertyRelative("m_Type").intValue;
|
||||||
AddMenu(gm, sp, new ShaderProperty(name, type), false);
|
AddMenu(gm, sp, new ShaderProperty(name, type), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,17 +98,26 @@ namespace Coffee.UIExtensions
|
|||||||
for (var j = 0; j < mats.Count; j++)
|
for (var j = 0; j < mats.Count; j++)
|
||||||
{
|
{
|
||||||
var mat = mats[j];
|
var mat = mats[j];
|
||||||
if (!mat || !mat.shader) continue;
|
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++)
|
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 = (ShaderPropertyType)mat.shader.GetPropertyType(i);
|
||||||
|
#else
|
||||||
var name = ShaderUtil.GetPropertyName(mat.shader, i);
|
var name = ShaderUtil.GetPropertyName(mat.shader, i);
|
||||||
var type = (AnimatableProperty.ShaderPropertyType)ShaderUtil.GetPropertyType(mat.shader, i);
|
var type = (ShaderPropertyType)ShaderUtil.GetPropertyType(mat.shader, i);
|
||||||
|
#endif
|
||||||
if (!s_Names.Add(name)) continue;
|
if (!s_Names.Add(name)) continue;
|
||||||
|
|
||||||
AddMenu(gm, sp, new ShaderProperty(name, type), true);
|
AddMenu(gm, sp, new ShaderProperty(name, type), true);
|
||||||
|
|
||||||
if (type != AnimatableProperty.ShaderPropertyType.Texture) continue;
|
if (type != ShaderPropertyType.Texture) continue;
|
||||||
|
|
||||||
AddMenu(gm, sp, new ShaderProperty($"{name}_ST"), true);
|
AddMenu(gm, sp, new ShaderProperty($"{name}_ST"), true);
|
||||||
AddMenu(gm, sp, new ShaderProperty($"{name}_HDR"), true);
|
AddMenu(gm, sp, new ShaderProperty($"{name}_HDR"), true);
|
||||||
@@ -102,7 +133,35 @@ namespace Coffee.UIExtensions
|
|||||||
if (add && s_ActiveNames.Contains(prop.name)) return;
|
if (add && s_ActiveNames.Contains(prop.name)) return;
|
||||||
|
|
||||||
var label = new GUIContent($"{prop.name} ({prop.type})");
|
var label = new GUIContent($"{prop.name} ({prop.type})");
|
||||||
menu.AddItem(label, s_ActiveNames.Contains(prop.name), () =>
|
menu.AddItem(label, s_ActiveNames.Contains(prop.name), () => AddProp(sp, prop));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawCustomProperty(SerializedProperty sp, ref ShaderProperty prop)
|
||||||
|
{
|
||||||
|
var r = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight + 8);
|
||||||
|
r.xMin += 60;
|
||||||
|
GUI.Label(r, (Texture)null, EditorStyles.helpBox);
|
||||||
|
|
||||||
|
r = new Rect(r.x + 4, r.y + 4, r.width - 8 - 100 - 16, r.height - 8);
|
||||||
|
prop.name = EditorGUI.TextField(r, prop.name);
|
||||||
|
r.x += r.width + 2;
|
||||||
|
r.width = 100 - 2;
|
||||||
|
prop.type = (ShaderPropertyType)EditorGUI.EnumPopup(r, prop.type);
|
||||||
|
r.x += r.width;
|
||||||
|
r.width = 16;
|
||||||
|
|
||||||
|
EditorGUI.BeginDisabledGroup(!prop.IsValid(s_ActiveNames));
|
||||||
|
if (GUI.Button(r, EditorGUIUtility.IconContent("Toolbar Plus"), EditorStyles.label))
|
||||||
|
{
|
||||||
|
GUI.FocusControl("");
|
||||||
|
AddProp(sp, prop);
|
||||||
|
prop.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddProp(SerializedProperty sp, ShaderProperty prop)
|
||||||
{
|
{
|
||||||
var index = s_ActiveNames.IndexOf(prop.name);
|
var index = s_ActiveNames.IndexOf(prop.name);
|
||||||
if (0 <= index)
|
if (0 <= index)
|
||||||
@@ -118,25 +177,38 @@ namespace Coffee.UIExtensions
|
|||||||
}
|
}
|
||||||
|
|
||||||
sp.serializedObject.ApplyModifiedProperties();
|
sp.serializedObject.ApplyModifiedProperties();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct ShaderProperty
|
private struct ShaderProperty
|
||||||
{
|
{
|
||||||
public readonly string name;
|
public string name;
|
||||||
public readonly AnimatableProperty.ShaderPropertyType type;
|
public ShaderPropertyType type;
|
||||||
|
|
||||||
public ShaderProperty(string name)
|
public ShaderProperty(string name)
|
||||||
{
|
{
|
||||||
this.name = name;
|
this.name = name;
|
||||||
type = AnimatableProperty.ShaderPropertyType.Vector;
|
type = ShaderPropertyType.Vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShaderProperty(string name, AnimatableProperty.ShaderPropertyType type)
|
public ShaderProperty(string name, ShaderPropertyType type)
|
||||||
{
|
{
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
name = "";
|
||||||
|
type = ShaderPropertyType.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValid(List<string> activeNames)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(name)) return false;
|
||||||
|
if (type == ShaderPropertyType.None) return false;
|
||||||
|
if (activeNames.Contains(name)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
@@ -8,12 +9,11 @@ using UnityEngine;
|
|||||||
using UnityEngine.Profiling;
|
using UnityEngine.Profiling;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using Coffee.UIParticleInternal;
|
using Coffee.UIParticleInternal;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
#if UNITY_2021_2_OR_NEWER
|
#if UNITY_2021_2_OR_NEWER
|
||||||
using UnityEditor.Overlays;
|
using UnityEditor.Overlays;
|
||||||
#else
|
#else
|
||||||
using System;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Object = UnityEngine.Object;
|
|
||||||
#endif
|
#endif
|
||||||
#if UNITY_2021_2_OR_NEWER
|
#if UNITY_2021_2_OR_NEWER
|
||||||
using UnityEditor.SceneManagement;
|
using UnityEditor.SceneManagement;
|
||||||
@@ -28,6 +28,11 @@ namespace Coffee.UIExtensions
|
|||||||
[CanEditMultipleObjects]
|
[CanEditMultipleObjects]
|
||||||
internal class UIParticleEditor : GraphicEditor
|
internal class UIParticleEditor : GraphicEditor
|
||||||
{
|
{
|
||||||
|
internal class State : ScriptableSingleton<State>
|
||||||
|
{
|
||||||
|
public bool is3DScaleMode;
|
||||||
|
}
|
||||||
|
|
||||||
//################################
|
//################################
|
||||||
// Constant or Static Members.
|
// Constant or Static Members.
|
||||||
//################################
|
//################################
|
||||||
@@ -44,9 +49,9 @@ namespace Coffee.UIExtensions
|
|||||||
private static readonly GUIContent s_ContentRandom = new GUIContent("Random");
|
private static readonly GUIContent s_ContentRandom = new GUIContent("Random");
|
||||||
private static readonly GUIContent s_ContentScale = new GUIContent("Scale");
|
private static readonly GUIContent s_ContentScale = new GUIContent("Scale");
|
||||||
private static readonly GUIContent s_ContentPrimary = new GUIContent("Primary");
|
private static readonly GUIContent s_ContentPrimary = new GUIContent("Primary");
|
||||||
|
private static readonly GUIContent s_ViewSize = new GUIContent("View Size");
|
||||||
private static readonly Regex s_RegexBuiltInGuid = new Regex(@"^0{16}.0{15}$", RegexOptions.Compiled);
|
private static readonly Regex s_RegexBuiltInGuid = new Regex(@"^0{16}.0{15}$", RegexOptions.Compiled);
|
||||||
private static readonly List<Material> s_TempMaterials = new List<Material>();
|
private static readonly List<Material> s_TempMaterials = new List<Material>();
|
||||||
private static bool s_XYZMode;
|
|
||||||
|
|
||||||
private SerializedProperty _maskable;
|
private SerializedProperty _maskable;
|
||||||
private SerializedProperty _scale3D;
|
private SerializedProperty _scale3D;
|
||||||
@@ -58,8 +63,11 @@ namespace Coffee.UIExtensions
|
|||||||
private SerializedProperty _autoScalingMode;
|
private SerializedProperty _autoScalingMode;
|
||||||
private SerializedProperty _useCustomView;
|
private SerializedProperty _useCustomView;
|
||||||
private SerializedProperty _customViewSize;
|
private SerializedProperty _customViewSize;
|
||||||
|
private SerializedProperty _timeScaleMultiplier;
|
||||||
private ReorderableList _ro;
|
private ReorderableList _ro;
|
||||||
private bool _showMax;
|
private bool _showMax;
|
||||||
|
private bool _is3DScaleMode;
|
||||||
|
private GameObject[] _gameObjects;
|
||||||
|
|
||||||
private static readonly HashSet<Shader> s_Shaders = new HashSet<Shader>();
|
private static readonly HashSet<Shader> s_Shaders = new HashSet<Shader>();
|
||||||
#if UNITY_2018 || UNITY_2019
|
#if UNITY_2018 || UNITY_2019
|
||||||
@@ -95,6 +103,7 @@ namespace Coffee.UIExtensions
|
|||||||
_autoScalingMode = serializedObject.FindProperty("m_AutoScalingMode");
|
_autoScalingMode = serializedObject.FindProperty("m_AutoScalingMode");
|
||||||
_useCustomView = serializedObject.FindProperty("m_UseCustomView");
|
_useCustomView = serializedObject.FindProperty("m_UseCustomView");
|
||||||
_customViewSize = serializedObject.FindProperty("m_CustomViewSize");
|
_customViewSize = serializedObject.FindProperty("m_CustomViewSize");
|
||||||
|
_timeScaleMultiplier = serializedObject.FindProperty("m_TimeScaleMultiplier");
|
||||||
|
|
||||||
var sp = serializedObject.FindProperty("m_Particles");
|
var sp = serializedObject.FindProperty("m_Particles");
|
||||||
_ro = new ReorderableList(sp.serializedObject, sp, true, true, true, true)
|
_ro = new ReorderableList(sp.serializedObject, sp, true, true, true, true)
|
||||||
@@ -103,7 +112,7 @@ namespace Coffee.UIExtensions
|
|||||||
{
|
{
|
||||||
var ps = sp.GetArrayElementAtIndex(index).objectReferenceValue as ParticleSystem;
|
var ps = sp.GetArrayElementAtIndex(index).objectReferenceValue as ParticleSystem;
|
||||||
var materialCount = 0;
|
var materialCount = 0;
|
||||||
if (ps && ps.TryGetComponent<ParticleSystemRenderer>(out var psr))
|
if (ps != null && ps.TryGetComponent<ParticleSystemRenderer>(out var psr))
|
||||||
{
|
{
|
||||||
materialCount = psr.sharedMaterials.Length;
|
materialCount = psr.sharedMaterials.Length;
|
||||||
}
|
}
|
||||||
@@ -117,7 +126,7 @@ namespace Coffee.UIExtensions
|
|||||||
var p = sp.GetArrayElementAtIndex(index);
|
var p = sp.GetArrayElementAtIndex(index);
|
||||||
EditorGUI.ObjectField(rect, p, GUIContent.none);
|
EditorGUI.ObjectField(rect, p, GUIContent.none);
|
||||||
var ps = p.objectReferenceValue as ParticleSystem;
|
var ps = p.objectReferenceValue as ParticleSystem;
|
||||||
if (!ps || !ps.TryGetComponent<ParticleSystemRenderer>(out var psr)) return;
|
if (ps == null || !ps.TryGetComponent<ParticleSystemRenderer>(out var psr)) return;
|
||||||
|
|
||||||
rect.x += 15;
|
rect.x += 15;
|
||||||
rect.width -= 15;
|
rect.width -= 15;
|
||||||
@@ -163,6 +172,26 @@ namespace Coffee.UIExtensions
|
|||||||
uip.RefreshParticles(uip.particles);
|
uip.RefreshParticles(uip.particles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize 3D scale mode.
|
||||||
|
_is3DScaleMode = State.instance.is3DScaleMode;
|
||||||
|
if (!_is3DScaleMode)
|
||||||
|
{
|
||||||
|
var x = _scale3D.FindPropertyRelative("x");
|
||||||
|
var y = _scale3D.FindPropertyRelative("y");
|
||||||
|
var z = _scale3D.FindPropertyRelative("z");
|
||||||
|
_is3DScaleMode = !Mathf.Approximately(x.floatValue, y.floatValue) ||
|
||||||
|
!Mathf.Approximately(y.floatValue, z.floatValue) ||
|
||||||
|
y.hasMultipleDifferentValues ||
|
||||||
|
z.hasMultipleDifferentValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add temporary ParticleSystem for preview if enabled.
|
||||||
|
if (UIParticleProjectSettings.previewOnSelect)
|
||||||
|
{
|
||||||
|
_gameObjects = targets.OfType<UIParticle>().Select(x => x.gameObject).ToArray();
|
||||||
|
ParticleSystemPreviewSystem.Register(_gameObjects);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -171,17 +200,24 @@ namespace Coffee.UIExtensions
|
|||||||
public override void OnInspectorGUI()
|
public override void OnInspectorGUI()
|
||||||
{
|
{
|
||||||
var current = target as UIParticle;
|
var current = target as UIParticle;
|
||||||
if (!current) return;
|
if (current == null) return;
|
||||||
|
|
||||||
Profiler.BeginSample("(UIP:E) OnInspectorGUI");
|
Profiler.BeginSample("(UIP:E) OnInspectorGUI");
|
||||||
serializedObject.Update();
|
serializedObject.Update();
|
||||||
|
|
||||||
// Maskable
|
// Maskable
|
||||||
|
if (_maskable != null)
|
||||||
|
{
|
||||||
EditorGUILayout.PropertyField(_maskable);
|
EditorGUILayout.PropertyField(_maskable);
|
||||||
|
}
|
||||||
|
|
||||||
// Scale
|
// Scale
|
||||||
EditorGUI.BeginDisabledGroup(!_meshSharing.hasMultipleDifferentValues && _meshSharing.intValue == 4);
|
EditorGUI.BeginDisabledGroup(!_meshSharing.hasMultipleDifferentValues && _meshSharing.intValue == 4);
|
||||||
s_XYZMode = DrawFloatOrVector3Field(_scale3D, s_XYZMode);
|
if (DrawFloatOrVector3Field(_scale3D, _is3DScaleMode) != _is3DScaleMode)
|
||||||
|
{
|
||||||
|
State.instance.is3DScaleMode = _is3DScaleMode = !_is3DScaleMode;
|
||||||
|
}
|
||||||
|
|
||||||
EditorGUI.EndDisabledGroup();
|
EditorGUI.EndDisabledGroup();
|
||||||
|
|
||||||
// AnimatableProperties
|
// AnimatableProperties
|
||||||
@@ -211,16 +247,27 @@ namespace Coffee.UIExtensions
|
|||||||
|
|
||||||
// Custom View Size
|
// Custom View Size
|
||||||
EditorGUILayout.PropertyField(_useCustomView);
|
EditorGUILayout.PropertyField(_useCustomView);
|
||||||
EditorGUI.BeginChangeCheck();
|
|
||||||
EditorGUI.BeginDisabledGroup(!_useCustomView.boolValue);
|
|
||||||
EditorGUI.indentLevel++;
|
EditorGUI.indentLevel++;
|
||||||
|
if (_useCustomView.boolValue)
|
||||||
|
{
|
||||||
|
EditorGUI.BeginChangeCheck();
|
||||||
EditorGUILayout.PropertyField(_customViewSize);
|
EditorGUILayout.PropertyField(_customViewSize);
|
||||||
EditorGUI.indentLevel--;
|
|
||||||
EditorGUI.EndDisabledGroup();
|
|
||||||
if (EditorGUI.EndChangeCheck())
|
if (EditorGUI.EndChangeCheck())
|
||||||
{
|
{
|
||||||
_customViewSize.floatValue = Mathf.Max(0.1f, _customViewSize.floatValue);
|
_customViewSize.floatValue = Mathf.Max(0.1f, _customViewSize.floatValue);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorGUI.BeginDisabledGroup(!_useCustomView.boolValue);
|
||||||
|
EditorGUILayout.FloatField(s_ViewSize, UIParticleProjectSettings.defaultViewSizeForBaking);
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUI.indentLevel--;
|
||||||
|
|
||||||
|
// Time Scale Multiplier
|
||||||
|
EditorGUILayout.PropertyField(_timeScaleMultiplier);
|
||||||
|
|
||||||
// Target ParticleSystems.
|
// Target ParticleSystems.
|
||||||
EditorGUI.BeginChangeCheck();
|
EditorGUI.BeginChangeCheck();
|
||||||
@@ -240,7 +287,7 @@ namespace Coffee.UIExtensions
|
|||||||
Profiler.BeginSample("(UIP:E) Non-UI built-in shader is not supported.");
|
Profiler.BeginSample("(UIP:E) Non-UI built-in shader is not supported.");
|
||||||
foreach (var mat in s_TempMaterials)
|
foreach (var mat in s_TempMaterials)
|
||||||
{
|
{
|
||||||
if (!mat || !mat.shader) continue;
|
if (mat == null || mat.shader == null) continue;
|
||||||
var shader = mat.shader;
|
var shader = mat.shader;
|
||||||
if (IsBuiltInObject(shader) && !shader.name.StartsWith("UI/"))
|
if (IsBuiltInObject(shader) && !shader.name.StartsWith("UI/"))
|
||||||
{
|
{
|
||||||
@@ -259,7 +306,7 @@ namespace Coffee.UIExtensions
|
|||||||
{
|
{
|
||||||
foreach (var mat in s_TempMaterials)
|
foreach (var mat in s_TempMaterials)
|
||||||
{
|
{
|
||||||
if (!mat || !mat.shader) continue;
|
if (mat == null || mat.shader == null) continue;
|
||||||
var shader = mat.shader;
|
var shader = mat.shader;
|
||||||
if (!s_Shaders.Add(shader)) continue;
|
if (!s_Shaders.Add(shader)) continue;
|
||||||
|
|
||||||
@@ -317,6 +364,10 @@ namespace Coffee.UIExtensions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Remove the temporary ParticleSystem.
|
||||||
|
ParticleSystemPreviewSystem.DrawWarningForTemporary(_gameObjects);
|
||||||
|
|
||||||
Profiler.EndSample();
|
Profiler.EndSample();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,7 +506,7 @@ namespace Coffee.UIExtensions
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool FixButton(bool show, string text)
|
private static bool FixButton(bool show, string text, GUIContent buttonText = null)
|
||||||
{
|
{
|
||||||
if (!show) return false;
|
if (!show) return false;
|
||||||
using (new EditorGUILayout.HorizontalScope(GUILayout.ExpandWidth(true)))
|
using (new EditorGUILayout.HorizontalScope(GUILayout.ExpandWidth(true)))
|
||||||
@@ -463,7 +514,7 @@ namespace Coffee.UIExtensions
|
|||||||
EditorGUILayout.HelpBox(text, MessageType.Warning, true);
|
EditorGUILayout.HelpBox(text, MessageType.Warning, true);
|
||||||
using (new EditorGUILayout.VerticalScope())
|
using (new EditorGUILayout.VerticalScope())
|
||||||
{
|
{
|
||||||
return GUILayout.Button(s_ContentFix, GUILayout.Width(30));
|
return GUILayout.Button(buttonText ?? s_ContentFix, GUILayout.ExpandWidth(false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 418 B After Width: | Height: | Size: 418 B |
@@ -6,11 +6,22 @@ namespace Coffee.UIExtensions
|
|||||||
{
|
{
|
||||||
internal class UIParticleMenu
|
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)
|
private static void AddParticleEmpty(MenuCommand menuCommand)
|
||||||
{
|
{
|
||||||
// Create empty UI element.
|
// Create empty UI element.
|
||||||
EditorApplication.ExecuteMenuItem("GameObject/UI/Image");
|
EditorApplication.ExecuteMenuItem(k_MenuPathForUgui + "/Image");
|
||||||
var ui = Selection.activeGameObject;
|
var ui = Selection.activeGameObject;
|
||||||
Object.DestroyImmediate(ui.GetComponent<Image>());
|
Object.DestroyImmediate(ui.GetComponent<Image>());
|
||||||
|
|
||||||
@@ -21,7 +32,7 @@ namespace Coffee.UIExtensions
|
|||||||
uiParticle.rectTransform.sizeDelta = Vector2.zero;
|
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)
|
private static void AddParticle(MenuCommand menuCommand)
|
||||||
{
|
{
|
||||||
// Create empty UIEffect.
|
// Create empty UIEffect.
|
||||||
@@ -29,7 +40,7 @@ namespace Coffee.UIExtensions
|
|||||||
var uiParticle = Selection.activeGameObject.GetComponent<UIParticle>();
|
var uiParticle = Selection.activeGameObject.GetComponent<UIParticle>();
|
||||||
|
|
||||||
// Create ParticleSystem.
|
// Create ParticleSystem.
|
||||||
EditorApplication.ExecuteMenuItem("GameObject/Effects/Particle System");
|
EditorApplication.ExecuteMenuItem(k_MenuPathToCreateParticleSystem);
|
||||||
var ps = Selection.activeGameObject;
|
var ps = Selection.activeGameObject;
|
||||||
ps.transform.SetParent(uiParticle.transform, false);
|
ps.transform.SetParent(uiParticle.transform, false);
|
||||||
ps.transform.localPosition = Vector3.zero;
|
ps.transform.localPosition = Vector3.zero;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Copyright 2018-2024 mob-sakai
|
Copyright 2018-2026 mob-sakai
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
|||||||
72
README.md
72
README.md
@@ -144,7 +144,7 @@ _This package requires **Unity 2018.3 or later**._
|
|||||||
|
|
||||||
#### Install as Embedded Package
|
#### Install as Embedded Package
|
||||||
|
|
||||||
1. Download a source code zip file from [Releases](https://github.com/mob-sakai/ParticleEffectForUGUI.git/releases) and extract it.
|
1. Download a source code zip file from [Releases](https://github.com/mob-sakai/ParticleEffectForUGUI/releases) and extract it.
|
||||||
2. Place it in your project's `Packages` directory.
|
2. Place it in your project's `Packages` directory.
|
||||||

|

|
||||||
- If you want to fix bugs or add features, install it as an embedded package.
|
- If you want to fix bugs or add features, install it as an embedded package.
|
||||||
@@ -158,12 +158,18 @@ _This package requires **Unity 2018.3 or later**._
|
|||||||
|
|
||||||
`UIParticle` controls the ParticleSystems that are attached to its own game objects and child game objects.
|
`UIParticle` controls the ParticleSystems that are attached to its own game objects and child game objects.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
- **Maskable**: Does this graphic allow maskable.
|
- **Maskable**: Does this graphic allow maskable.
|
||||||
- **Scale**: Scale the rendering particles. When the `3D` toggle is enabled, 3D scale (x, y, z) is supported.
|
- **Scale**: Scale the rendering particles. When the `3D` toggle is enabled, 3D scale (x, y, z) is supported.
|
||||||
- **Animatable Properties**: If you want to update material properties (e.g., `_MainTex_ST`, `_Color`) in AnimationClip,
|
- **Animatable Properties**: If you want to update material properties (e.g., `_MainTex_ST`, `_Color`) in AnimationClip,
|
||||||
use this to mark as animatable.
|
use this to mark as animatable.
|
||||||
|
|
||||||
|
> [!TIPS]
|
||||||
|
> (Unity 2021.1 or later) **Custom Animatable Properties**
|
||||||
|
> From the `Add Custom...` menu in `Animatable Properties`, you can add fields located outside the `Properties` block in the shader as `Animatable Properties`.
|
||||||
|
> This allows `Matrix`, `Matrix Array`, `Float Array`, and `Vector Array` values modified by the `ParticleSystemRenderer.SetPropertyBlock` method to be automatically reflected in UIParticle.
|
||||||
|
|
||||||
- **Mesh Sharing**: Particle simulation results are shared within the same group. A large number of the same effects can
|
- **Mesh Sharing**: Particle simulation results are shared within the same group. A large number of the same effects can
|
||||||
be displayed with a small load. When the `Random` toggle is enabled, it will be grouped randomly.
|
be displayed with a small load. When the `Random` toggle is enabled, it will be grouped randomly.
|
||||||
- **None:** Disable mesh sharing.
|
- **None:** Disable mesh sharing.
|
||||||
@@ -180,11 +186,17 @@ _This package requires **Unity 2018.3 or later**._
|
|||||||
- **UIParticle:** UIParticle.scale will be adjusted.
|
- **UIParticle:** UIParticle.scale will be adjusted.
|
||||||
- **Use Custom View:** Use this if the particles are not displayed correctly due to min/max particle size.
|
- **Use Custom View:** Use this if the particles are not displayed correctly due to min/max particle size.
|
||||||
- **Custom view size:** Change the bake view size.
|
- **Custom view size:** Change the bake view size.
|
||||||
|
- **Time Scale Multiplier:** Time scale multiplier.
|
||||||
- **Rendering Order**: The ParticleSystem list to be rendered. You can change the order and the materials.
|
- **Rendering Order**: The ParticleSystem list to be rendered. You can change the order and the materials.
|
||||||
|
|
||||||
**NOTE:** Press the `Refresh` button to reconstruct the rendering order based on children ParticleSystem's sorting order
|
> [!TIPS]
|
||||||
|
> Press the `Refresh` button to reconstruct the rendering order based on children ParticleSystem's sorting order
|
||||||
and z-position.
|
and z-position.
|
||||||
|
|
||||||
|
> [!TIPS]
|
||||||
|
> When a `GameObject` with this component is selected in the editor, a temporary `ParticleSystem` is added if needed so you can preview the effect in the Scene view.
|
||||||
|
> The generated `ParticleSystem` is marked with `HideFlags.DontSave`, so it is neither saved nor included in builds.
|
||||||
|
|
||||||
<br><br>
|
<br><br>
|
||||||
|
|
||||||
### Basic Usage
|
### Basic Usage
|
||||||
@@ -194,6 +206,10 @@ and z-position.
|
|||||||
2. Adjust the ParticleSystem as you like.
|
2. Adjust the ParticleSystem as you like.
|
||||||

|

|
||||||
|
|
||||||
|
> [!Tips]
|
||||||
|
> Adding a `UIParticle` to the parent is the recommended setup rather than attaching it directly to the `ParticleSystem`.
|
||||||
|
> When using `ParticleSystem.emission.rateOverDistance`, it is recommended to move the transform of `UIParticle` rather than the `ParticleSystem`.
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
### Usage with Your Existing ParticleSystem Prefab
|
### Usage with Your Existing ParticleSystem Prefab
|
||||||
@@ -210,7 +226,7 @@ and z-position.
|
|||||||
If you want to mask particles, set a stencil-supported shader (such as `UI/UIAdditive`) to the material for
|
If you want to mask particles, set a stencil-supported shader (such as `UI/UIAdditive`) to the material for
|
||||||
ParticleSystem.
|
ParticleSystem.
|
||||||
If you use some custom shaders, see
|
If you use some custom shaders, see
|
||||||
the [How to Make a Custom Shader to Support Mask/RectMask2D Component](#how-to-make-a-custom-shader-to-support-maskrectmask2d-component)
|
the [How to Make a Custom Shader to Support Mask/RectMask2D Component](#how-to-make-a-custom-shader-to-support-mask-and-rectmask2d-component)
|
||||||
section.
|
section.
|
||||||
|
|
||||||

|

|
||||||
@@ -255,13 +271,35 @@ uiParticle.Stop();
|
|||||||
- **Unscaled Time:** Update with unscaled delta time.
|
- **Unscaled Time:** Update with unscaled delta time.
|
||||||
- **OnAttracted**: An event called when attracting is complete (per particle).
|
- **OnAttracted**: An event called when attracting is complete (per particle).
|
||||||
|
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
### Component: ParticleSystemPreviewer
|
||||||
|
|
||||||
|
`ParticleSystemPreviewer` is used to preview a ParticleSystem in the editor.
|
||||||
|
|
||||||
|
- When a `GameObject` with this component is selected in the editor, a temporary `ParticleSystem` is added if needed so you can preview the effect in the Scene view.
|
||||||
|
- The generated `ParticleSystem` is marked with `HideFlags.DontSave`, so it is neither saved nor included in builds.
|
||||||
|
|
||||||
|
|
||||||
<br><br>
|
<br><br>
|
||||||
|
|
||||||
### Project Settings
|
### Project Settings
|
||||||
|
|
||||||

|
You can adjust the project-wide settings for `UI Particle`. (`Edit > Project Settings > UI > UI Particle`)
|
||||||
|
|
||||||
- Click `Edit > Project Settings` to open the Project Settings window and then select `UI > UI Particle` category.
|

|
||||||
|
|
||||||
|
#### Settings
|
||||||
|
|
||||||
|
- **Enable Linear To Gamma**: Automatically correct the color space of the mesh.
|
||||||
|
- **Default View Size For Baking**: Default view size for baking particle systems.
|
||||||
|
|
||||||
|
#### Editor
|
||||||
|
|
||||||
|
- **Hide Generated Component**: Automatically hide the generated `UIParticleRenderer` component and `UIParticle BakingCamera`.
|
||||||
|
|
||||||
|
- **Preview On Select**: When selecting UIParticle, a temporary ParticleSystem is generated for preview.
|
||||||
|
|
||||||
<br><br>
|
<br><br>
|
||||||
|
|
||||||
@@ -326,33 +364,39 @@ uiParticle.Stop();
|
|||||||
|
|
||||||
### 🔍 FAQ: Why Are My UIParticles Not Displayed Correctly?
|
### 🔍 FAQ: Why Are My UIParticles Not Displayed Correctly?
|
||||||
|
|
||||||
|
See [this issue](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/270).
|
||||||
|
|
||||||
If `ParticleSystem` alone displays particles correctly but `UIParticle` does not, please check the following points:
|
If `ParticleSystem` alone displays particles correctly but `UIParticle` does not, please check the following points:
|
||||||
|
|
||||||
- [Shader Limitation](#shader-limitation)
|
- [Shader Limitation](https://github.com/mob-sakai/ParticleEffectForUGUI#shader-limitation)
|
||||||
- `UIParticle` does not support all built-in shaders except for `UI/Default`.
|
|
||||||
- Most cases can be solved by using `UI/Additive` or `UI/Default`.
|
- Most cases can be solved by using `UI/Additive` or `UI/Default`.
|
||||||
- Particles are not masked
|
- Particles are not masked
|
||||||
- `UIParticle` is maskable.
|
- `UIParticle` is maskable.
|
||||||
|
- Use maskable/clipable shader (such as `UI/Additive` or `UI/Default`)
|
||||||
- Set `Mask` or `RectMask2D` component properly.
|
- Set `Mask` or `RectMask2D` component properly.
|
||||||
- [Use maskable/clipable shader](#how-to-make-a-custom-shader-to-support-maskrectmask2d-component) (such
|
|
||||||
as `UI/Additive` or `UI/Default`)
|
|
||||||
- Particles are too small
|
- Particles are too small
|
||||||
- If particles are small enough, they will not appear on the screen.
|
- If particles are small enough, they will not appear on the screen.
|
||||||
- Increase the `Scale` value.
|
- Increase the `Scale` value.
|
||||||
- If you don't want to change the apparent size depending on the resolution, try the `Auto Scaling` option.
|
- If you don't want to change the apparent size depending on the resolution, try the `Auto Scaling` option.
|
||||||
- Particles are too many
|
- Particles are too many
|
||||||
- No more than 65535 vertices can be displayed (for mesh combination limitations).
|
- No more than 65535 vertices can be displayed (for mesh combination limitations).
|
||||||
- Please set `Emission` module and `Max Particles` of ParticleSystem properly.
|
- Please set `Emission` module and `Max Particles` of `ParticleSystem` properly.
|
||||||
- Particles are emitted off-screen.
|
- Particles are emitted off-screen.
|
||||||
- When `Position Mode = Relative`, particles are emitted from the scaled position of the ParticleSystem, not from
|
- When `Position Mode = Relative`, particles are emitted from the scaled position of the `ParticleSystem`, not from
|
||||||
the screen point of the ParticleSystem.
|
the screen point of the `ParticleSystem`.
|
||||||
- Place the ParticleSystem in the proper position or try `Position Mode = Absolute`.
|
- Place the `ParticleSystem` in the proper position or try `Position Mode = Absolute`.
|
||||||
- Attaching `UIParticle` to the same object as `ParticleSystem`
|
- Attaching `UIParticle` to the same object as `ParticleSystem`
|
||||||
- `Transform.localScale` will be overridden by the `Auto Scaling` option.
|
- `Transform.localScale` will be overridden by the `Auto Scaling` option.
|
||||||
- It is recommended to place `ParticleSystem` under `UIParticle`.
|
- It is recommended to place `ParticleSystem` under `UIParticle`.
|
||||||
- If `Transform.localScale` contains 0, rendering will be skipped.
|
- If `Transform.localScale` contains 0, rendering will be skipped.
|
||||||
|
- If you are using `Shape` module, please check whether the scale includes 0.
|
||||||
- Displayed particles are in the correct position but too large/too small
|
- Displayed particles are in the correct position but too large/too small
|
||||||
- Adjust `ParticleSystem.renderer.Min/MaxParticleSize`.
|
- Adjust `ParticleSystem.renderer.Min/MaxParticleSize`.
|
||||||
|
- If `Trails.RibbonCount` is greater than 1, the following error occurs frequently.
|
||||||
|
- `Failed setting triangles. Some indices are referencing out of bounds vertices. IndexCount: 660, VertexCount: 222`
|
||||||
|
- Adding a `UIParticle` to the parent is the recommended setup rather than attaching it directly to the `ParticleSystem`.
|
||||||
|
- When using `ParticleSystem.emission.rateOverDistance`, it is recommended to move the transform of `UIParticle` rather than the `ParticleSystem`.
|
||||||
|
- (v4.13.0, v5.0.0-preview.18) Try changing the `Default View Size For Baking` setting under `ProjectSettings > UI > UI Particle` to a value such as 100 or 1000.
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,18 @@ namespace Coffee.UIExtensions
|
|||||||
{
|
{
|
||||||
public enum ShaderPropertyType
|
public enum ShaderPropertyType
|
||||||
{
|
{
|
||||||
|
None = -1,
|
||||||
Color,
|
Color,
|
||||||
Vector,
|
Vector,
|
||||||
Float,
|
Float,
|
||||||
Range,
|
Range,
|
||||||
Texture
|
Texture,
|
||||||
|
Int,
|
||||||
|
|
||||||
|
Matrix = 100,
|
||||||
|
MatrixArray = 101,
|
||||||
|
FloatArray = 102,
|
||||||
|
VectorArray = 103,
|
||||||
}
|
}
|
||||||
|
|
||||||
[SerializeField] private string m_Name = "";
|
[SerializeField] private string m_Name = "";
|
||||||
@@ -32,42 +39,41 @@ namespace Coffee.UIExtensions
|
|||||||
|
|
||||||
public void UpdateMaterialProperties(Material material, MaterialPropertyBlock mpb)
|
public void UpdateMaterialProperties(Material material, MaterialPropertyBlock mpb)
|
||||||
{
|
{
|
||||||
|
#if UNITY_2021_1_OR_NEWER
|
||||||
|
if (!mpb.HasProperty(id)) return;
|
||||||
|
#else
|
||||||
if (!material.HasProperty(id)) return;
|
if (!material.HasProperty(id)) return;
|
||||||
|
#endif
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case ShaderPropertyType.Color:
|
case ShaderPropertyType.Color:
|
||||||
var color = mpb.GetColor(id);
|
material.SetColor(id, mpb.GetColor(id));
|
||||||
if (color != default)
|
|
||||||
{
|
|
||||||
material.SetColor(id, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ShaderPropertyType.Vector:
|
case ShaderPropertyType.Vector:
|
||||||
var vector = mpb.GetVector(id);
|
material.SetVector(id, mpb.GetVector(id));
|
||||||
if (vector != default)
|
|
||||||
{
|
|
||||||
material.SetVector(id, vector);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ShaderPropertyType.Float:
|
case ShaderPropertyType.Float:
|
||||||
case ShaderPropertyType.Range:
|
case ShaderPropertyType.Range:
|
||||||
var value = mpb.GetFloat(id);
|
material.SetFloat(id, mpb.GetFloat(id));
|
||||||
if (!Mathf.Approximately(value, 0))
|
|
||||||
{
|
|
||||||
material.SetFloat(id, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ShaderPropertyType.Texture:
|
case ShaderPropertyType.Texture:
|
||||||
var tex = mpb.GetTexture(id);
|
material.SetTexture(id, mpb.GetTexture(id));
|
||||||
if (tex != default(Texture))
|
break;
|
||||||
{
|
case ShaderPropertyType.Int:
|
||||||
material.SetTexture(id, tex);
|
material.SetInt(id, mpb.GetInt(id));
|
||||||
}
|
break;
|
||||||
|
case ShaderPropertyType.Matrix:
|
||||||
|
material.SetMatrix(id, mpb.GetMatrix(id));
|
||||||
|
break;
|
||||||
|
case ShaderPropertyType.MatrixArray:
|
||||||
|
material.SetMatrixArray(id, mpb.GetMatrixArray(id));
|
||||||
|
break;
|
||||||
|
case ShaderPropertyType.FloatArray:
|
||||||
|
material.SetFloatArray(id, mpb.GetFloatArray(id));
|
||||||
|
break;
|
||||||
|
case ShaderPropertyType.VectorArray:
|
||||||
|
material.SetVectorArray(id, mpb.GetVectorArray(id));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
Runtime/Coffee.UIParticle.R.dll
Normal file
BIN
Runtime/Coffee.UIParticle.R.dll
Normal file
Binary file not shown.
33
Runtime/Coffee.UIParticle.R.dll.meta
Normal file
33
Runtime/Coffee.UIParticle.R.dll.meta
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4d73b3825bf044d418ae21bb331d3902
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 1
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
- first:
|
||||||
|
Windows Store Apps: WindowsStoreApps
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -98,7 +98,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
Profiler.BeginSample("(COF)[CanvasExt] GetViewProjectionMatrix");
|
Profiler.BeginSample("(COF)[CanvasExt] GetViewProjectionMatrix");
|
||||||
var rootCanvas = canvas.rootCanvas;
|
var rootCanvas = canvas.rootCanvas;
|
||||||
var cam = rootCanvas.worldCamera;
|
var cam = rootCanvas.worldCamera;
|
||||||
if (rootCanvas && rootCanvas.renderMode != RenderMode.ScreenSpaceOverlay && cam)
|
if (rootCanvas != null && rootCanvas.renderMode != RenderMode.ScreenSpaceOverlay && cam != null)
|
||||||
{
|
{
|
||||||
if (eye == Camera.MonoOrStereoscopicEye.Mono)
|
if (eye == Camera.MonoOrStereoscopicEye.Mono)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,16 +12,43 @@ namespace Coffee.UIParticleInternal
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class ComponentExtensions
|
internal static class ComponentExtensions
|
||||||
{
|
{
|
||||||
|
#if !UNITY_2019_2_OR_NEWER
|
||||||
|
public static bool TryGetComponent<T>(this GameObject self, out T component)
|
||||||
|
where T : Component
|
||||||
|
{
|
||||||
|
if (self == null)
|
||||||
|
{
|
||||||
|
component = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
component = self.GetComponent<T>();
|
||||||
|
return component != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryGetComponent<T>(this Component self, out T component)
|
||||||
|
where T : Component
|
||||||
|
{
|
||||||
|
if (self == null)
|
||||||
|
{
|
||||||
|
component = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.gameObject.TryGetComponent(out component);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get components in children of a specific type in the hierarchy of a GameObject.
|
/// Get components in children of a specific type in the hierarchy of a GameObject.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static T[] GetComponentsInChildren<T>(this Component self, int depth)
|
public static T[] GetComponentsInChildren<T>(this Component self, int depth)
|
||||||
where T : Component
|
where T : Component
|
||||||
{
|
{
|
||||||
var results = ListPool<T>.Rent();
|
var results = InternalListPool<T>.Rent();
|
||||||
self.GetComponentsInChildren_Internal(results, depth);
|
self.GetComponentsInChildren_Internal(results, depth);
|
||||||
var array = results.ToArray();
|
var array = results.ToArray();
|
||||||
ListPool<T>.Return(ref results);
|
InternalListPool<T>.Return(ref results);
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +65,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
private static void GetComponentsInChildren_Internal<T>(this Component self, List<T> results, int depth)
|
private static void GetComponentsInChildren_Internal<T>(this Component self, List<T> results, int depth)
|
||||||
where T : Component
|
where T : Component
|
||||||
{
|
{
|
||||||
if (!self || results == null || depth < 0) return;
|
if (self == null || results == null || depth < 0) return;
|
||||||
|
|
||||||
var tr = self.transform;
|
var tr = self.transform;
|
||||||
if (tr.TryGetComponent<T>(out var t))
|
if (tr.TryGetComponent<T>(out var t))
|
||||||
@@ -59,7 +86,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static T GetOrAddComponent<T>(this Component self) where T : Component
|
public static T GetOrAddComponent<T>(this Component self) where T : Component
|
||||||
{
|
{
|
||||||
if (!self) return null;
|
if (self == null) return null;
|
||||||
return self.TryGetComponent<T>(out var component)
|
return self.TryGetComponent<T>(out var component)
|
||||||
? component
|
? component
|
||||||
: self.gameObject.AddComponent<T>();
|
: self.gameObject.AddComponent<T>();
|
||||||
@@ -72,7 +99,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
{
|
{
|
||||||
T component = null;
|
T component = null;
|
||||||
var transform = self.transform;
|
var transform = self.transform;
|
||||||
while (transform)
|
while (transform != null)
|
||||||
{
|
{
|
||||||
if (transform.TryGetComponent<T>(out var c))
|
if (transform.TryGetComponent<T>(out var c))
|
||||||
{
|
{
|
||||||
@@ -93,7 +120,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
where T : Component
|
where T : Component
|
||||||
{
|
{
|
||||||
var tr = includeSelf ? self.transform : self.transform.parent;
|
var tr = includeSelf ? self.transform : self.transform.parent;
|
||||||
while (tr)
|
while (tr != null)
|
||||||
{
|
{
|
||||||
if (tr.TryGetComponent<T>(out var c) && valid(c)) return c;
|
if (tr.TryGetComponent<T>(out var c) && valid(c)) return c;
|
||||||
if (tr == stopAfter) return null;
|
if (tr == stopAfter) return null;
|
||||||
@@ -134,14 +161,43 @@ namespace Coffee.UIParticleInternal
|
|||||||
Profiler.EndSample();
|
Profiler.EndSample();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a component of a specific type to the children of a GameObject.
|
||||||
|
/// </summary>
|
||||||
|
public static void AddComponentOnChildren<T>(this Component self, bool includeSelf)
|
||||||
|
where T : Component
|
||||||
|
{
|
||||||
|
if (self == null) return;
|
||||||
|
|
||||||
|
Profiler.BeginSample("(COF)[ComponentExt] AddComponentOnChildren > Self");
|
||||||
|
if (includeSelf && !self.TryGetComponent<T>(out _))
|
||||||
|
{
|
||||||
|
self.gameObject.AddComponent<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Profiler.EndSample();
|
||||||
|
|
||||||
|
Profiler.BeginSample("(COF)[ComponentExt] AddComponentOnChildren > Child");
|
||||||
|
var childCount = self.transform.childCount;
|
||||||
|
for (var i = 0; i < childCount; i++)
|
||||||
|
{
|
||||||
|
var child = self.transform.GetChild(i);
|
||||||
|
if (child.TryGetComponent<T>(out _)) continue;
|
||||||
|
|
||||||
|
child.gameObject.AddComponent<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Profiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
#if !UNITY_2021_2_OR_NEWER && !UNITY_2020_3_45 && !UNITY_2020_3_46 && !UNITY_2020_3_47 && !UNITY_2020_3_48
|
#if !UNITY_2021_2_OR_NEWER && !UNITY_2020_3_45 && !UNITY_2020_3_46 && !UNITY_2020_3_47 && !UNITY_2020_3_48
|
||||||
public static T GetComponentInParent<T>(this Component self, bool includeInactive) where T : Component
|
public static T GetComponentInParent<T>(this Component self, bool includeInactive) where T : Component
|
||||||
{
|
{
|
||||||
if (!self) return null;
|
if (self == null) return null;
|
||||||
if (!includeInactive) return self.GetComponentInParent<T>();
|
if (!includeInactive) return self.GetComponentInParent<T>();
|
||||||
|
|
||||||
var current = self.transform;
|
var current = self.transform;
|
||||||
while (current)
|
while (current != null)
|
||||||
{
|
{
|
||||||
if (current.TryGetComponent<T>(out var c)) return c;
|
if (current.TryGetComponent<T>(out var c)) return c;
|
||||||
current = current.parent;
|
current = current.parent;
|
||||||
@@ -157,7 +213,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal static bool CanConvertTo<T>(this Object context) where T : MonoBehaviour
|
internal static bool CanConvertTo<T>(this Object context) where T : MonoBehaviour
|
||||||
{
|
{
|
||||||
return context && context.GetType() != typeof(T);
|
return context != null && context.GetType() != typeof(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -175,7 +231,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
target.enabled = false;
|
target.enabled = false;
|
||||||
|
|
||||||
// Find MonoScript of the specified component.
|
// Find MonoScript of the specified component.
|
||||||
foreach (var script in Resources.FindObjectsOfTypeAll<MonoScript>())
|
foreach (var script in MonoImporter.GetAllRuntimeMonoScripts())
|
||||||
{
|
{
|
||||||
if (script.GetClass() != typeof(T))
|
if (script.GetClass() != typeof(T))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using UnityEditor;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Coffee.UIParticleInternal
|
|
||||||
{
|
|
||||||
internal static class Misc
|
|
||||||
{
|
|
||||||
public static void Destroy(Object obj)
|
|
||||||
{
|
|
||||||
if (!obj) return;
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
if (!Application.isPlaying)
|
|
||||||
{
|
|
||||||
Object.DestroyImmediate(obj);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
Object.Destroy(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DestroyImmediate(Object obj)
|
|
||||||
{
|
|
||||||
if (!obj) return;
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
if (Application.isEditor)
|
|
||||||
{
|
|
||||||
Object.DestroyImmediate(obj);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
Object.Destroy(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Conditional("UNITY_EDITOR")]
|
|
||||||
public static void SetDirty(Object obj)
|
|
||||||
{
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
if (!obj) return;
|
|
||||||
EditorUtility.SetDirty(obj);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,23 +17,25 @@ namespace Coffee.UIParticleInternal
|
|||||||
Type.GetType("UnityEditor.Experimental.U2D.SpriteEditorExtension, UnityEditor")
|
Type.GetType("UnityEditor.Experimental.U2D.SpriteEditorExtension, UnityEditor")
|
||||||
?? Type.GetType("UnityEditor.U2D.SpriteEditorExtension, UnityEditor");
|
?? Type.GetType("UnityEditor.U2D.SpriteEditorExtension, UnityEditor");
|
||||||
|
|
||||||
private static readonly MethodInfo s_GetActiveAtlasTextureMethod = s_SpriteEditorExtensionType
|
private static readonly Func<Sprite, Texture2D> s_GetActiveAtlasTextureMethod =
|
||||||
.GetMethod("GetActiveAtlasTexture", BindingFlags.Static | BindingFlags.NonPublic);
|
(Func<Sprite, Texture2D>)Delegate.CreateDelegate(typeof(Func<Sprite, Texture2D>),
|
||||||
|
s_SpriteEditorExtensionType
|
||||||
|
.GetMethod("GetActiveAtlasTexture", BindingFlags.Static | BindingFlags.NonPublic));
|
||||||
|
|
||||||
private static readonly MethodInfo s_GetActiveAtlasMethod = s_SpriteEditorExtensionType
|
private static readonly Func<Sprite, SpriteAtlas> s_GetActiveAtlasMethod =
|
||||||
.GetMethod("GetActiveAtlas", BindingFlags.Static | BindingFlags.NonPublic);
|
(Func<Sprite, SpriteAtlas>)Delegate.CreateDelegate(typeof(Func<Sprite, SpriteAtlas>),
|
||||||
|
s_SpriteEditorExtensionType
|
||||||
|
.GetMethod("GetActiveAtlas", BindingFlags.Static | BindingFlags.NonPublic));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the actual texture of a sprite in play mode or edit mode.
|
/// Get the actual texture of a sprite in play mode or edit mode.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Texture2D GetActualTexture(this Sprite self)
|
public static Texture2D GetActualTexture(this Sprite self)
|
||||||
{
|
{
|
||||||
if (!self) return null;
|
if (self == null) return null;
|
||||||
|
|
||||||
if (Application.isPlaying) return self.texture;
|
var ret = s_GetActiveAtlasTextureMethod(self);
|
||||||
|
return ret != null ? ret : self.texture;
|
||||||
var ret = s_GetActiveAtlasTextureMethod.Invoke(null, new object[] { self }) as Texture2D;
|
|
||||||
return ret ? ret : self.texture;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -41,9 +43,9 @@ namespace Coffee.UIParticleInternal
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static SpriteAtlas GetActiveAtlas(this Sprite self)
|
public static SpriteAtlas GetActiveAtlas(this Sprite self)
|
||||||
{
|
{
|
||||||
if (!self) return null;
|
if (self == null) return null;
|
||||||
|
|
||||||
return s_GetActiveAtlasMethod.Invoke(null, new object[] { self }) as SpriteAtlas;
|
return s_GetActiveAtlasMethod(self);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -51,7 +53,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal static Texture2D GetActualTexture(this Sprite self)
|
internal static Texture2D GetActualTexture(this Sprite self)
|
||||||
{
|
{
|
||||||
return self ? self.texture : null;
|
return self != null ? self.texture : null;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Object = UnityEngine.Object;
|
using Object = UnityEngine.Object;
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
|
using System.IO;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEditor.Build;
|
using UnityEditor.Build;
|
||||||
using UnityEditor.Build.Reporting;
|
using UnityEditor.Build.Reporting;
|
||||||
@@ -14,42 +14,97 @@ namespace Coffee.UIParticleInternal
|
|||||||
public abstract class PreloadedProjectSettings : ScriptableObject
|
public abstract class PreloadedProjectSettings : ScriptableObject
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
{
|
{
|
||||||
private class PreprocessBuildWithReport : IPreprocessBuildWithReport
|
[Tooltip("When enabled, this settings asset will be added to PlayerSettings.preloadedAssets in build.\n\n" +
|
||||||
|
"When disable, you should load this settings via Resources, AssetBundles or Addressables to use.")]
|
||||||
|
[SerializeField]
|
||||||
|
[Header("Advanced")]
|
||||||
|
[HideInInspector]
|
||||||
|
private bool m_PreLoadSettingsInBuild = true;
|
||||||
|
|
||||||
|
protected static bool s_BuildingPlayer;
|
||||||
|
|
||||||
|
private class EditorEvents : AssetPostprocessor, IPreprocessBuildWithReport, IPostprocessBuildWithReport
|
||||||
{
|
{
|
||||||
int IOrderedCallback.callbackOrder => 0;
|
int IOrderedCallback.callbackOrder => 0;
|
||||||
|
|
||||||
void IPreprocessBuildWithReport.OnPreprocessBuild(BuildReport report)
|
private static void OnPostprocessAllAssets(string[] _, string[] __, string[] ___, string[] ____)
|
||||||
{
|
{
|
||||||
Initialize();
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IPreprocessBuildWithReport.OnPreprocessBuild(BuildReport report)
|
||||||
|
{
|
||||||
|
AssetDatabase.Refresh();
|
||||||
|
Initialize();
|
||||||
|
s_BuildingPlayer = true;
|
||||||
|
|
||||||
|
foreach (var t in TypeCache.GetTypesDerivedFrom(typeof(PreloadedProjectSettings<>)))
|
||||||
|
{
|
||||||
|
var settings = GetDefaultSettings(t);
|
||||||
|
if (settings == null || settings.m_PreLoadSettingsInBuild) continue;
|
||||||
|
|
||||||
|
PlayerSettings.SetPreloadedAssets(
|
||||||
|
PlayerSettings.GetPreloadedAssets()
|
||||||
|
.Where(x => x != null && x.GetType() != t)
|
||||||
|
.ToArray());
|
||||||
|
|
||||||
|
Debug.Log($"[PreloadedProjectSettings] Build started: removed '{settings.name}' " +
|
||||||
|
$"({t.Name}) from PreloadedAssets. " +
|
||||||
|
$"It will be restored after build completes.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[InitializeOnLoadMethod]
|
void IPostprocessBuildWithReport.OnPostprocessBuild(BuildReport report)
|
||||||
[InitializeOnEnterPlayMode]
|
{
|
||||||
private static void Initialize()
|
s_BuildingPlayer = false;
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_2019_3_OR_NEWER
|
||||||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||||
|
private static void OnDomainReload()
|
||||||
{
|
{
|
||||||
const BindingFlags flags = BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy;
|
|
||||||
foreach (var t in TypeCache.GetTypesDerivedFrom(typeof(PreloadedProjectSettings<>)))
|
foreach (var t in TypeCache.GetTypesDerivedFrom(typeof(PreloadedProjectSettings<>)))
|
||||||
{
|
{
|
||||||
var defaultSettings = GetDefaultSettings(t);
|
var defaultSettings = GetDefaultSettings(t);
|
||||||
if (!defaultSettings)
|
if (defaultSettings != null)
|
||||||
|
{
|
||||||
|
defaultSettings.OnDomainReload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Initialize()
|
||||||
|
{
|
||||||
|
foreach (var t in TypeCache.GetTypesDerivedFrom(typeof(PreloadedProjectSettings<>)))
|
||||||
|
{
|
||||||
|
var defaultSettings = GetDefaultSettings(t);
|
||||||
|
if (defaultSettings == null)
|
||||||
|
{
|
||||||
|
if (!s_BuildingPlayer)
|
||||||
{
|
{
|
||||||
// When create a new instance, automatically set it as default settings.
|
// When create a new instance, automatically set it as default settings.
|
||||||
defaultSettings = t.GetProperty("instance", flags)
|
defaultSettings = CreateInstance(t) as PreloadedProjectSettings;
|
||||||
?.GetValue(null, null) as PreloadedProjectSettings;
|
|
||||||
}
|
|
||||||
else if (GetPreloadedSettings(t).Length != 1)
|
|
||||||
{
|
|
||||||
SetDefaultSettings(defaultSettings);
|
SetDefaultSettings(defaultSettings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (GetPreloadedSettings(t).Length != 1)
|
||||||
|
{
|
||||||
|
if (!s_BuildingPlayer) SetDefaultSettings(defaultSettings);
|
||||||
|
}
|
||||||
|
|
||||||
EditorApplication.QueuePlayerLoopUpdate();
|
if (defaultSettings != null)
|
||||||
|
{
|
||||||
|
defaultSettings.OnInitialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static string GetDefaultName(Type type, bool nicify)
|
protected static string GetDefaultName(Type type, bool nicify)
|
||||||
{
|
{
|
||||||
var typeName = type.Name.Replace("ProjectSettings", "");
|
var typeName = type.Name;
|
||||||
return nicify
|
return nicify
|
||||||
? ObjectNames.NicifyVariableName(typeName)
|
? ObjectNames.NicifyVariableName(typeName)
|
||||||
: typeName;
|
: typeName;
|
||||||
@@ -58,22 +113,23 @@ namespace Coffee.UIParticleInternal
|
|||||||
private static Object[] GetPreloadedSettings(Type type)
|
private static Object[] GetPreloadedSettings(Type type)
|
||||||
{
|
{
|
||||||
return PlayerSettings.GetPreloadedAssets()
|
return PlayerSettings.GetPreloadedAssets()
|
||||||
.Where(x => x && x.GetType() == type)
|
.Where(x => x != null && x.GetType() == type)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static PreloadedProjectSettings GetDefaultSettings(Type type)
|
protected static PreloadedProjectSettings GetDefaultSettings(Type type)
|
||||||
{
|
{
|
||||||
return GetPreloadedSettings(type).FirstOrDefault() as PreloadedProjectSettings
|
return GetPreloadedSettings(type).FirstOrDefault() as PreloadedProjectSettings
|
||||||
?? AssetDatabase.FindAssets($"t:{nameof(PreloadedProjectSettings)}")
|
?? AssetDatabase.FindAssets($"t:{type.Name}")
|
||||||
.Select(AssetDatabase.GUIDToAssetPath)
|
.Select(AssetDatabase.GUIDToAssetPath)
|
||||||
.Select(AssetDatabase.LoadAssetAtPath<PreloadedProjectSettings>)
|
.Select(AssetDatabase.LoadAssetAtPath<PreloadedProjectSettings>)
|
||||||
.FirstOrDefault(x => x && x.GetType() == type);
|
.FirstOrDefault(x => x != null && x.GetType() == type);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void SetDefaultSettings(PreloadedProjectSettings asset)
|
protected static void SetDefaultSettings(PreloadedProjectSettings asset)
|
||||||
{
|
{
|
||||||
if (!asset) return;
|
if (asset == null) return;
|
||||||
|
|
||||||
var type = asset.GetType();
|
var type = asset.GetType();
|
||||||
if (string.IsNullOrEmpty(AssetDatabase.GetAssetPath(asset)))
|
if (string.IsNullOrEmpty(AssetDatabase.GetAssetPath(asset)))
|
||||||
{
|
{
|
||||||
@@ -84,13 +140,17 @@ namespace Coffee.UIParticleInternal
|
|||||||
|
|
||||||
var assetPath = $"Assets/ProjectSettings/{GetDefaultName(type, false)}.asset";
|
var assetPath = $"Assets/ProjectSettings/{GetDefaultName(type, false)}.asset";
|
||||||
assetPath = AssetDatabase.GenerateUniqueAssetPath(assetPath);
|
assetPath = AssetDatabase.GenerateUniqueAssetPath(assetPath);
|
||||||
|
if (!File.Exists(assetPath))
|
||||||
|
{
|
||||||
AssetDatabase.CreateAsset(asset, assetPath);
|
AssetDatabase.CreateAsset(asset, assetPath);
|
||||||
|
asset.OnCreateAsset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var preloadedAssets = PlayerSettings.GetPreloadedAssets();
|
var preloadedAssets = PlayerSettings.GetPreloadedAssets();
|
||||||
var projectSettings = GetPreloadedSettings(type);
|
var projectSettings = GetPreloadedSettings(type);
|
||||||
PlayerSettings.SetPreloadedAssets(preloadedAssets
|
PlayerSettings.SetPreloadedAssets(preloadedAssets
|
||||||
.Where(x => x)
|
.Where(x => x != null)
|
||||||
.Except(projectSettings.Except(new[] { asset }))
|
.Except(projectSettings.Except(new[] { asset }))
|
||||||
.Append(asset)
|
.Append(asset)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
@@ -98,6 +158,47 @@ namespace Coffee.UIParticleInternal
|
|||||||
|
|
||||||
AssetDatabase.Refresh();
|
AssetDatabase.Refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual void OnCreateAsset()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnInitialize()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnDomainReload()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal abstract class PreloadedProjectSettingsEditor : Editor
|
||||||
|
{
|
||||||
|
private SerializedProperty _preLoadSettingsInBuild;
|
||||||
|
|
||||||
|
protected virtual void OnEnable()
|
||||||
|
{
|
||||||
|
_preLoadSettingsInBuild = serializedObject.FindProperty("m_PreLoadSettingsInBuild");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DrawPreLoadSettingsInBuild(string packageName)
|
||||||
|
{
|
||||||
|
EditorGUILayout.PropertyField(_preLoadSettingsInBuild);
|
||||||
|
if (!_preLoadSettingsInBuild.boolValue)
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.HelpBox(
|
||||||
|
$"{target.GetType().Name} asset will not be built in.\n" +
|
||||||
|
$"please load manually from Resources, AssetBundle, or Addressables before using {packageName}.",
|
||||||
|
MessageType.Warning);
|
||||||
|
if (GUILayout.Button("Ping"))
|
||||||
|
{
|
||||||
|
EditorGUIUtility.PingObject(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
{
|
{
|
||||||
@@ -112,29 +213,33 @@ namespace Coffee.UIParticleInternal
|
|||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
private string _jsonText;
|
private string _jsonText;
|
||||||
|
|
||||||
|
public static bool hasInstance => s_Instance != null;
|
||||||
|
|
||||||
public static T instance
|
public static T instance
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (s_Instance) return s_Instance;
|
if (s_Instance != null) return s_Instance;
|
||||||
|
|
||||||
s_Instance = GetDefaultSettings(typeof(T)) as T;
|
s_Instance = GetDefaultSettings(typeof(T)) as T;
|
||||||
if (s_Instance) return s_Instance;
|
if (s_Instance != null) return s_Instance;
|
||||||
|
|
||||||
s_Instance = CreateInstance<T>();
|
s_Instance = CreateInstance<T>();
|
||||||
if (!s_Instance)
|
if (s_Instance == null)
|
||||||
{
|
{
|
||||||
s_Instance = null;
|
s_Instance = null;
|
||||||
return s_Instance;
|
return s_Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetDefaultSettings(s_Instance);
|
if (!s_BuildingPlayer) SetDefaultSettings(s_Instance);
|
||||||
return s_Instance;
|
return s_Instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPlayModeStateChanged(PlayModeStateChange state)
|
private void OnPlayModeStateChanged(PlayModeStateChange state)
|
||||||
{
|
{
|
||||||
|
if (this == null) return;
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case PlayModeStateChange.ExitingEditMode:
|
case PlayModeStateChange.ExitingEditMode:
|
||||||
@@ -150,8 +255,13 @@ namespace Coffee.UIParticleInternal
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnDomainReload()
|
||||||
|
{
|
||||||
|
s_Instance = null;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
public static T instance => s_Instance ? s_Instance : s_Instance = CreateInstance<T>();
|
public static T instance => s_Instance != null ? s_Instance : s_Instance = CreateInstance<T>();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -160,17 +270,21 @@ namespace Coffee.UIParticleInternal
|
|||||||
protected virtual void OnEnable()
|
protected virtual void OnEnable()
|
||||||
{
|
{
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
var isDefaultSettings = !s_Instance || s_Instance == this || GetDefaultSettings(typeof(T)) == this;
|
var isDefaultSettings = s_Instance == null || s_Instance == this || GetDefaultSettings(typeof(T)) == this;
|
||||||
if (!isDefaultSettings)
|
if (!isDefaultSettings)
|
||||||
{
|
{
|
||||||
DestroyImmediate(this, true);
|
DestroyImmediate(this, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
||||||
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||||
|
#else
|
||||||
|
if (s_Instance != null && s_Instance != this)
|
||||||
|
{
|
||||||
|
Destroy(s_Instance);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (s_Instance) return;
|
|
||||||
s_Instance = this as T;
|
s_Instance = this as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,9 +313,9 @@ namespace Coffee.UIParticleInternal
|
|||||||
|
|
||||||
public override void OnGUI(string searchContext)
|
public override void OnGUI(string searchContext)
|
||||||
{
|
{
|
||||||
if (!_target)
|
if (_target == null)
|
||||||
{
|
{
|
||||||
if (_editor)
|
if (_editor != null)
|
||||||
{
|
{
|
||||||
DestroyImmediate(_editor);
|
DestroyImmediate(_editor);
|
||||||
_editor = null;
|
_editor = null;
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ namespace Coffee.UIParticleInternal
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class FastActionBase<T>
|
internal class FastActionBase<T>
|
||||||
{
|
{
|
||||||
private static readonly ObjectPool<LinkedListNode<T>> s_NodePool =
|
private static readonly InternalObjectPool<LinkedListNode<T>> s_NodePool =
|
||||||
new ObjectPool<LinkedListNode<T>>(() => new LinkedListNode<T>(default), _ => true, x => x.Value = default);
|
new InternalObjectPool<LinkedListNode<T>>(() => new LinkedListNode<T>(default), _ => true,
|
||||||
|
x => x.Value = default);
|
||||||
|
|
||||||
private readonly LinkedList<T> _delegates = new LinkedList<T>();
|
private readonly LinkedList<T> _delegates = new LinkedList<T>();
|
||||||
|
|
||||||
@@ -65,11 +66,6 @@ namespace Coffee.UIParticleInternal
|
|||||||
node = node.Next;
|
node = node.Next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
_delegates.Clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -8,19 +8,19 @@ namespace Coffee.UIParticleInternal
|
|||||||
{
|
{
|
||||||
private static readonly Dictionary<Type, IFrameCache> s_Caches = new Dictionary<Type, IFrameCache>();
|
private static readonly Dictionary<Type, IFrameCache> s_Caches = new Dictionary<Type, IFrameCache>();
|
||||||
|
|
||||||
static FrameCache()
|
#if UNITY_EDITOR && UNITY_2019_3_OR_NEWER
|
||||||
{
|
|
||||||
s_Caches.Clear();
|
|
||||||
UIExtraCallbacks.onLateAfterCanvasRebuild += ClearAllCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||||
|
#elif UNITY_EDITOR
|
||||||
|
[InitializeOnLoadMethod]
|
||||||
|
#else
|
||||||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||||
|
#endif
|
||||||
private static void Clear()
|
private static void Clear()
|
||||||
{
|
{
|
||||||
s_Caches.Clear();
|
s_Caches.Clear();
|
||||||
|
UIExtraCallbacks.onLateAfterCanvasRebuild -= ClearAllCache;
|
||||||
|
UIExtraCallbacks.onLateAfterCanvasRebuild += ClearAllCache;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to retrieve a value from the frame cache with a specified key.
|
/// Tries to retrieve a value from the frame cache with a specified key.
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ using Conditional = System.Diagnostics.ConditionalAttribute;
|
|||||||
|
|
||||||
namespace Coffee.UIParticleInternal
|
namespace Coffee.UIParticleInternal
|
||||||
{
|
{
|
||||||
internal static class Logging
|
internal static class Logger
|
||||||
{
|
{
|
||||||
#if !ENABLE_COFFEE_LOGGER
|
#if !ENABLE_COFFEE_LOGGER
|
||||||
private const string k_DisableSymbol = "DISABLE_COFFEE_LOGGER";
|
private const string k_DisableSymbol = "DISABLE_COFFEE_LOGGER";
|
||||||
@@ -48,7 +48,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
public static void LogIf(bool enable, object tag, object message, Object context = null)
|
public static void LogIf(bool enable, object tag, object message, Object context = null)
|
||||||
{
|
{
|
||||||
if (!enable) return;
|
if (!enable) return;
|
||||||
Log_Internal(LogType.Log, tag, message, context ? context : tag as Object);
|
Log_Internal(LogType.Log, tag, message, context != null ? context : tag as Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !ENABLE_COFFEE_LOGGER
|
#if !ENABLE_COFFEE_LOGGER
|
||||||
@@ -56,7 +56,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
#endif
|
#endif
|
||||||
public static void Log(object tag, object message, Object context = null)
|
public static void Log(object tag, object message, Object context = null)
|
||||||
{
|
{
|
||||||
Log_Internal(LogType.Log, tag, message, context ? context : tag as Object);
|
Log_Internal(LogType.Log, tag, message, context != null ? context : tag as Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !ENABLE_COFFEE_LOGGER
|
#if !ENABLE_COFFEE_LOGGER
|
||||||
@@ -64,13 +64,13 @@ namespace Coffee.UIParticleInternal
|
|||||||
#endif
|
#endif
|
||||||
public static void LogWarning(object tag, object message, Object context = null)
|
public static void LogWarning(object tag, object message, Object context = null)
|
||||||
{
|
{
|
||||||
Log_Internal(LogType.Warning, tag, message, context ? context : tag as Object);
|
Log_Internal(LogType.Warning, tag, message, context != null ? context : tag as Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void LogError(object tag, object message, Object context = null)
|
public static void LogError(object tag, object message, Object context = null)
|
||||||
{
|
{
|
||||||
#if ENABLE_COFFEE_LOGGER
|
#if ENABLE_COFFEE_LOGGER
|
||||||
Log_Internal(LogType.Error, tag, message, context ? context : tag as Object);
|
Log_Internal(LogType.Error, tag, message, context != null ? context : tag as Object);
|
||||||
#else
|
#else
|
||||||
Debug.LogError($"{tag}: {message}", context);
|
Debug.LogError($"{tag}: {message}", context);
|
||||||
#endif
|
#endif
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 8255313895da84e7cbdc876be3795334
|
guid: 4f9f22bb079324476b1473030ad9fec3
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -13,9 +13,11 @@ namespace Coffee.UIParticleInternal
|
|||||||
|
|
||||||
public static int count => s_Repository.count;
|
public static int count => s_Repository.count;
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
public static Func<string, Shader> onShaderFind = Shader.Find;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR && UNITY_2019_3_OR_NEWER
|
||||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||||
private static void Clear()
|
public static void Clear()
|
||||||
{
|
{
|
||||||
s_Repository.Clear();
|
s_Repository.Clear();
|
||||||
}
|
}
|
||||||
@@ -48,7 +50,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
public static void Get(Hash128 hash, ref Material material, string shaderName)
|
public static void Get(Hash128 hash, ref Material material, string shaderName)
|
||||||
{
|
{
|
||||||
Profiler.BeginSample("(COF)[MaterialRepository] Get");
|
Profiler.BeginSample("(COF)[MaterialRepository] Get");
|
||||||
s_Repository.Get(hash, ref material, x => new Material(Shader.Find(x))
|
s_Repository.Get(hash, ref material, x => new Material(onShaderFind(x))
|
||||||
{
|
{
|
||||||
hideFlags = HideFlags.DontSave | HideFlags.NotEditable
|
hideFlags = HideFlags.DontSave | HideFlags.NotEditable
|
||||||
}, shaderName);
|
}, shaderName);
|
||||||
@@ -61,7 +63,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
public static void Get(Hash128 hash, ref Material material, string shaderName, string[] keywords)
|
public static void Get(Hash128 hash, ref Material material, string shaderName, string[] keywords)
|
||||||
{
|
{
|
||||||
Profiler.BeginSample("(COF)[MaterialRepository] Get");
|
Profiler.BeginSample("(COF)[MaterialRepository] Get");
|
||||||
s_Repository.Get(hash, ref material, x => new Material(Shader.Find(x.shaderName))
|
s_Repository.Get(hash, ref material, x => new Material(onShaderFind(x.shaderName))
|
||||||
{
|
{
|
||||||
hideFlags = HideFlags.DontSave | HideFlags.NotEditable,
|
hideFlags = HideFlags.DontSave | HideFlags.NotEditable,
|
||||||
shaderKeywords = x.keywords
|
shaderKeywords = x.keywords
|
||||||
|
|||||||
129
Runtime/Internal/Utilities/Misc.cs
Normal file
129
Runtime/Internal/Utilities/Misc.cs
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
#if UNITY_2021_2_OR_NEWER
|
||||||
|
using UnityEditor.SceneManagement;
|
||||||
|
#else
|
||||||
|
using UnityEditor.Experimental.SceneManagement;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Coffee.UIParticleInternal
|
||||||
|
{
|
||||||
|
internal static class Misc
|
||||||
|
{
|
||||||
|
public static T[] FindObjectsOfType<T>() where T : Object
|
||||||
|
{
|
||||||
|
#if UNITY_6000_4_OR_NEWER
|
||||||
|
return Object.FindObjectsByType<T>(FindObjectsInactive.Include);
|
||||||
|
#elif UNITY_2023_1_OR_NEWER
|
||||||
|
return Object.FindObjectsByType<T>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||||
|
#else
|
||||||
|
return Object.FindObjectsOfType<T>();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Destroy(Object obj)
|
||||||
|
{
|
||||||
|
if (obj == null) return;
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
if (!Application.isPlaying)
|
||||||
|
{
|
||||||
|
Object.DestroyImmediate(obj);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
Object.Destroy(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DestroyImmediate(Object obj)
|
||||||
|
{
|
||||||
|
if (obj == null) return;
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
Object.DestroyImmediate(obj, true);
|
||||||
|
#else
|
||||||
|
Object.Destroy(obj);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional("UNITY_EDITOR")]
|
||||||
|
public static void SetDirty(Object obj)
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
if (obj == null) return;
|
||||||
|
EditorUtility.SetDirty(obj);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
public static T[] GetAllComponentsInPrefabStage<T>() where T : Component
|
||||||
|
{
|
||||||
|
var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
|
||||||
|
if (prefabStage == null) return Array.Empty<T>();
|
||||||
|
|
||||||
|
return prefabStage.prefabContentsRoot.GetComponentsInChildren<T>(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool isBatchOrBuilding => Application.isBatchMode || BuildPipeline.isBuildingPlayer;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
[Conditional("UNITY_EDITOR")]
|
||||||
|
public static void QueuePlayerLoopUpdate()
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
if (!EditorApplication.isPlaying)
|
||||||
|
{
|
||||||
|
EditorApplication.QueuePlayerLoopUpdate();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !UNITY_2021_2_OR_NEWER
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
[Conditional("UNITY_EDITOR")]
|
||||||
|
internal class IconAttribute : Attribute
|
||||||
|
{
|
||||||
|
private readonly string _path;
|
||||||
|
|
||||||
|
public IconAttribute(string path)
|
||||||
|
{
|
||||||
|
_path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
private static Action<Object, Texture2D> s_SetIconForObject = typeof(EditorGUIUtility)
|
||||||
|
.GetMethod("SetIconForObject", BindingFlags.Static | BindingFlags.NonPublic)
|
||||||
|
.CreateDelegate(typeof(Action<Object, Texture2D>), null) as Action<Object, Texture2D>;
|
||||||
|
|
||||||
|
[InitializeOnLoadMethod]
|
||||||
|
private static void InitializeOnLoadMethod()
|
||||||
|
{
|
||||||
|
if (Misc.isBatchOrBuilding) return;
|
||||||
|
|
||||||
|
var types = TypeCache.GetTypesWithAttribute(typeof(IconAttribute));
|
||||||
|
var scripts = MonoImporter.GetAllRuntimeMonoScripts();
|
||||||
|
foreach (var type in types)
|
||||||
|
{
|
||||||
|
var script = scripts.FirstOrDefault(x => x.GetClass() == type);
|
||||||
|
if (script == null) continue;
|
||||||
|
|
||||||
|
var path = type.GetCustomAttribute<IconAttribute>()?._path;
|
||||||
|
var icon = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
|
||||||
|
if (icon == null) continue;
|
||||||
|
|
||||||
|
s_SetIconForObject(script, icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 39ed6a6b0a72e482488bd298b2ae762e
|
guid: 182319ecc315e4858b119764af0fbcb0
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -6,15 +6,58 @@ namespace Coffee.UIParticleInternal
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Object pool.
|
/// Object pool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class ObjectPool<T>
|
internal class InternalObjectPool<T> where T : class
|
||||||
{
|
{
|
||||||
|
#if UNITY_2021_1_OR_NEWER
|
||||||
|
private readonly Predicate<T> _onValid; // Delegate for checking if instances are valid
|
||||||
|
private readonly UnityEngine.Pool.ObjectPool<T> _pool;
|
||||||
|
|
||||||
|
public InternalObjectPool(Func<T> onCreate, Predicate<T> onValid, Action<T> onReturn)
|
||||||
|
{
|
||||||
|
_pool = new UnityEngine.Pool.ObjectPool<T>(onCreate, null, onReturn);
|
||||||
|
_onValid = onValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rent an instance from the pool.
|
||||||
|
/// When you no longer need it, return it with <see cref="Return" />.
|
||||||
|
/// </summary>
|
||||||
|
public T Rent()
|
||||||
|
{
|
||||||
|
while (0 < _pool.CountInactive)
|
||||||
|
{
|
||||||
|
var instance = _pool.Get();
|
||||||
|
if (_onValid(instance))
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are no instances in the pool, create a new one.
|
||||||
|
Logger.Log(this, $"A new instance is created (pooled: {_pool.CountInactive}, created: {_pool.CountAll}).");
|
||||||
|
return _pool.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return an instance to the pool and assign null.
|
||||||
|
/// Be sure to return the instance obtained with <see cref="Rent" /> with this method.
|
||||||
|
/// </summary>
|
||||||
|
public void Return(ref T instance)
|
||||||
|
{
|
||||||
|
if (instance == null) return; // Ignore if already pooled or null.
|
||||||
|
|
||||||
|
_pool.Release(instance);
|
||||||
|
Logger.Log(this, $"An instance is released (pooled: {_pool.CountInactive}, created: {_pool.CountAll}).");
|
||||||
|
instance = default; // Set the reference to null.
|
||||||
|
}
|
||||||
|
#else
|
||||||
private readonly Func<T> _onCreate; // Delegate for creating instances
|
private readonly Func<T> _onCreate; // Delegate for creating instances
|
||||||
private readonly Action<T> _onReturn; // Delegate for returning instances to the pool
|
private readonly Action<T> _onReturn; // Delegate for returning instances to the pool
|
||||||
private readonly Predicate<T> _onValid; // Delegate for checking if instances are valid
|
private readonly Predicate<T> _onValid; // Delegate for checking if instances are valid
|
||||||
private readonly Stack<T> _pool = new Stack<T>(32); // Object pool
|
private readonly Stack<T> _pool = new Stack<T>(32); // Object pool
|
||||||
private int _count; // Total count of created instances
|
private int _count; // Total count of created instances
|
||||||
|
|
||||||
public ObjectPool(Func<T> onCreate, Predicate<T> onValid, Action<T> onReturn)
|
public InternalObjectPool(Func<T> onCreate, Predicate<T> onValid, Action<T> onReturn)
|
||||||
{
|
{
|
||||||
_onCreate = onCreate;
|
_onCreate = onCreate;
|
||||||
_onValid = onValid;
|
_onValid = onValid;
|
||||||
@@ -37,7 +80,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If there are no instances in the pool, create a new one.
|
// 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();
|
return _onCreate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,18 +94,43 @@ namespace Coffee.UIParticleInternal
|
|||||||
|
|
||||||
_onReturn(instance); // Return the instance to the pool.
|
_onReturn(instance); // Return the instance to the pool.
|
||||||
_pool.Push(instance);
|
_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.
|
instance = default; // Set the reference to null.
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Object pool for <see cref="List{T}" />.
|
/// Object pool for <see cref="List{T}" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class ListPool<T>
|
internal static class InternalListPool<T>
|
||||||
{
|
{
|
||||||
private static readonly ObjectPool<List<T>> s_ListPool =
|
#if UNITY_2021_1_OR_NEWER
|
||||||
new ObjectPool<List<T>>(() => new List<T>(), _ => true, x => x.Clear());
|
/// <summary>
|
||||||
|
/// Rent an instance from the pool.
|
||||||
|
/// When you no longer need it, return it with <see cref="Return" />.
|
||||||
|
/// </summary>
|
||||||
|
public static List<T> Rent()
|
||||||
|
{
|
||||||
|
return UnityEngine.Pool.ListPool<T>.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return an instance to the pool and assign null.
|
||||||
|
/// Be sure to return the instance obtained with <see cref="Rent" /> with this method.
|
||||||
|
/// </summary>
|
||||||
|
public static void Return(ref List<T> toRelease)
|
||||||
|
{
|
||||||
|
if (toRelease != null)
|
||||||
|
{
|
||||||
|
UnityEngine.Pool.ListPool<T>.Release(toRelease);
|
||||||
|
}
|
||||||
|
|
||||||
|
toRelease = null;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
private static readonly InternalObjectPool<List<T>> s_ListPool =
|
||||||
|
new InternalObjectPool<List<T>>(() => new List<T>(), _ => true, x => x.Clear());
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Rent an instance from the pool.
|
/// Rent an instance from the pool.
|
||||||
@@ -81,5 +149,6 @@ namespace Coffee.UIParticleInternal
|
|||||||
{
|
{
|
||||||
s_ListPool.Return(ref toRelease);
|
s_ListPool.Return(ref toRelease);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
Profiler.BeginSample("(COF)[ObjectRepository] GetFromCache");
|
Profiler.BeginSample("(COF)[ObjectRepository] GetFromCache");
|
||||||
if (_cache.TryGetValue(hash, out var entry))
|
if (_cache.TryGetValue(hash, out var entry))
|
||||||
{
|
{
|
||||||
if (!entry.storedObject)
|
if (entry.storedObject == null)
|
||||||
{
|
{
|
||||||
Release(ref entry.storedObject);
|
Release(ref entry.storedObject);
|
||||||
Profiler.EndSample();
|
Profiler.EndSample();
|
||||||
@@ -103,7 +103,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
Release(ref obj);
|
Release(ref obj);
|
||||||
++entry.reference;
|
++entry.reference;
|
||||||
obj = entry.storedObject;
|
obj = entry.storedObject;
|
||||||
Logging.Log(_name, $"Get(total#{count}): {entry}");
|
Logger.Log(_name, $"Get(total#{count}): {entry}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Profiler.EndSample();
|
Profiler.EndSample();
|
||||||
@@ -116,7 +116,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
|
|
||||||
private void Add(Hash128 hash, ref T obj, T newObject)
|
private void Add(Hash128 hash, ref T obj, T newObject)
|
||||||
{
|
{
|
||||||
if (!newObject)
|
if (newObject == null)
|
||||||
{
|
{
|
||||||
Release(ref obj);
|
Release(ref obj);
|
||||||
obj = newObject;
|
obj = newObject;
|
||||||
@@ -130,8 +130,8 @@ namespace Coffee.UIParticleInternal
|
|||||||
newEntry.hash = hash;
|
newEntry.hash = hash;
|
||||||
newEntry.reference = 1;
|
newEntry.reference = 1;
|
||||||
_cache[hash] = newEntry;
|
_cache[hash] = newEntry;
|
||||||
_objectKey[newObject.GetInstanceID()] = hash;
|
_objectKey[newObject.GetHashCode()] = hash;
|
||||||
Logging.Log(_name, $"<color=#03c700>Add</color>(total#{count}): {newEntry}");
|
Logger.Log(_name, $"<color=#03c700>Add</color>(total#{count}): {newEntry}");
|
||||||
Release(ref obj);
|
Release(ref obj);
|
||||||
obj = newObject;
|
obj = newObject;
|
||||||
Profiler.EndSample();
|
Profiler.EndSample();
|
||||||
@@ -146,23 +146,23 @@ namespace Coffee.UIParticleInternal
|
|||||||
|
|
||||||
// Find and release the entry.
|
// Find and release the entry.
|
||||||
Profiler.BeginSample("(COF)[ObjectRepository] Release");
|
Profiler.BeginSample("(COF)[ObjectRepository] Release");
|
||||||
var id = obj.GetInstanceID();
|
var id = obj.GetHashCode();
|
||||||
if (_objectKey.TryGetValue(id, out var hash)
|
if (_objectKey.TryGetValue(id, out var hash)
|
||||||
&& _cache.TryGetValue(hash, out var entry))
|
&& _cache.TryGetValue(hash, out var entry))
|
||||||
{
|
{
|
||||||
entry.reference--;
|
entry.reference--;
|
||||||
if (entry.reference <= 0 || !entry.storedObject)
|
if (entry.reference <= 0 || entry.storedObject == null)
|
||||||
{
|
{
|
||||||
Remove(entry);
|
Remove(entry);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logging.Log(_name, $"Release(total#{_cache.Count}): {entry}");
|
Logger.Log(_name, $"Release(total#{_cache.Count}): {entry}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logging.Log(_name, $"Release(total#{_cache.Count}): <color=red>Already released: {obj}</color>");
|
Logger.Log(_name, $"Release(total#{_cache.Count}): <color=red>Already released: {obj}</color>");
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = null;
|
obj = null;
|
||||||
@@ -175,10 +175,10 @@ namespace Coffee.UIParticleInternal
|
|||||||
|
|
||||||
Profiler.BeginSample("(COF)[ObjectRepository] Remove");
|
Profiler.BeginSample("(COF)[ObjectRepository] Remove");
|
||||||
_cache.Remove(entry.hash);
|
_cache.Remove(entry.hash);
|
||||||
_objectKey.Remove(entry.storedObject.GetInstanceID());
|
_objectKey.Remove(entry.storedObject.GetHashCode());
|
||||||
_pool.Push(entry);
|
_pool.Push(entry);
|
||||||
entry.reference = 0;
|
entry.reference = 0;
|
||||||
Logging.Log(_name, $"<color=#f29e03>Remove</color>(total#{_cache.Count}): {entry}");
|
Logger.Log(_name, $"<color=#f29e03>Remove</color>(total#{_cache.Count}): {entry}");
|
||||||
entry.Release(_onRelease);
|
entry.Release(_onRelease);
|
||||||
Profiler.EndSample();
|
Profiler.EndSample();
|
||||||
}
|
}
|
||||||
@@ -192,7 +192,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
public void Release(Action<T> onRelease)
|
public void Release(Action<T> onRelease)
|
||||||
{
|
{
|
||||||
reference = 0;
|
reference = 0;
|
||||||
if (storedObject)
|
if (storedObject != null)
|
||||||
{
|
{
|
||||||
onRelease?.Invoke(storedObject);
|
onRelease?.Invoke(storedObject);
|
||||||
}
|
}
|
||||||
|
|||||||
88
Runtime/Internal/Utilities/TypeCache.cs
Normal file
88
Runtime/Internal/Utilities/TypeCache.cs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#if !UNITY_2019_2_OR_NEWER
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Coffee.UIParticleInternal
|
||||||
|
{
|
||||||
|
public static class TypeCache
|
||||||
|
{
|
||||||
|
private static readonly object s_Lock = new object();
|
||||||
|
private static readonly Dictionary<Type, Type[]> s_DerivedTypesCache = new Dictionary<Type, Type[]>();
|
||||||
|
private static readonly Dictionary<Type, Type[]> s_AttributeTypesCache = new Dictionary<Type, Type[]>();
|
||||||
|
|
||||||
|
public static IEnumerable<Type> GetTypesDerivedFrom(Type baseType)
|
||||||
|
{
|
||||||
|
lock (s_Lock)
|
||||||
|
{
|
||||||
|
if (s_DerivedTypesCache.TryGetValue(baseType, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
var types = new List<Type>();
|
||||||
|
foreach (var t in GetAllLoadableTypes())
|
||||||
|
{
|
||||||
|
if (t != baseType && baseType.IsAssignableFrom(t))
|
||||||
|
{
|
||||||
|
types.Add(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s_DerivedTypesCache[baseType] = types.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Type> GetTypesWithAttribute(Type attr)
|
||||||
|
{
|
||||||
|
lock (s_Lock)
|
||||||
|
{
|
||||||
|
if (s_AttributeTypesCache.TryGetValue(attr, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
var types = new List<Type>();
|
||||||
|
foreach (var t in GetAllLoadableTypes())
|
||||||
|
{
|
||||||
|
if (t.GetCustomAttributes(attr, inherit: true).Length > 0)
|
||||||
|
{
|
||||||
|
types.Add(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s_AttributeTypesCache[attr] = types.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<Type> GetAllLoadableTypes()
|
||||||
|
{
|
||||||
|
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||||
|
{
|
||||||
|
Type[] types;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
types = assembly.GetTypes();
|
||||||
|
}
|
||||||
|
catch (ReflectionTypeLoadException ex)
|
||||||
|
{
|
||||||
|
types = ex.Types;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (types == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var t in types)
|
||||||
|
{
|
||||||
|
if (t != null)
|
||||||
|
{
|
||||||
|
yield return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
11
Runtime/Internal/Utilities/TypeCache.cs.meta
Normal file
11
Runtime/Internal/Utilities/TypeCache.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bc1207b657ed74ec19e459664d06925f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
@@ -14,11 +15,13 @@ namespace Coffee.UIParticleInternal
|
|||||||
private static readonly FastAction s_AfterCanvasRebuildAction = new FastAction();
|
private static readonly FastAction s_AfterCanvasRebuildAction = new FastAction();
|
||||||
private static readonly FastAction s_LateAfterCanvasRebuildAction = new FastAction();
|
private static readonly FastAction s_LateAfterCanvasRebuildAction = new FastAction();
|
||||||
private static readonly FastAction s_BeforeCanvasRebuildAction = new FastAction();
|
private static readonly FastAction s_BeforeCanvasRebuildAction = new FastAction();
|
||||||
|
private static readonly FastAction s_OnScreenSizeChangedAction = new FastAction();
|
||||||
|
private static Vector2Int s_LastScreenSize;
|
||||||
|
|
||||||
static UIExtraCallbacks()
|
static UIExtraCallbacks()
|
||||||
{
|
{
|
||||||
Canvas.willRenderCanvases += OnBeforeCanvasRebuild;
|
Canvas.willRenderCanvases += OnBeforeCanvasRebuild;
|
||||||
Logging.LogMulticast(typeof(Canvas), "willRenderCanvases", message: "ctor");
|
Logger.LogMulticast(typeof(Canvas), "willRenderCanvases", message: "ctor");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -48,6 +51,15 @@ namespace Coffee.UIParticleInternal
|
|||||||
remove => s_AfterCanvasRebuildAction.Remove(value);
|
remove => s_AfterCanvasRebuildAction.Remove(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event that occurs when the screen size changes.
|
||||||
|
/// </summary>
|
||||||
|
public static event Action onScreenSizeChanged
|
||||||
|
{
|
||||||
|
add => s_OnScreenSizeChangedAction.Add(value);
|
||||||
|
remove => s_OnScreenSizeChangedAction.Remove(value);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes the UIExtraCallbacks to ensure proper event handling.
|
/// Initializes the UIExtraCallbacks to ensure proper event handling.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -56,20 +68,32 @@ namespace Coffee.UIParticleInternal
|
|||||||
if (s_IsInitializedAfterCanvasRebuild) return;
|
if (s_IsInitializedAfterCanvasRebuild) return;
|
||||||
s_IsInitializedAfterCanvasRebuild = true;
|
s_IsInitializedAfterCanvasRebuild = true;
|
||||||
|
|
||||||
|
// Explicitly set `Canvas.willRenderCanvases += CanvasUpdateRegistry.PerformUpdate`.
|
||||||
CanvasUpdateRegistry.IsRebuildingLayout();
|
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;
|
Canvas.willRenderCanvases += OnAfterCanvasRebuild;
|
||||||
Logging.LogMulticast(typeof(Canvas), "willRenderCanvases",
|
Logger.LogMulticast(typeof(Canvas), "willRenderCanvases",
|
||||||
message: "InitializeAfterCanvasRebuild");
|
message: "InitializeAfterCanvasRebuild");
|
||||||
}
|
}
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR && UNITY_2019_3_OR_NEWER
|
||||||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||||
|
#elif UNITY_EDITOR
|
||||||
[InitializeOnLoadMethod]
|
[InitializeOnLoadMethod]
|
||||||
#endif
|
#else
|
||||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||||
|
#endif
|
||||||
private static void InitializeOnLoad()
|
private static void InitializeOnLoad()
|
||||||
{
|
{
|
||||||
Canvas.willRenderCanvases -= OnAfterCanvasRebuild;
|
Canvas.willRenderCanvases -= OnAfterCanvasRebuild;
|
||||||
s_IsInitializedAfterCanvasRebuild = false;
|
s_IsInitializedAfterCanvasRebuild = false;
|
||||||
|
s_LastScreenSize = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -77,6 +101,17 @@ namespace Coffee.UIParticleInternal
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private static void OnBeforeCanvasRebuild()
|
private static void OnBeforeCanvasRebuild()
|
||||||
{
|
{
|
||||||
|
var screenSize = new Vector2Int(Screen.width, Screen.height);
|
||||||
|
if (s_LastScreenSize != screenSize)
|
||||||
|
{
|
||||||
|
if (s_LastScreenSize != default)
|
||||||
|
{
|
||||||
|
s_OnScreenSizeChangedAction.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
s_LastScreenSize = screenSize;
|
||||||
|
}
|
||||||
|
|
||||||
s_BeforeCanvasRebuildAction.Invoke();
|
s_BeforeCanvasRebuildAction.Invoke();
|
||||||
InitializeAfterCanvasRebuild();
|
InitializeAfterCanvasRebuild();
|
||||||
}
|
}
|
||||||
|
|||||||
237
Runtime/ParticleSystemPreviewer.cs
Normal file
237
Runtime/ParticleSystemPreviewer.cs
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Coffee.UIParticleInternal;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Coffee.UIExtensions
|
||||||
|
{
|
||||||
|
[Icon("Packages/com.coffee.ui-particle/Editor/UIParticleIcon.png")]
|
||||||
|
[ExecuteAlways]
|
||||||
|
internal class ParticleSystemPreviewer : MonoBehaviour
|
||||||
|
{
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
[CustomEditor(typeof(ParticleSystemPreviewer))]
|
||||||
|
[CanEditMultipleObjects]
|
||||||
|
internal class ParticleSystemPreviewerEditor : Editor
|
||||||
|
{
|
||||||
|
private GameObject[] _gameObjects;
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
_gameObjects = targets.OfType<ParticleSystemPreviewer>().Select(x => x.gameObject).ToArray();
|
||||||
|
ParticleSystemPreviewSystem.Register(_gameObjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
base.OnInspectorGUI();
|
||||||
|
ParticleSystemPreviewSystem.DrawWarningForTemporary(_gameObjects);
|
||||||
|
ParticleSystemPreviewSystem.DrawWarningForPermanent(_gameObjects);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This class manages temporary ParticleSystems for preview purposes.
|
||||||
|
/// When previewing in the editor, it is common to place an empty ParticleSystem as the root, but it consumes memory at runtime if included in the build.
|
||||||
|
/// The temporary ParticleSystems created by this class only exist when the specified GameObject is selected, and are automatically deleted when the selection is cleared.
|
||||||
|
/// </summary>
|
||||||
|
internal class ParticleSystemPreviewSystem : ScriptableSingleton<ParticleSystemPreviewSystem>
|
||||||
|
{
|
||||||
|
private const HideFlags k_TemporaryHideFlags = HideFlags.DontSave | HideFlags.NotEditable;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
private List<GameObject> m_PreviewObjects = new List<GameObject>();
|
||||||
|
|
||||||
|
#if UNITY_2019_3_OR_NEWER
|
||||||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||||
|
#endif
|
||||||
|
[InitializeOnLoadMethod]
|
||||||
|
public static void Initialize()
|
||||||
|
{
|
||||||
|
instance.OnSelectionChanged();
|
||||||
|
|
||||||
|
Selection.selectionChanged -= instance.OnSelectionChanged;
|
||||||
|
Selection.selectionChanged += instance.OnSelectionChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a temporary ParticleSystem to the specified GameObject for preview purposes.
|
||||||
|
/// </summary>
|
||||||
|
public static void Register(GameObject[] targets)
|
||||||
|
{
|
||||||
|
foreach (var target in targets)
|
||||||
|
{
|
||||||
|
Register(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a temporary ParticleSystem to the specified GameObject for preview purposes.
|
||||||
|
/// </summary>
|
||||||
|
public static void Register(GameObject target)
|
||||||
|
{
|
||||||
|
if (!target) return;
|
||||||
|
if (EditorApplication.isPlaying) return;
|
||||||
|
if (instance.m_PreviewObjects.Contains(target)) return;
|
||||||
|
if (target.TryGetComponent<ParticleSystem>(out var ps))
|
||||||
|
{
|
||||||
|
if (ps.hideFlags == k_TemporaryHideFlags)
|
||||||
|
{
|
||||||
|
RegisterParticleSystem(ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create temporary ParticleSystem for preview.
|
||||||
|
RegisterParticleSystem(target.AddComponent<ParticleSystem>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the temporary ParticleSystem associated with the specified GameObject.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target"></param>
|
||||||
|
public static void Unregister(GameObject target)
|
||||||
|
{
|
||||||
|
if (!target) return;
|
||||||
|
|
||||||
|
var index = instance.m_PreviewObjects.IndexOf(target);
|
||||||
|
if (index < 0) return;
|
||||||
|
|
||||||
|
instance.m_PreviewObjects.RemoveAt(index);
|
||||||
|
if (HasTemporaryParticleSystem(target))
|
||||||
|
{
|
||||||
|
RemoveParticleSystem(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RegisterParticleSystem(ParticleSystem ps)
|
||||||
|
{
|
||||||
|
if (!ps) return;
|
||||||
|
if (EditorApplication.isPlaying) return;
|
||||||
|
|
||||||
|
ps.hideFlags = k_TemporaryHideFlags;
|
||||||
|
|
||||||
|
var emission = ps.emission;
|
||||||
|
emission.enabled = false;
|
||||||
|
var shape = ps.shape;
|
||||||
|
shape.enabled = false;
|
||||||
|
|
||||||
|
if (ps.TryGetComponent<ParticleSystemRenderer>(out var psr))
|
||||||
|
{
|
||||||
|
psr.enabled = false;
|
||||||
|
psr.hideFlags = k_TemporaryHideFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.m_PreviewObjects.Add(ps.gameObject);
|
||||||
|
EditorUtility.SetDirty(ps.gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the temporary ParticleSystem associated with the specified GameObject.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target"></param>
|
||||||
|
private static void RemoveParticleSystem(GameObject target)
|
||||||
|
{
|
||||||
|
if (target.TryGetComponent<ParticleSystem>(out var ps))
|
||||||
|
{
|
||||||
|
Misc.DestroyImmediate(ps);
|
||||||
|
EditorUtility.SetDirty(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.TryGetComponent<ParticleSystem>(out var psr))
|
||||||
|
{
|
||||||
|
Misc.DestroyImmediate(psr);
|
||||||
|
EditorUtility.SetDirty(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the specified GameObject has a temporary ParticleSystem.
|
||||||
|
/// </summary>
|
||||||
|
private static bool HasTemporaryParticleSystem(GameObject target)
|
||||||
|
{
|
||||||
|
return target
|
||||||
|
&& instance.m_PreviewObjects.Contains(target)
|
||||||
|
&& target.TryGetComponent<ParticleSystem>(out var ps)
|
||||||
|
&& ps.hideFlags == k_TemporaryHideFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the specified GameObject has a permanent ParticleSystem.
|
||||||
|
/// </summary>
|
||||||
|
private static bool HasPermanentParticleSystem(GameObject target)
|
||||||
|
{
|
||||||
|
return target
|
||||||
|
&& target.TryGetComponent<ParticleSystem>(out var ps)
|
||||||
|
&& ps.hideFlags != k_TemporaryHideFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSelectionChanged()
|
||||||
|
{
|
||||||
|
var selectedGameObjects = Selection.gameObjects;
|
||||||
|
for (var i = m_PreviewObjects.Count - 1; 0 <= i; i--)
|
||||||
|
{
|
||||||
|
var go = m_PreviewObjects[i];
|
||||||
|
if (!go)
|
||||||
|
{
|
||||||
|
m_PreviewObjects.RemoveAt(i);
|
||||||
|
}
|
||||||
|
else if (EditorApplication.isPlaying && !selectedGameObjects.Contains(go))
|
||||||
|
{
|
||||||
|
Unregister(go);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DrawWarningForTemporary(GameObject[] gameObjects)
|
||||||
|
{
|
||||||
|
if (gameObjects == null || gameObjects.Length == 0 || !gameObjects.Any(HasTemporaryParticleSystem)) return;
|
||||||
|
|
||||||
|
if (WarningButton("The temporary ParticleSystem for preview is attached.\n" +
|
||||||
|
"It will be removed when exiting edit mode.", "Remove"))
|
||||||
|
{
|
||||||
|
foreach (var go in gameObjects)
|
||||||
|
{
|
||||||
|
if (HasTemporaryParticleSystem(go))
|
||||||
|
{
|
||||||
|
RemoveParticleSystem(go);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DrawWarningForPermanent(GameObject[] gameObjects)
|
||||||
|
{
|
||||||
|
if (gameObjects == null || gameObjects.Length == 0 || !gameObjects.Any(HasPermanentParticleSystem)) return;
|
||||||
|
|
||||||
|
if (WarningButton("The permanent ParticleSystem is attached.\n" +
|
||||||
|
"It will be included in build.", "Remove"))
|
||||||
|
{
|
||||||
|
foreach (var go in gameObjects)
|
||||||
|
{
|
||||||
|
if (HasPermanentParticleSystem(go))
|
||||||
|
{
|
||||||
|
RemoveParticleSystem(go);
|
||||||
|
Unregister(go);
|
||||||
|
Register(go);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool WarningButton(string message, string buttonText)
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.HelpBox(message, MessageType.Warning, true);
|
||||||
|
var clicked = GUILayout.Button(EditorGUIUtility.TrTempContent(buttonText));
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
return clicked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
11
Runtime/ParticleSystemPreviewer.cs.meta
Normal file
11
Runtime/ParticleSystemPreviewer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b171deb49fb7b471291108ad7e1c9baa
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -9,12 +9,16 @@ using UnityEngine.UI;
|
|||||||
using Random = UnityEngine.Random;
|
using Random = UnityEngine.Random;
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("Coffee.UIParticle.Editor")]
|
[assembly: InternalsVisibleTo("Coffee.UIParticle.Editor")]
|
||||||
|
[assembly: InternalsVisibleTo("Coffee.UIParticle.Editor.Tests")]
|
||||||
|
[assembly: InternalsVisibleTo("Coffee.UIParticle.PerformanceDemo")]
|
||||||
|
[assembly: InternalsVisibleTo("Coffee.UIParticle.Demo")]
|
||||||
|
|
||||||
namespace Coffee.UIExtensions
|
namespace Coffee.UIExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Render maskable and sortable particle effect ,without Camera, RenderTexture or Canvas.
|
/// Render maskable and sortable particle effect ,without Camera, RenderTexture or Canvas.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Icon("Packages/com.coffee.ui-particle/Editor/UIParticleIcon.png")]
|
||||||
[ExecuteAlways]
|
[ExecuteAlways]
|
||||||
[RequireComponent(typeof(RectTransform))]
|
[RequireComponent(typeof(RectTransform))]
|
||||||
[RequireComponent(typeof(CanvasRenderer))]
|
[RequireComponent(typeof(CanvasRenderer))]
|
||||||
@@ -117,6 +121,10 @@ namespace Coffee.UIExtensions
|
|||||||
"Change the bake view size.")]
|
"Change the bake view size.")]
|
||||||
private float m_CustomViewSize = 10;
|
private float m_CustomViewSize = 10;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
[Tooltip("Time scale multiplier.")]
|
||||||
|
private float m_TimeScaleMultiplier = 1;
|
||||||
|
|
||||||
private readonly List<UIParticleRenderer> _renderers = new List<UIParticleRenderer>();
|
private readonly List<UIParticleRenderer> _renderers = new List<UIParticleRenderer>();
|
||||||
private Camera _bakeCamera;
|
private Camera _bakeCamera;
|
||||||
private int _groupId;
|
private int _groupId;
|
||||||
@@ -255,6 +263,22 @@ namespace Coffee.UIExtensions
|
|||||||
set => m_CustomViewSize = Mathf.Max(0.1f, value);
|
set => m_CustomViewSize = Mathf.Max(0.1f, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// View size for Baking.
|
||||||
|
/// </summary>
|
||||||
|
public float viewSizeForBaking => useCustomView
|
||||||
|
? m_CustomViewSize
|
||||||
|
: UIParticleProjectSettings.defaultViewSizeForBaking;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Time scale multiplier.
|
||||||
|
/// </summary>
|
||||||
|
public float timeScaleMultiplier
|
||||||
|
{
|
||||||
|
get => m_TimeScaleMultiplier;
|
||||||
|
set => m_TimeScaleMultiplier = value;
|
||||||
|
}
|
||||||
|
|
||||||
internal bool useMeshSharing => m_MeshSharing != MeshSharing.None;
|
internal bool useMeshSharing => m_MeshSharing != MeshSharing.None;
|
||||||
|
|
||||||
internal bool isPrimary =>
|
internal bool isPrimary =>
|
||||||
@@ -341,6 +365,7 @@ namespace Coffee.UIExtensions
|
|||||||
|
|
||||||
_isScaleStored = false;
|
_isScaleStored = false;
|
||||||
UIParticleUpdater.Unregister(this);
|
UIParticleUpdater.Unregister(this);
|
||||||
|
_renderers.RemoveAll(r => r == null);
|
||||||
_renderers.ForEach(r => r.Reset());
|
_renderers.ForEach(r => r.Reset());
|
||||||
UnregisterDirtyMaterialCallback(UpdateRendererMaterial);
|
UnregisterDirtyMaterialCallback(UpdateRendererMaterial);
|
||||||
|
|
||||||
@@ -454,7 +479,7 @@ namespace Coffee.UIExtensions
|
|||||||
for (var i = 0; i < _renderers.Count; i++)
|
for (var i = 0; i < _renderers.Count; i++)
|
||||||
{
|
{
|
||||||
var r = _renderers[i];
|
var r = _renderers[i];
|
||||||
if (!r || !r.material) continue;
|
if (r == null || r.material == null) continue;
|
||||||
result.Add(r.material);
|
result.Add(r.material);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -472,7 +497,7 @@ namespace Coffee.UIExtensions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetParticleSystemInstance(GameObject instance, bool destroyOldParticles)
|
public void SetParticleSystemInstance(GameObject instance, bool destroyOldParticles)
|
||||||
{
|
{
|
||||||
if (!instance) return;
|
if (instance == null) return;
|
||||||
|
|
||||||
var childCount = transform.childCount;
|
var childCount = transform.childCount;
|
||||||
for (var i = 0; i < childCount; i++)
|
for (var i = 0; i < childCount; i++)
|
||||||
@@ -501,7 +526,7 @@ namespace Coffee.UIExtensions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetParticleSystemPrefab(GameObject prefab)
|
public void SetParticleSystemPrefab(GameObject prefab)
|
||||||
{
|
{
|
||||||
if (!prefab) return;
|
if (prefab == null) return;
|
||||||
|
|
||||||
SetParticleSystemInstance(Instantiate(prefab.gameObject), true);
|
SetParticleSystemInstance(Instantiate(prefab.gameObject), true);
|
||||||
}
|
}
|
||||||
@@ -521,12 +546,17 @@ namespace Coffee.UIExtensions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void RefreshParticles(GameObject root)
|
private void RefreshParticles(GameObject root)
|
||||||
{
|
{
|
||||||
if (!root) return;
|
if (root == null) return;
|
||||||
root.GetComponentsInChildren(true, particles);
|
root.GetComponentsInChildren(true, particles);
|
||||||
for (var i = particles.Count - 1; 0 <= i; i--)
|
for (var i = particles.Count - 1; 0 <= i; i--)
|
||||||
{
|
{
|
||||||
var ps = particles[i];
|
var ps = particles[i];
|
||||||
if (!ps || ps.GetComponentInParent<UIParticle>(true) != this)
|
if (!ps
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
|| (ps.hideFlags & HideFlags.DontSave) != 0 // Dummy ParticleSystems for preview.
|
||||||
|
|| ps.gameObject.CompareTag("EditorOnly") // Ignore "EditorOnly" tagged ParticleSystems.
|
||||||
|
#endif
|
||||||
|
|| ps.GetComponentInParent<UIParticle>(true) != this) // Ignore ParticleSystems that are not under this UIParticle.
|
||||||
{
|
{
|
||||||
particles.RemoveAt(i);
|
particles.RemoveAt(i);
|
||||||
}
|
}
|
||||||
@@ -574,13 +604,15 @@ namespace Coffee.UIExtensions
|
|||||||
for (var i = 0; i < particleSystems.Count; i++)
|
for (var i = 0; i < particleSystems.Count; i++)
|
||||||
{
|
{
|
||||||
var ps = particleSystems[i];
|
var ps = particleSystems[i];
|
||||||
if (!ps) continue;
|
if (ps == null) continue;
|
||||||
GetRenderer(j++).Set(this, ps, false);
|
|
||||||
|
var mainEmitter = ps.GetMainEmitter(particleSystems);
|
||||||
|
GetRenderer(j++).Set(this, ps, false, mainEmitter);
|
||||||
|
|
||||||
// If the trail is enabled, set it additionally.
|
// If the trail is enabled, set it additionally.
|
||||||
if (ps.trails.enabled)
|
if (ps.trails.enabled)
|
||||||
{
|
{
|
||||||
GetRenderer(j++).Set(this, ps, true);
|
GetRenderer(j++).Set(this, ps, true, mainEmitter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -623,7 +655,7 @@ namespace Coffee.UIExtensions
|
|||||||
for (var i = 0; i < _renderers.Count; i++)
|
for (var i = 0; i < _renderers.Count; i++)
|
||||||
{
|
{
|
||||||
var r = _renderers[i];
|
var r = _renderers[i];
|
||||||
if (r) continue;
|
if (r != null) continue;
|
||||||
|
|
||||||
RefreshParticles(particles);
|
RefreshParticles(particles);
|
||||||
break;
|
break;
|
||||||
@@ -633,7 +665,7 @@ namespace Coffee.UIExtensions
|
|||||||
for (var i = 0; i < _renderers.Count; i++)
|
for (var i = 0; i < _renderers.Count; i++)
|
||||||
{
|
{
|
||||||
var r = _renderers[i];
|
var r = _renderers[i];
|
||||||
if (!r) continue;
|
if (r == null) continue;
|
||||||
|
|
||||||
r.UpdateMesh(bakeCamera);
|
r.UpdateMesh(bakeCamera);
|
||||||
}
|
}
|
||||||
@@ -662,7 +694,7 @@ namespace Coffee.UIExtensions
|
|||||||
for (var i = 0; i < _renderers.Count; i++)
|
for (var i = 0; i < _renderers.Count; i++)
|
||||||
{
|
{
|
||||||
var r = _renderers[i];
|
var r = _renderers[i];
|
||||||
if (!r) continue;
|
if (r == null) continue;
|
||||||
r.maskable = maskable;
|
r.maskable = maskable;
|
||||||
r.SetMaterialDirty();
|
r.SetMaterialDirty();
|
||||||
}
|
}
|
||||||
@@ -675,7 +707,7 @@ namespace Coffee.UIExtensions
|
|||||||
_renderers.Add(UIParticleRenderer.AddRenderer(this, index));
|
_renderers.Add(UIParticleRenderer.AddRenderer(this, index));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_renderers[index])
|
if (_renderers[index] == null)
|
||||||
{
|
{
|
||||||
_renderers[index] = UIParticleRenderer.AddRenderer(this, index);
|
_renderers[index] = UIParticleRenderer.AddRenderer(this, index);
|
||||||
}
|
}
|
||||||
@@ -685,15 +717,15 @@ namespace Coffee.UIExtensions
|
|||||||
|
|
||||||
private Camera GetBakeCamera()
|
private Camera GetBakeCamera()
|
||||||
{
|
{
|
||||||
if (!canvas) return Camera.main;
|
if (canvas == null) return Camera.main;
|
||||||
if (!useCustomView && canvas.renderMode != RenderMode.ScreenSpaceOverlay && canvas.rootCanvas.worldCamera)
|
if (!useCustomView && canvas.renderMode != RenderMode.ScreenSpaceOverlay && canvas.rootCanvas.worldCamera)
|
||||||
{
|
{
|
||||||
return canvas.rootCanvas.worldCamera;
|
return canvas.rootCanvas.worldCamera;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_bakeCamera)
|
if (_bakeCamera != null)
|
||||||
{
|
{
|
||||||
_bakeCamera.orthographicSize = useCustomView ? customViewSize : 10;
|
_bakeCamera.orthographicSize = viewSizeForBaking;
|
||||||
return _bakeCamera;
|
return _bakeCamera;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -710,7 +742,7 @@ namespace Coffee.UIExtensions
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create baking camera.
|
// Create baking camera.
|
||||||
if (!_bakeCamera)
|
if (_bakeCamera == null)
|
||||||
{
|
{
|
||||||
var go = new GameObject("[generated] UIParticle BakingCamera");
|
var go = new GameObject("[generated] UIParticle BakingCamera");
|
||||||
go.SetActive(false);
|
go.SetActive(false);
|
||||||
@@ -720,7 +752,7 @@ namespace Coffee.UIExtensions
|
|||||||
|
|
||||||
// Setup baking camera.
|
// Setup baking camera.
|
||||||
_bakeCamera.enabled = false;
|
_bakeCamera.enabled = false;
|
||||||
_bakeCamera.orthographicSize = useCustomView ? customViewSize : 10;
|
_bakeCamera.orthographicSize = viewSizeForBaking;
|
||||||
_bakeCamera.transform.SetPositionAndRotation(new Vector3(0, 0, -1000), Quaternion.identity);
|
_bakeCamera.transform.SetPositionAndRotation(new Vector3(0, 0, -1000), Quaternion.identity);
|
||||||
_bakeCamera.orthographic = true;
|
_bakeCamera.orthographic = true;
|
||||||
_bakeCamera.farClipPlane = 2000f;
|
_bakeCamera.farClipPlane = 2000f;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ MonoImporter:
|
|||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
defaultReferences: []
|
defaultReferences: []
|
||||||
executionOrder: 0
|
executionOrder: 0
|
||||||
icon: {fileID: 2800000, guid: 5f0675613942149309588d556e33d990, type: 3}
|
icon: {instanceID: 0}
|
||||||
userData:
|
userData:
|
||||||
assetBundleName:
|
assetBundleName:
|
||||||
assetBundleVariant:
|
assetBundleVariant:
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ namespace Coffee.UIExtensions
|
|||||||
|
|
||||||
private Vector3 GetDestinationPosition(UIParticle uiParticle, ParticleSystem particleSystem)
|
private Vector3 GetDestinationPosition(UIParticle uiParticle, ParticleSystem particleSystem)
|
||||||
{
|
{
|
||||||
var isUI = uiParticle && uiParticle.enabled;
|
var isUI = uiParticle != null && uiParticle.enabled;
|
||||||
var psPos = particleSystem.transform.position;
|
var psPos = particleSystem.transform.position;
|
||||||
var attractorPos = transform.position;
|
var attractorPos = transform.position;
|
||||||
var dstPos = attractorPos;
|
var dstPos = attractorPos;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Coffee.UIParticleInternal;
|
using Coffee.UIParticleInternal;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityEngine.Serialization;
|
||||||
|
|
||||||
namespace Coffee.UIExtensions
|
namespace Coffee.UIExtensions
|
||||||
{
|
{
|
||||||
@@ -9,14 +10,25 @@ namespace Coffee.UIExtensions
|
|||||||
{
|
{
|
||||||
[Header("Setting")]
|
[Header("Setting")]
|
||||||
[SerializeField]
|
[SerializeField]
|
||||||
internal bool m_EnableLinearToGamma = true;
|
[Tooltip("Automatically correct the color space of the mesh.")]
|
||||||
|
[FormerlySerializedAs("m_EnableLinearToGamma")]
|
||||||
|
private bool m_AutoColorCorrection = true;
|
||||||
|
|
||||||
public static bool enableLinearToGamma
|
public static bool autoColorCorrection
|
||||||
{
|
{
|
||||||
get => instance.m_EnableLinearToGamma;
|
get => instance.m_AutoColorCorrection;
|
||||||
set => instance.m_EnableLinearToGamma = value;
|
set => instance.m_AutoColorCorrection = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
[Tooltip("Default view size for baking particle systems.")]
|
||||||
|
private float m_DefaultViewSizeForBaking = 10;
|
||||||
|
|
||||||
|
public static float defaultViewSizeForBaking
|
||||||
|
{
|
||||||
|
get => instance.m_DefaultViewSizeForBaking;
|
||||||
|
set => instance.m_DefaultViewSizeForBaking = value;
|
||||||
|
}
|
||||||
|
|
||||||
[Header("Editor")]
|
[Header("Editor")]
|
||||||
[Tooltip("Hide the automatically generated objects.\n" +
|
[Tooltip("Hide the automatically generated objects.\n" +
|
||||||
@@ -25,16 +37,32 @@ namespace Coffee.UIExtensions
|
|||||||
[SerializeField]
|
[SerializeField]
|
||||||
private bool m_HideGeneratedObjects = true;
|
private bool m_HideGeneratedObjects = true;
|
||||||
|
|
||||||
public static HideFlags globalHideFlags => instance.m_HideGeneratedObjects
|
[Tooltip("When selecting UIParticle, a temporary ParticleSystem is generated for preview.")]
|
||||||
|
[SerializeField]
|
||||||
|
private bool m_PreviewOnSelect = true;
|
||||||
|
|
||||||
|
internal static HideFlags globalHideFlags => instance.m_HideGeneratedObjects
|
||||||
? HideFlags.DontSave | HideFlags.NotEditable | HideFlags.HideInHierarchy | HideFlags.HideInInspector
|
? HideFlags.DontSave | HideFlags.NotEditable | HideFlags.HideInHierarchy | HideFlags.HideInInspector
|
||||||
: HideFlags.DontSave | HideFlags.NotEditable;
|
: HideFlags.DontSave | HideFlags.NotEditable;
|
||||||
|
|
||||||
|
internal static bool previewOnSelect => instance.m_PreviewOnSelect;
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
[SettingsProvider]
|
[SettingsProvider]
|
||||||
private static SettingsProvider CreateSettingsProvider()
|
private static SettingsProvider CreateSettingsProvider()
|
||||||
{
|
{
|
||||||
return new PreloadedProjectSettingsProvider("Project/UI/UI Particle");
|
return new PreloadedProjectSettingsProvider("Project/UI/UI Particle");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CustomEditor(typeof(UIParticleProjectSettings))]
|
||||||
|
private class UIParticleProjectSettingsEditor : Editor
|
||||||
|
{
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
EditorGUIUtility.labelWidth = 180;
|
||||||
|
base.OnInspectorGUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ using UnityEngine.UI;
|
|||||||
|
|
||||||
namespace Coffee.UIExtensions
|
namespace Coffee.UIExtensions
|
||||||
{
|
{
|
||||||
|
[Icon("Packages/com.coffee.ui-particle/Editor/UIParticleIcon.png")]
|
||||||
[ExecuteAlways]
|
[ExecuteAlways]
|
||||||
[RequireComponent(typeof(RectTransform))]
|
[RequireComponent(typeof(RectTransform))]
|
||||||
[RequireComponent(typeof(CanvasRenderer))]
|
[RequireComponent(typeof(CanvasRenderer))]
|
||||||
@@ -29,6 +30,7 @@ namespace Coffee.UIExtensions
|
|||||||
private int _index;
|
private int _index;
|
||||||
private bool _isPrevStored;
|
private bool _isPrevStored;
|
||||||
private bool _isTrail;
|
private bool _isTrail;
|
||||||
|
private bool _meshCleared;
|
||||||
private Bounds _lastBounds;
|
private Bounds _lastBounds;
|
||||||
private Material _materialForRendering;
|
private Material _materialForRendering;
|
||||||
private Material _modifiedMaterial;
|
private Material _modifiedMaterial;
|
||||||
@@ -40,6 +42,7 @@ namespace Coffee.UIExtensions
|
|||||||
private Vector2Int _prevScreenSize;
|
private Vector2Int _prevScreenSize;
|
||||||
private bool _preWarm;
|
private bool _preWarm;
|
||||||
private ParticleSystemRenderer _renderer;
|
private ParticleSystemRenderer _renderer;
|
||||||
|
private ParticleSystem _mainEmitter;
|
||||||
|
|
||||||
public override Texture mainTexture => _isTrail ? null : _particleSystem.GetTextureForSprite();
|
public override Texture mainTexture => _isTrail ? null : _particleSystem.GetTextureForSprite();
|
||||||
|
|
||||||
@@ -53,7 +56,7 @@ namespace Coffee.UIExtensions
|
|||||||
s_Corners[1] = transform.TransformPoint(_lastBounds.min.x, _lastBounds.max.y, 0);
|
s_Corners[1] = transform.TransformPoint(_lastBounds.min.x, _lastBounds.max.y, 0);
|
||||||
s_Corners[2] = transform.TransformPoint(_lastBounds.max.x, _lastBounds.max.y, 0);
|
s_Corners[2] = transform.TransformPoint(_lastBounds.max.x, _lastBounds.max.y, 0);
|
||||||
s_Corners[3] = transform.TransformPoint(_lastBounds.max.x, _lastBounds.min.y, 0);
|
s_Corners[3] = transform.TransformPoint(_lastBounds.max.x, _lastBounds.min.y, 0);
|
||||||
if (canvas)
|
if (canvas != null)
|
||||||
{
|
{
|
||||||
var worldToLocalMatrix = canvas.rootCanvas.transform.worldToLocalMatrix;
|
var worldToLocalMatrix = canvas.rootCanvas.transform.worldToLocalMatrix;
|
||||||
for (var i = 0; i < 4; ++i)
|
for (var i = 0; i < 4; ++i)
|
||||||
@@ -93,7 +96,7 @@ namespace Coffee.UIExtensions
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!_materialForRendering)
|
if (_materialForRendering == null)
|
||||||
{
|
{
|
||||||
_materialForRendering = base.materialForRendering;
|
_materialForRendering = base.materialForRendering;
|
||||||
}
|
}
|
||||||
@@ -104,7 +107,7 @@ namespace Coffee.UIExtensions
|
|||||||
|
|
||||||
public void Reset(int index = -1)
|
public void Reset(int index = -1)
|
||||||
{
|
{
|
||||||
if (_renderer)
|
if (_renderer != null)
|
||||||
{
|
{
|
||||||
_renderer.enabled = true;
|
_renderer.enabled = true;
|
||||||
}
|
}
|
||||||
@@ -112,13 +115,14 @@ namespace Coffee.UIExtensions
|
|||||||
_parent = null;
|
_parent = null;
|
||||||
_particleSystem = null;
|
_particleSystem = null;
|
||||||
_renderer = null;
|
_renderer = null;
|
||||||
|
_mainEmitter = null;
|
||||||
if (0 <= index)
|
if (0 <= index)
|
||||||
{
|
{
|
||||||
_index = index;
|
_index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
//_emitter = null;
|
//_emitter = null;
|
||||||
if (this && isActiveAndEnabled)
|
if (isActiveAndEnabled)
|
||||||
{
|
{
|
||||||
material = null;
|
material = null;
|
||||||
canvasRenderer.Clear();
|
canvasRenderer.Clear();
|
||||||
@@ -137,7 +141,7 @@ namespace Coffee.UIExtensions
|
|||||||
base.OnEnable();
|
base.OnEnable();
|
||||||
|
|
||||||
hideFlags = UIParticleProjectSettings.globalHideFlags;
|
hideFlags = UIParticleProjectSettings.globalHideFlags;
|
||||||
if (!s_CombineInstances[0].mesh)
|
if (s_CombineInstances[0].mesh == null)
|
||||||
{
|
{
|
||||||
s_CombineInstances[0].mesh = new Mesh
|
s_CombineInstances[0].mesh = new Mesh
|
||||||
{
|
{
|
||||||
@@ -202,9 +206,9 @@ namespace Coffee.UIExtensions
|
|||||||
}
|
}
|
||||||
|
|
||||||
var hash = new Hash128(
|
var hash = new Hash128(
|
||||||
modifiedMaterial ? (uint)modifiedMaterial.GetInstanceID() : 0,
|
modifiedMaterial ? (uint)modifiedMaterial.GetHashCode() : 0,
|
||||||
texture ? (uint)texture.GetInstanceID() : 0,
|
texture ? (uint)texture.GetHashCode() : 0,
|
||||||
0 < _parent.m_AnimatableProperties.Length ? (uint)GetInstanceID() : 0,
|
0 < _parent.m_AnimatableProperties.Length ? (uint)GetHashCode() : 0,
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
(uint)EditorJsonUtility.ToJson(modifiedMaterial).GetHashCode()
|
(uint)EditorJsonUtility.ToJson(modifiedMaterial).GetHashCode()
|
||||||
#else
|
#else
|
||||||
@@ -223,7 +227,7 @@ namespace Coffee.UIExtensions
|
|||||||
return _modifiedMaterial;
|
return _modifiedMaterial;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Set(UIParticle parent, ParticleSystem ps, bool isTrail)
|
public void Set(UIParticle parent, ParticleSystem ps, bool isTrail, ParticleSystem mainEmitter)
|
||||||
{
|
{
|
||||||
_parent = parent;
|
_parent = parent;
|
||||||
maskable = parent.maskable;
|
maskable = parent.maskable;
|
||||||
@@ -246,10 +250,7 @@ namespace Coffee.UIExtensions
|
|||||||
|
|
||||||
ps.TryGetComponent(out _renderer);
|
ps.TryGetComponent(out _renderer);
|
||||||
_renderer.enabled = false;
|
_renderer.enabled = false;
|
||||||
|
|
||||||
//_emitter = emitter;
|
|
||||||
_isTrail = isTrail;
|
_isTrail = isTrail;
|
||||||
|
|
||||||
_renderer.GetSharedMaterials(s_Materials);
|
_renderer.GetSharedMaterials(s_Materials);
|
||||||
material = s_Materials[isTrail ? 1 : 0];
|
material = s_Materials[isTrail ? 1 : 0];
|
||||||
s_Materials.Clear();
|
s_Materials.Clear();
|
||||||
@@ -266,6 +267,7 @@ namespace Coffee.UIExtensions
|
|||||||
_prevScreenSize = new Vector2Int(Screen.width, Screen.height);
|
_prevScreenSize = new Vector2Int(Screen.width, Screen.height);
|
||||||
_prevCanvasScale = canvas ? canvas.scaleFactor : 1f;
|
_prevCanvasScale = canvas ? canvas.scaleFactor : 1f;
|
||||||
_delay = true;
|
_delay = true;
|
||||||
|
_mainEmitter = mainEmitter;
|
||||||
|
|
||||||
canvasRenderer.SetTexture(null);
|
canvasRenderer.SetTexture(null);
|
||||||
|
|
||||||
@@ -282,28 +284,47 @@ namespace Coffee.UIExtensions
|
|||||||
|| !transform.lossyScale.GetScaled(_parent.scale3DForCalc).IsVisible() // Scale is not visible.
|
|| !transform.lossyScale.GetScaled(_parent.scale3DForCalc).IsVisible() // Scale is not visible.
|
||||||
|| (!_particleSystem.IsAlive() && !_particleSystem.isPlaying) // No particle.
|
|| (!_particleSystem.IsAlive() && !_particleSystem.isPlaying) // No particle.
|
||||||
|| (_isTrail && !_particleSystem.trails.enabled) // Trail, but it is not enabled.
|
|| (_isTrail && !_particleSystem.trails.enabled) // Trail, but it is not enabled.
|
||||||
#if UNITY_2018_3_OR_NEWER
|
|
||||||
|| canvasRenderer.GetInheritedAlpha() <
|
|
||||||
0.01f // #102: Do not bake particle system to mesh when the alpha is zero.
|
|
||||||
#endif
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
// Skip clearing the mesh if it's already cleared.
|
||||||
|
if (_meshCleared) return;
|
||||||
|
|
||||||
Profiler.BeginSample("[UIParticleRenderer] Clear Mesh");
|
Profiler.BeginSample("[UIParticleRenderer] Clear Mesh");
|
||||||
workerMesh.Clear();
|
workerMesh.Clear();
|
||||||
canvasRenderer.SetMesh(workerMesh);
|
canvasRenderer.SetMesh(workerMesh);
|
||||||
_lastBounds = new Bounds();
|
_lastBounds = new Bounds();
|
||||||
|
_meshCleared = true;
|
||||||
Profiler.EndSample();
|
Profiler.EndSample();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset custom data.
|
||||||
|
// var customData = _particleSystem.customData;
|
||||||
|
// if (!customData.enabled || customData.GetMode(ParticleSystemCustomData.Custom1) == ParticleSystemCustomDataMode.Disabled)
|
||||||
|
// {
|
||||||
|
// customData.SetVector(ParticleSystemCustomData.Custom1, 0, 0);
|
||||||
|
// customData.SetVector(ParticleSystemCustomData.Custom1, 1, 0);
|
||||||
|
// customData.SetVector(ParticleSystemCustomData.Custom1, 2, 0);
|
||||||
|
// customData.SetVector(ParticleSystemCustomData.Custom1, 3, 0);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (!customData.enabled || customData.GetMode(ParticleSystemCustomData.Custom2) == ParticleSystemCustomDataMode.Disabled)
|
||||||
|
// {
|
||||||
|
// customData.SetVector(ParticleSystemCustomData.Custom2, 0, 0);
|
||||||
|
// customData.SetVector(ParticleSystemCustomData.Custom2, 1, 0);
|
||||||
|
// customData.SetVector(ParticleSystemCustomData.Custom2, 2, 0);
|
||||||
|
// customData.SetVector(ParticleSystemCustomData.Custom2, 3, 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
_meshCleared = false;
|
||||||
var main = _particleSystem.main;
|
var main = _particleSystem.main;
|
||||||
var scale = GetWorldScale();
|
var scale = GetWorldScale();
|
||||||
var psPos = _particleSystem.transform.position;
|
var psPos = _particleSystem.transform.position;
|
||||||
|
|
||||||
// Simulate particles.
|
// Simulate particles.
|
||||||
Profiler.BeginSample("[UIParticle] Bake Mesh > Simulate Particles");
|
Profiler.BeginSample("[UIParticle] Bake Mesh > Simulate Particles");
|
||||||
if (!_isTrail && _parent.canSimulate)
|
if (!_isTrail && _parent.canSimulate && !_mainEmitter)
|
||||||
{
|
{
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
if (!Application.isPlaying)
|
if (!Application.isPlaying)
|
||||||
@@ -314,6 +335,13 @@ namespace Coffee.UIExtensions
|
|||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
ResolveResolutionChange(psPos, scale);
|
ResolveResolutionChange(psPos, scale);
|
||||||
|
|
||||||
|
// fix: second and subsequent bursts not displayed when world simulation and non-looping. (#326)
|
||||||
|
if (!_particleSystem.IsLocalSpace() && !main.loop && _particleSystem.time == 0)
|
||||||
|
{
|
||||||
|
_delay = true;
|
||||||
|
}
|
||||||
|
|
||||||
Simulate(scale, _parent.isPaused || _delay);
|
Simulate(scale, _parent.isPaused || _delay);
|
||||||
|
|
||||||
if (_delay && !_parent.isPaused)
|
if (_delay && !_parent.isPaused)
|
||||||
@@ -416,12 +444,12 @@ namespace Coffee.UIExtensions
|
|||||||
_lastBounds = bounds;
|
_lastBounds = bounds;
|
||||||
|
|
||||||
// Convert linear color to gamma color.
|
// Convert linear color to gamma color.
|
||||||
if (UIParticleProjectSettings.enableLinearToGamma && canvas.ShouldGammaToLinearInMesh())
|
if (UIParticleProjectSettings.autoColorCorrection && canvas.ShouldGammaToLinearInMesh())
|
||||||
{
|
{
|
||||||
workerMesh.LinearToGamma();
|
workerMesh.LinearToGamma();
|
||||||
}
|
}
|
||||||
|
|
||||||
var components = ListPool<Component>.Rent();
|
var components = InternalListPool<Component>.Rent();
|
||||||
GetComponents(typeof(IMeshModifier), components);
|
GetComponents(typeof(IMeshModifier), components);
|
||||||
for (var i = 0; i < components.Count; i++)
|
for (var i = 0; i < components.Count; i++)
|
||||||
{
|
{
|
||||||
@@ -430,7 +458,7 @@ namespace Coffee.UIExtensions
|
|||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
#pragma warning restore CS0618 // Type or member is obsolete
|
||||||
}
|
}
|
||||||
|
|
||||||
ListPool<Component>.Return(ref components);
|
InternalListPool<Component>.Return(ref components);
|
||||||
}
|
}
|
||||||
|
|
||||||
Profiler.EndSample();
|
Profiler.EndSample();
|
||||||
@@ -442,7 +470,7 @@ namespace Coffee.UIExtensions
|
|||||||
|
|
||||||
// Get grouped renderers.
|
// Get grouped renderers.
|
||||||
Profiler.BeginSample("[UIParticleRenderer] Set Mesh");
|
Profiler.BeginSample("[UIParticleRenderer] Set Mesh");
|
||||||
var renderers = ListPool<UIParticleRenderer>.Rent();
|
var renderers = InternalListPool<UIParticleRenderer>.Rent();
|
||||||
if (_parent.useMeshSharing)
|
if (_parent.useMeshSharing)
|
||||||
{
|
{
|
||||||
UIParticleUpdater.GetGroupedRenderers(_parent.groupId, _index, renderers);
|
UIParticleUpdater.GetGroupedRenderers(_parent.groupId, _index, renderers);
|
||||||
@@ -459,7 +487,7 @@ namespace Coffee.UIExtensions
|
|||||||
r.canvasRenderer.SetMaterial(materialForRendering, 0);
|
r.canvasRenderer.SetMaterial(materialForRendering, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ListPool<UIParticleRenderer>.Return(ref renderers);
|
InternalListPool<UIParticleRenderer>.Return(ref renderers);
|
||||||
|
|
||||||
if (_parent.canRender)
|
if (_parent.canRender)
|
||||||
{
|
{
|
||||||
@@ -548,6 +576,24 @@ namespace Coffee.UIExtensions
|
|||||||
* Matrix4x4.Scale(scale)
|
* Matrix4x4.Scale(scale)
|
||||||
* Matrix4x4.Translate(-psPos);
|
* Matrix4x4.Translate(-psPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_mainEmitter)
|
||||||
|
{
|
||||||
|
if (_mainEmitter.IsLocalSpace())
|
||||||
|
{
|
||||||
|
return Matrix4x4.Translate(psPos)
|
||||||
|
* Matrix4x4.Scale(scale)
|
||||||
|
* Matrix4x4.Translate(-psPos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
psPos = _particleSystem.transform.position - _mainEmitter.transform.position;
|
||||||
|
return Matrix4x4.Translate(psPos)
|
||||||
|
* Matrix4x4.Scale(scale)
|
||||||
|
* Matrix4x4.Translate(-psPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Matrix4x4.Scale(scale);
|
return Matrix4x4.Scale(scale);
|
||||||
case ParticleSystemSimulationSpace.Custom:
|
case ParticleSystemSimulationSpace.Custom:
|
||||||
return Matrix4x4.Translate(_particleSystem.main.customSimulationSpace.position.GetScaled(scale))
|
return Matrix4x4.Translate(_particleSystem.main.customSimulationSpace.position.GetScaled(scale))
|
||||||
@@ -610,6 +656,7 @@ namespace Coffee.UIExtensions
|
|||||||
: main.useUnscaledTime
|
: main.useUnscaledTime
|
||||||
? Time.unscaledDeltaTime
|
? Time.unscaledDeltaTime
|
||||||
: Time.deltaTime;
|
: Time.deltaTime;
|
||||||
|
deltaTime *= _parent.timeScaleMultiplier;
|
||||||
|
|
||||||
// Pre-warm:
|
// Pre-warm:
|
||||||
if (0 < deltaTime && _preWarm)
|
if (0 < deltaTime && _preWarm)
|
||||||
@@ -690,7 +737,7 @@ namespace Coffee.UIExtensions
|
|||||||
if (s_Mpb.isEmpty) return;
|
if (s_Mpb.isEmpty) return;
|
||||||
|
|
||||||
// #41: Copy the value from MaterialPropertyBlock to CanvasRenderer
|
// #41: Copy the value from MaterialPropertyBlock to CanvasRenderer
|
||||||
if (!materialForRendering) return;
|
if (materialForRendering == null) return;
|
||||||
|
|
||||||
for (var i = 0; i < _parent.m_AnimatableProperties.Length; i++)
|
for (var i = 0; i < _parent.m_AnimatableProperties.Length; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ MonoImporter:
|
|||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
defaultReferences: []
|
defaultReferences: []
|
||||||
executionOrder: 0
|
executionOrder: 0
|
||||||
icon: {fileID: 2800000, guid: 5f0675613942149309588d556e33d990, type: 3}
|
icon: {instanceID: 0}
|
||||||
userData:
|
userData:
|
||||||
assetBundleName:
|
assetBundleName:
|
||||||
assetBundleVariant:
|
assetBundleVariant:
|
||||||
|
|||||||
@@ -16,37 +16,61 @@ namespace Coffee.UIExtensions
|
|||||||
|
|
||||||
public static void Register(UIParticle particle)
|
public static void Register(UIParticle particle)
|
||||||
{
|
{
|
||||||
if (!particle) return;
|
if (particle == null) return;
|
||||||
s_ActiveParticles.Add(particle);
|
s_ActiveParticles.Add(particle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Unregister(UIParticle particle)
|
public static void Unregister(UIParticle particle)
|
||||||
{
|
{
|
||||||
if (!particle) return;
|
if (particle == null) return;
|
||||||
s_ActiveParticles.Remove(particle);
|
s_ActiveParticles.Remove(particle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Register(UIParticleAttractor attractor)
|
public static void Register(UIParticleAttractor attractor)
|
||||||
{
|
{
|
||||||
if (!attractor) return;
|
if (attractor == null) return;
|
||||||
s_ActiveAttractors.Add(attractor);
|
s_ActiveAttractors.Add(attractor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Unregister(UIParticleAttractor attractor)
|
public static void Unregister(UIParticleAttractor attractor)
|
||||||
{
|
{
|
||||||
if (!attractor) return;
|
if (attractor == null) return;
|
||||||
s_ActiveAttractors.Remove(attractor);
|
s_ActiveAttractors.Remove(attractor);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
|
#if UNITY_2019_3_OR_NEWER
|
||||||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||||
|
private static void OnDomainReload()
|
||||||
|
{
|
||||||
|
s_ActiveParticles.Clear();
|
||||||
|
s_ActiveAttractors.Clear();
|
||||||
|
s_UpdatedGroupIds.Clear();
|
||||||
|
s_FrameCount = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
[InitializeOnLoadMethod]
|
[InitializeOnLoadMethod]
|
||||||
|
private static void InitializeOnLoad()
|
||||||
|
{
|
||||||
|
UIExtraCallbacks.onAfterCanvasRebuild += Refresh;
|
||||||
|
|
||||||
|
EditorApplication.playModeStateChanged += state =>
|
||||||
|
{
|
||||||
|
UIExtraCallbacks.onAfterCanvasRebuild -= Refresh;
|
||||||
|
if (state == PlayModeStateChange.EnteredEditMode || state == PlayModeStateChange.EnteredPlayMode)
|
||||||
|
{
|
||||||
|
UIExtraCallbacks.onAfterCanvasRebuild += Refresh;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||||
#endif
|
|
||||||
private static void InitializeOnLoad()
|
private static void InitializeOnLoad()
|
||||||
{
|
{
|
||||||
UIExtraCallbacks.onAfterCanvasRebuild += Refresh;
|
UIExtraCallbacks.onAfterCanvasRebuild += Refresh;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
private static void Refresh()
|
private static void Refresh()
|
||||||
{
|
{
|
||||||
@@ -58,7 +82,7 @@ namespace Coffee.UIExtensions
|
|||||||
for (var i = 0; i < s_ActiveParticles.Count; i++)
|
for (var i = 0; i < s_ActiveParticles.Count; i++)
|
||||||
{
|
{
|
||||||
var uip = s_ActiveParticles[i];
|
var uip = s_ActiveParticles[i];
|
||||||
if (!uip || !uip.canvas || !uip.isPrimary || !s_UpdatedGroupIds.Add(uip.groupId)) continue;
|
if (uip == null || uip.canvas == null || !uip.isPrimary || !s_UpdatedGroupIds.Add(uip.groupId)) continue;
|
||||||
|
|
||||||
uip.UpdateTransformScale();
|
uip.UpdateTransformScale();
|
||||||
uip.UpdateRenderers();
|
uip.UpdateRenderers();
|
||||||
@@ -68,7 +92,7 @@ namespace Coffee.UIExtensions
|
|||||||
for (var i = 0; i < s_ActiveParticles.Count; i++)
|
for (var i = 0; i < s_ActiveParticles.Count; i++)
|
||||||
{
|
{
|
||||||
var uip = s_ActiveParticles[i];
|
var uip = s_ActiveParticles[i];
|
||||||
if (!uip || !uip.canvas) continue;
|
if (uip == null || uip.canvas == null) continue;
|
||||||
|
|
||||||
uip.UpdateTransformScale();
|
uip.UpdateTransformScale();
|
||||||
|
|
||||||
@@ -112,7 +136,7 @@ namespace Coffee.UIExtensions
|
|||||||
var uip = s_ActiveParticles[i];
|
var uip = s_ActiveParticles[i];
|
||||||
if (!uip.useMeshSharing || uip.groupId != groupId) continue;
|
if (!uip.useMeshSharing || uip.groupId != groupId) continue;
|
||||||
if (uip.isPrimary) return uip;
|
if (uip.isPrimary) return uip;
|
||||||
if (!primary && uip.canSimulate) primary = uip;
|
if (primary == null && uip.canSimulate) primary = uip;
|
||||||
}
|
}
|
||||||
|
|
||||||
return primary;
|
return primary;
|
||||||
|
|||||||
@@ -12,12 +12,8 @@ namespace Coffee.UIParticleInternal
|
|||||||
public static ParticleSystem.Particle[] GetParticleArray(int size)
|
public static ParticleSystem.Particle[] GetParticleArray(int size)
|
||||||
{
|
{
|
||||||
if (s_TmpParticles.Length < size)
|
if (s_TmpParticles.Length < size)
|
||||||
{
|
|
||||||
while (s_TmpParticles.Length < size)
|
|
||||||
{
|
{
|
||||||
size = Mathf.NextPowerOfTwo(size);
|
size = Mathf.NextPowerOfTwo(size);
|
||||||
}
|
|
||||||
|
|
||||||
s_TmpParticles = new ParticleSystem.Particle[size];
|
s_TmpParticles = new ParticleSystem.Particle[size];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,15 +84,15 @@ namespace Coffee.UIParticleInternal
|
|||||||
var bRenderer = b.GetComponent<ParticleSystemRenderer>();
|
var bRenderer = b.GetComponent<ParticleSystemRenderer>();
|
||||||
|
|
||||||
// Render queue: ascending
|
// Render queue: ascending
|
||||||
var aMat = aRenderer.sharedMaterial ? aRenderer.sharedMaterial : aRenderer.trailMaterial;
|
var aMat = aRenderer.sharedMaterial != null ? aRenderer.sharedMaterial : aRenderer.trailMaterial;
|
||||||
var bMat = bRenderer.sharedMaterial ? bRenderer.sharedMaterial : bRenderer.trailMaterial;
|
var bMat = bRenderer.sharedMaterial != null ? bRenderer.sharedMaterial : bRenderer.trailMaterial;
|
||||||
if (!aMat && !bMat) return 0;
|
if (aMat == null && bMat == null) return 0;
|
||||||
if (!aMat) return -1;
|
if (aMat == null) return -1;
|
||||||
if (!bMat) return 1;
|
if (bMat == null) return 1;
|
||||||
|
|
||||||
if (sortByMaterial)
|
if (sortByMaterial)
|
||||||
{
|
{
|
||||||
return aMat.GetInstanceID() - bMat.GetInstanceID();
|
return aMat.GetHashCode() - bMat.GetHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aMat.renderQueue != bMat.renderQueue)
|
if (aMat.renderQueue != bMat.renderQueue)
|
||||||
@@ -135,7 +131,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
{
|
{
|
||||||
for (var i = 0; i < list.Count; i++)
|
for (var i = 0; i < list.Count; i++)
|
||||||
{
|
{
|
||||||
if (list[i].GetInstanceID() == ps.GetInstanceID())
|
if (list[i].GetHashCode() == ps.GetHashCode())
|
||||||
{
|
{
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@@ -146,7 +142,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
|
|
||||||
public static Texture2D GetTextureForSprite(this ParticleSystem self)
|
public static Texture2D GetTextureForSprite(this ParticleSystem self)
|
||||||
{
|
{
|
||||||
if (!self) return null;
|
if (self == null) return null;
|
||||||
|
|
||||||
// Get sprite's texture.
|
// Get sprite's texture.
|
||||||
var tsaModule = self.textureSheetAnimation;
|
var tsaModule = self.textureSheetAnimation;
|
||||||
@@ -155,7 +151,7 @@ namespace Coffee.UIParticleInternal
|
|||||||
for (var i = 0; i < tsaModule.spriteCount; i++)
|
for (var i = 0; i < tsaModule.spriteCount; i++)
|
||||||
{
|
{
|
||||||
var sprite = tsaModule.GetSprite(i);
|
var sprite = tsaModule.GetSprite(i);
|
||||||
if (!sprite) continue;
|
if (sprite == null) continue;
|
||||||
|
|
||||||
return sprite.GetActualTexture();
|
return sprite.GetActualTexture();
|
||||||
}
|
}
|
||||||
@@ -167,9 +163,38 @@ namespace Coffee.UIParticleInternal
|
|||||||
{
|
{
|
||||||
foreach (var p in self)
|
foreach (var p in self)
|
||||||
{
|
{
|
||||||
if (!p) continue;
|
if (p == null) continue;
|
||||||
action.Invoke(p);
|
action.Invoke(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ParticleSystem GetMainEmitter(this ParticleSystem self, List<ParticleSystem> list)
|
||||||
|
{
|
||||||
|
if (self == null || list == null || list.Count == 0) return null;
|
||||||
|
|
||||||
|
for (var i = 0; i < list.Count; i++)
|
||||||
|
{
|
||||||
|
var parent = list[i];
|
||||||
|
if (parent != self && IsSubEmitterOf(self, parent)) return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsSubEmitterOf(this ParticleSystem self, ParticleSystem parent)
|
||||||
|
{
|
||||||
|
if (self == null || parent == null) return false;
|
||||||
|
|
||||||
|
var subEmitters = parent.subEmitters;
|
||||||
|
if (!subEmitters.enabled) return false; // No sub emitters.
|
||||||
|
|
||||||
|
var count = subEmitters.subEmittersCount;
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (subEmitters.GetSubEmitterSystem(i) == self) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
Samples~/Demo/Animations/ColorArrayInjection.cs
Normal file
32
Samples~/Demo/Animations/ColorArrayInjection.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
[RequireComponent(typeof(ParticleSystemRenderer))]
|
||||||
|
public class ColorArrayInjection : MonoBehaviour
|
||||||
|
{
|
||||||
|
[SerializeField] private string m_PropertyName = "_Color2";
|
||||||
|
|
||||||
|
[SerializeField] private Color[] m_Colors = new Color[4]
|
||||||
|
{
|
||||||
|
Color.red,
|
||||||
|
Color.green,
|
||||||
|
Color.blue,
|
||||||
|
Color.yellow
|
||||||
|
};
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
if (TryGetComponent<ParticleSystemRenderer>(out var psr))
|
||||||
|
{
|
||||||
|
var mpb = new MaterialPropertyBlock();
|
||||||
|
psr.GetPropertyBlock(mpb);
|
||||||
|
mpb.SetVectorArray(m_PropertyName, m_Colors.Select(x => new Vector4(x.r, x.g, x.b, 1.0f)).ToArray());
|
||||||
|
psr.SetPropertyBlock(mpb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnValidate()
|
||||||
|
{
|
||||||
|
OnEnable();
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Samples~/Demo/Animations/ColorArrayInjection.cs.meta
Normal file
11
Samples~/Demo/Animations/ColorArrayInjection.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: da4c2e90ecd3d4dd4a520099c08cf493
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
160
Samples~/Demo/Materials/UIAdditive+Color.shader
Normal file
160
Samples~/Demo/Materials/UIAdditive+Color.shader
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
Shader "UI/Additive And Color"
|
||||||
|
{
|
||||||
|
Properties
|
||||||
|
{
|
||||||
|
_MainTex ("Sprite Texture", 2D) = "white" {}
|
||||||
|
_Color ("Tint", Color) = (1,1,1,1)
|
||||||
|
|
||||||
|
_StencilComp ("Stencil Comparison", Float) = 8
|
||||||
|
_Stencil ("Stencil ID", Float) = 0
|
||||||
|
_StencilOp ("Stencil Operation", Float) = 0
|
||||||
|
_StencilWriteMask ("Stencil Write Mask", Float) = 255
|
||||||
|
_StencilReadMask ("Stencil Read Mask", Float) = 255
|
||||||
|
|
||||||
|
_ColorMask ("Color Mask", Float) = 15
|
||||||
|
|
||||||
|
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
SubShader
|
||||||
|
{
|
||||||
|
Tags
|
||||||
|
{
|
||||||
|
"Queue"="Transparent"
|
||||||
|
"IgnoreProjector"="True"
|
||||||
|
"RenderType"="Transparent"
|
||||||
|
"PreviewType"="Plane"
|
||||||
|
"CanUseSpriteAtlas"="True"
|
||||||
|
}
|
||||||
|
|
||||||
|
Stencil
|
||||||
|
{
|
||||||
|
Ref [_Stencil]
|
||||||
|
Comp [_StencilComp]
|
||||||
|
Pass [_StencilOp]
|
||||||
|
ReadMask [_StencilReadMask]
|
||||||
|
WriteMask [_StencilWriteMask]
|
||||||
|
}
|
||||||
|
|
||||||
|
Cull Off
|
||||||
|
Lighting Off
|
||||||
|
ZWrite Off
|
||||||
|
ZTest [unity_GUIZTestMode]
|
||||||
|
Fog { Mode Off }
|
||||||
|
Blend One One
|
||||||
|
ColorMask [_ColorMask]
|
||||||
|
|
||||||
|
Pass
|
||||||
|
{
|
||||||
|
Name "Default"
|
||||||
|
CGPROGRAM
|
||||||
|
#pragma vertex vert
|
||||||
|
#pragma fragment frag
|
||||||
|
#pragma target 2.0
|
||||||
|
|
||||||
|
#include "UnityCG.cginc"
|
||||||
|
#include "UnityUI.cginc"
|
||||||
|
|
||||||
|
#pragma multi_compile __ UNITY_UI_CLIP_RECT
|
||||||
|
#pragma multi_compile __ UNITY_UI_ALPHACLIP
|
||||||
|
|
||||||
|
struct appdata_t
|
||||||
|
{
|
||||||
|
float4 vertex : POSITION;
|
||||||
|
float4 color : COLOR;
|
||||||
|
float2 texcoord : TEXCOORD0;
|
||||||
|
UNITY_VERTEX_INPUT_INSTANCE_ID
|
||||||
|
};
|
||||||
|
|
||||||
|
struct v2f
|
||||||
|
{
|
||||||
|
float4 vertex : SV_POSITION;
|
||||||
|
fixed4 color : COLOR;
|
||||||
|
float2 texcoord : TEXCOORD0;
|
||||||
|
float4 worldPosition : TEXCOORD1;
|
||||||
|
float4 mask : TEXCOORD2;
|
||||||
|
UNITY_VERTEX_OUTPUT_STEREO
|
||||||
|
};
|
||||||
|
|
||||||
|
fixed4 _Color;
|
||||||
|
sampler2D _MainTex;
|
||||||
|
float4 _MainTex_ST;
|
||||||
|
fixed4 _TextureSampleAdd;
|
||||||
|
float4 _ClipRect;
|
||||||
|
float _UIMaskSoftnessX;
|
||||||
|
float _UIMaskSoftnessY;
|
||||||
|
int _UIVertexColorAlwaysGammaSpace;
|
||||||
|
#define LENGTH 4
|
||||||
|
float4 _Color2[4];
|
||||||
|
|
||||||
|
half3 _UIGammaToLinear(half3 value)
|
||||||
|
{
|
||||||
|
half3 low = 0.0849710 * value - 0.000163029;
|
||||||
|
half3 high = value * (value * (value * 0.265885 + 0.736584) - 0.00980184) + 0.00319697;
|
||||||
|
|
||||||
|
// We should be 0.5 away from any actual gamma value stored in an 8 bit channel
|
||||||
|
const half3 split = (half3)0.0725490; // Equals 18.5 / 255
|
||||||
|
return (value < split) ? low : high;
|
||||||
|
}
|
||||||
|
|
||||||
|
v2f vert(appdata_t v)
|
||||||
|
{
|
||||||
|
v2f OUT;
|
||||||
|
UNITY_SETUP_INSTANCE_ID(v);
|
||||||
|
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
|
||||||
|
float4 vPosition = UnityObjectToClipPos(v.vertex);
|
||||||
|
OUT.worldPosition = v.vertex;
|
||||||
|
OUT.vertex = vPosition;
|
||||||
|
|
||||||
|
float2 pixelSize = vPosition.w;
|
||||||
|
pixelSize /= float2(1, 1) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy));
|
||||||
|
|
||||||
|
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
|
||||||
|
float2 maskUV = (v.vertex.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy);
|
||||||
|
OUT.texcoord = TRANSFORM_TEX(v.texcoord.xy, _MainTex);
|
||||||
|
OUT.mask = float4(v.vertex.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_UIMaskSoftnessX, _UIMaskSoftnessY) + abs(pixelSize.xy)));
|
||||||
|
|
||||||
|
if (_UIVertexColorAlwaysGammaSpace)
|
||||||
|
{
|
||||||
|
#ifndef UNITY_COLORSPACE_GAMMA
|
||||||
|
v.color.rgb = _UIGammaToLinear(v.color.rgb);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
OUT.color = v.color * _Color;
|
||||||
|
return OUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed4 frag(v2f IN) : SV_Target
|
||||||
|
{
|
||||||
|
//Round up the alpha color coming from the interpolator (to 1.0/256.0 steps)
|
||||||
|
//The incoming alpha could have numerical instability, which makes it very sensible to
|
||||||
|
//HDR color transparency blend, when it blends with the world's texture.
|
||||||
|
const half alphaPrecision = half(0xff);
|
||||||
|
const half invAlphaPrecision = half(1.0 / alphaPrecision);
|
||||||
|
IN.color.a = round(IN.color.a * alphaPrecision) * invAlphaPrecision;
|
||||||
|
|
||||||
|
half4 color = IN.color * (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd);
|
||||||
|
|
||||||
|
#ifdef UNITY_UI_CLIP_RECT
|
||||||
|
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw);
|
||||||
|
color.a *= m.x * m.y;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef UNITY_UI_ALPHACLIP
|
||||||
|
clip(color.a - 0.001);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
fixed3 fc = _Color2[floor(color.r*LENGTH-0.01)].rgb;
|
||||||
|
if (0.1 < fc.r || 0.1 < fc.g || 0.1 < fc.b)
|
||||||
|
{
|
||||||
|
color.rgb = fc;
|
||||||
|
}
|
||||||
|
color.rgb *= color.a;
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
ENDCG
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Samples~/Demo/Materials/UIAdditive+Color.shader.meta
Normal file
9
Samples~/Demo/Materials/UIAdditive+Color.shader.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9cc9ed37ff19d40e684526abbc1d44a6
|
||||||
|
ShaderImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
defaultTextures: []
|
||||||
|
nonModifiableTextures: []
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
90
Samples~/Demo/Materials/UIParticle_Demo_Animatable+Color.mat
Normal file
90
Samples~/Demo/Materials/UIParticle_Demo_Animatable+Color.mat
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!21 &2100000
|
||||||
|
Material:
|
||||||
|
serializedVersion: 8
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_Name: UIParticle_Demo_Animatable+Color
|
||||||
|
m_Shader: {fileID: 4800000, guid: 9cc9ed37ff19d40e684526abbc1d44a6, type: 3}
|
||||||
|
m_Parent: {fileID: 0}
|
||||||
|
m_ModifiedSerializedProperties: 0
|
||||||
|
m_ValidKeywords: []
|
||||||
|
m_InvalidKeywords: []
|
||||||
|
m_LightmapFlags: 4
|
||||||
|
m_EnableInstancingVariants: 0
|
||||||
|
m_DoubleSidedGI: 0
|
||||||
|
m_CustomRenderQueue: -1
|
||||||
|
stringTagMap: {}
|
||||||
|
disabledShaderPasses: []
|
||||||
|
m_LockedProperties:
|
||||||
|
m_SavedProperties:
|
||||||
|
serializedVersion: 3
|
||||||
|
m_TexEnvs:
|
||||||
|
- _BumpMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _DetailAlbedoMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _DetailMask:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _DetailNormalMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _EmissionMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _MainTex:
|
||||||
|
m_Texture: {fileID: 10300, guid: 0000000000000000f000000000000000, type: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _MetallicGlossMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _OcclusionMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _ParallaxMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
m_Ints: []
|
||||||
|
m_Floats:
|
||||||
|
- _BumpScale: 1
|
||||||
|
- _ColorMask: 15
|
||||||
|
- _Cutoff: 0.5
|
||||||
|
- _DetailNormalMapScale: 1
|
||||||
|
- _DstBlend: 0
|
||||||
|
- _GlossMapScale: 1
|
||||||
|
- _Glossiness: 0.5
|
||||||
|
- _GlossyReflections: 1
|
||||||
|
- _Metallic: 0
|
||||||
|
- _Mode: 0
|
||||||
|
- _OcclusionStrength: 1
|
||||||
|
- _Parallax: 0.02
|
||||||
|
- _SmoothnessTextureChannel: 0
|
||||||
|
- _SpecularHighlights: 1
|
||||||
|
- _SrcBlend: 1
|
||||||
|
- _Stencil: 0
|
||||||
|
- _StencilComp: 8
|
||||||
|
- _StencilOp: 0
|
||||||
|
- _StencilReadMask: 255
|
||||||
|
- _StencilWriteMask: 255
|
||||||
|
- _UVSec: 0
|
||||||
|
- _UseUIAlphaClip: 0
|
||||||
|
- _ZWrite: 1
|
||||||
|
m_Colors:
|
||||||
|
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||||
|
m_BuildTextureStacks: []
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 7a55e246f37df405bac88eac692e3a86
|
guid: 81f29a831022a4756b17daa366d67e10
|
||||||
folderAsset: yes
|
NativeFormatImporter:
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 2100000
|
||||||
userData:
|
userData:
|
||||||
assetBundleName:
|
assetBundleName:
|
||||||
assetBundleVariant:
|
assetBundleVariant:
|
||||||
@@ -8,8 +8,8 @@ Material:
|
|||||||
m_PrefabInstance: {fileID: 0}
|
m_PrefabInstance: {fileID: 0}
|
||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
m_Name: UIParticle_Demo_Animatable
|
m_Name: UIParticle_Demo_Animatable
|
||||||
m_Shader: {fileID: 4800000, guid: ecfa8f5732b504ef98fba10aa18d0326, type: 3}
|
m_Shader: {fileID: 4800000, guid: 9cc9ed37ff19d40e684526abbc1d44a6, type: 3}
|
||||||
m_ShaderKeywords:
|
m_Parent: {fileID: 0}
|
||||||
m_LightmapFlags: 4
|
m_LightmapFlags: 4
|
||||||
m_EnableInstancingVariants: 0
|
m_EnableInstancingVariants: 0
|
||||||
m_DoubleSidedGI: 0
|
m_DoubleSidedGI: 0
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ MonoBehaviour:
|
|||||||
m_AnimatableProperties:
|
m_AnimatableProperties:
|
||||||
- m_Name: _MainTex_ST
|
- m_Name: _MainTex_ST
|
||||||
m_Type: 1
|
m_Type: 1
|
||||||
|
- m_Name: _Color2
|
||||||
|
m_Type: 103
|
||||||
m_Particles:
|
m_Particles:
|
||||||
- {fileID: 198637387440798640}
|
- {fileID: 198637387440798640}
|
||||||
m_MeshSharing: 0
|
m_MeshSharing: 0
|
||||||
@@ -88,6 +90,7 @@ GameObject:
|
|||||||
- component: {fileID: 199176884810573912}
|
- component: {fileID: 199176884810573912}
|
||||||
- component: {fileID: 222047182059782320}
|
- component: {fileID: 222047182059782320}
|
||||||
- component: {fileID: 95475093951880840}
|
- component: {fileID: 95475093951880840}
|
||||||
|
- component: {fileID: 3543952012814938654}
|
||||||
m_Layer: 0
|
m_Layer: 0
|
||||||
m_Name: UvAnimParticle
|
m_Name: UvAnimParticle
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@@ -4864,3 +4867,21 @@ Animator:
|
|||||||
m_HasTransformHierarchy: 1
|
m_HasTransformHierarchy: 1
|
||||||
m_AllowConstantClipSamplingOptimization: 1
|
m_AllowConstantClipSamplingOptimization: 1
|
||||||
m_KeepAnimatorControllerStateOnDisable: 0
|
m_KeepAnimatorControllerStateOnDisable: 0
|
||||||
|
--- !u!114 &3543952012814938654
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1349193913114882}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: da4c2e90ecd3d4dd4a520099c08cf493, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_PropertyName: _Color2
|
||||||
|
m_Colors:
|
||||||
|
- {r: 1, g: 0, b: 0, a: 1}
|
||||||
|
- {r: 0, g: 1, b: 0, a: 1}
|
||||||
|
- {r: 0, g: 0, b: 1, a: 1}
|
||||||
|
- {r: 1, g: 0.92156863, b: 0.015686275, a: 1}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace Coffee.UIExtensions.Demo
|
|||||||
|
|
||||||
private void Start()
|
private void Start()
|
||||||
{
|
{
|
||||||
if (!m_Origin) return;
|
if (m_Origin == null) return;
|
||||||
m_Origin.SetActive(false);
|
m_Origin.SetActive(false);
|
||||||
|
|
||||||
var parent = m_Origin.transform.parent;
|
var parent = m_Origin.transform.parent;
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public class UIElementDragger : MonoBehaviour, IBeginDragHandler, IDragHandler,
|
|||||||
break;
|
break;
|
||||||
case Target.Custom:
|
case Target.Custom:
|
||||||
_rectTransform.localPosition += delta;
|
_rectTransform.localPosition += delta;
|
||||||
if (m_CustomTarget)
|
if (m_CustomTarget != null)
|
||||||
{
|
{
|
||||||
if (m_UseCanvasScale)
|
if (m_UseCanvasScale)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Coffee.UIParticleInternal;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.Serialization;
|
using UnityEngine.Serialization;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
@@ -51,11 +52,7 @@ namespace Coffee.UIExtensions.Demo
|
|||||||
|
|
||||||
public void EnableAnimations(bool flag)
|
public void EnableAnimations(bool flag)
|
||||||
{
|
{
|
||||||
#if UNITY_2023_1_OR_NEWER
|
foreach (var animator in Misc.FindObjectsOfType<Animator>())
|
||||||
foreach (var animator in FindObjectsByType<Animator>(FindObjectsInactive.Include, FindObjectsSortMode.None))
|
|
||||||
#else
|
|
||||||
foreach (var animator in FindObjectsOfType<Animator>())
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
animator.enabled = flag;
|
animator.enabled = flag;
|
||||||
}
|
}
|
||||||
@@ -83,11 +80,7 @@ namespace Coffee.UIExtensions.Demo
|
|||||||
|
|
||||||
public void UIParticle_Scale(float scale)
|
public void UIParticle_Scale(float scale)
|
||||||
{
|
{
|
||||||
#if UNITY_2023_1_OR_NEWER
|
foreach (var uip in Misc.FindObjectsOfType<UIParticle>())
|
||||||
foreach (var uip in FindObjectsByType<UIParticle>(FindObjectsInactive.Include, FindObjectsSortMode.None))
|
|
||||||
#else
|
|
||||||
foreach (var uip in FindObjectsOfType<UIParticle>())
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
uip.scale = scale;
|
uip.scale = scale;
|
||||||
}
|
}
|
||||||
@@ -95,11 +88,7 @@ namespace Coffee.UIExtensions.Demo
|
|||||||
|
|
||||||
public void ParticleSystem_WorldSpaseSimulation(bool flag)
|
public void ParticleSystem_WorldSpaseSimulation(bool flag)
|
||||||
{
|
{
|
||||||
#if UNITY_2023_1_OR_NEWER
|
foreach (var p in Misc.FindObjectsOfType<ParticleSystem>())
|
||||||
foreach (var p in FindObjectsByType<ParticleSystem>(FindObjectsInactive.Include, FindObjectsSortMode.None))
|
|
||||||
#else
|
|
||||||
foreach (var p in FindObjectsOfType<ParticleSystem>())
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
var main = p.main;
|
var main = p.main;
|
||||||
main.simulationSpace = flag
|
main.simulationSpace = flag
|
||||||
@@ -135,11 +124,7 @@ namespace Coffee.UIExtensions.Demo
|
|||||||
|
|
||||||
public void ParticleSystem_SetScale(float scale)
|
public void ParticleSystem_SetScale(float scale)
|
||||||
{
|
{
|
||||||
#if UNITY_2023_1_OR_NEWER
|
foreach (var ps in Misc.FindObjectsOfType<ParticleSystem>())
|
||||||
foreach (var ps in FindObjectsByType<ParticleSystem>(FindObjectsInactive.Include, FindObjectsSortMode.None))
|
|
||||||
#else
|
|
||||||
foreach (var ps in FindObjectsOfType<ParticleSystem>())
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
ps.transform.localScale = new Vector3(scale, scale, scale);
|
ps.transform.localScale = new Vector3(scale, scale, scale);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,12 +40,8 @@
|
|||||||
Lighting Off
|
Lighting Off
|
||||||
ZWrite Off
|
ZWrite Off
|
||||||
ZTest [unity_GUIZTestMode]
|
ZTest [unity_GUIZTestMode]
|
||||||
Fog
|
Fog { Mode Off }
|
||||||
{
|
|
||||||
Mode Off
|
|
||||||
}
|
|
||||||
Blend One One
|
Blend One One
|
||||||
|
|
||||||
ColorMask [_ColorMask]
|
ColorMask [_ColorMask]
|
||||||
|
|
||||||
Pass
|
Pass
|
||||||
@@ -76,6 +72,7 @@
|
|||||||
fixed4 color : COLOR;
|
fixed4 color : COLOR;
|
||||||
float2 texcoord : TEXCOORD0;
|
float2 texcoord : TEXCOORD0;
|
||||||
float4 worldPosition : TEXCOORD1;
|
float4 worldPosition : TEXCOORD1;
|
||||||
|
float4 mask : TEXCOORD2;
|
||||||
UNITY_VERTEX_OUTPUT_STEREO
|
UNITY_VERTEX_OUTPUT_STEREO
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -84,16 +81,43 @@
|
|||||||
float4 _MainTex_ST;
|
float4 _MainTex_ST;
|
||||||
fixed4 _TextureSampleAdd;
|
fixed4 _TextureSampleAdd;
|
||||||
float4 _ClipRect;
|
float4 _ClipRect;
|
||||||
|
float _UIMaskSoftnessX;
|
||||||
|
float _UIMaskSoftnessY;
|
||||||
|
int _UIVertexColorAlwaysGammaSpace;
|
||||||
|
|
||||||
|
half3 _UIGammaToLinear(half3 value)
|
||||||
|
{
|
||||||
|
half3 low = 0.0849710 * value - 0.000163029;
|
||||||
|
half3 high = value * (value * (value * 0.265885 + 0.736584) - 0.00980184) + 0.00319697;
|
||||||
|
|
||||||
|
// We should be 0.5 away from any actual gamma value stored in an 8 bit channel
|
||||||
|
const half3 split = (half3)0.0725490; // Equals 18.5 / 255
|
||||||
|
return (value < split) ? low : high;
|
||||||
|
}
|
||||||
|
|
||||||
v2f vert(appdata_t v)
|
v2f vert(appdata_t v)
|
||||||
{
|
{
|
||||||
v2f OUT;
|
v2f OUT;
|
||||||
UNITY_SETUP_INSTANCE_ID(v);
|
UNITY_SETUP_INSTANCE_ID(v);
|
||||||
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
|
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
|
||||||
|
float4 vPosition = UnityObjectToClipPos(v.vertex);
|
||||||
OUT.worldPosition = v.vertex;
|
OUT.worldPosition = v.vertex;
|
||||||
OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
|
OUT.vertex = vPosition;
|
||||||
|
|
||||||
OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
|
float2 pixelSize = vPosition.w;
|
||||||
|
pixelSize /= float2(1, 1) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy));
|
||||||
|
|
||||||
|
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
|
||||||
|
float2 maskUV = (v.vertex.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy);
|
||||||
|
OUT.texcoord = TRANSFORM_TEX(v.texcoord.xy, _MainTex);
|
||||||
|
OUT.mask = float4(v.vertex.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_UIMaskSoftnessX, _UIMaskSoftnessY) + abs(pixelSize.xy)));
|
||||||
|
|
||||||
|
if (_UIVertexColorAlwaysGammaSpace)
|
||||||
|
{
|
||||||
|
#ifndef UNITY_COLORSPACE_GAMMA
|
||||||
|
v.color.rgb = _UIGammaToLinear(v.color.rgb);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
OUT.color = v.color * _Color;
|
OUT.color = v.color * _Color;
|
||||||
return OUT;
|
return OUT;
|
||||||
@@ -101,17 +125,26 @@
|
|||||||
|
|
||||||
fixed4 frag(v2f IN) : SV_Target
|
fixed4 frag(v2f IN) : SV_Target
|
||||||
{
|
{
|
||||||
half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
|
//Round up the alpha color coming from the interpolator (to 1.0/256.0 steps)
|
||||||
|
//The incoming alpha could have numerical instability, which makes it very sensible to
|
||||||
|
//HDR color transparency blend, when it blends with the world's texture.
|
||||||
|
const half alphaPrecision = half(0xff);
|
||||||
|
const half invAlphaPrecision = half(1.0 / alphaPrecision);
|
||||||
|
IN.color.a = round(IN.color.a * alphaPrecision) * invAlphaPrecision;
|
||||||
|
|
||||||
|
half4 color = IN.color * (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd);
|
||||||
|
|
||||||
#ifdef UNITY_UI_CLIP_RECT
|
#ifdef UNITY_UI_CLIP_RECT
|
||||||
color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
|
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw);
|
||||||
|
color.a *= m.x * m.y;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef UNITY_UI_ALPHACLIP
|
#ifdef UNITY_UI_ALPHACLIP
|
||||||
clip (color.a - 0.001);
|
clip(color.a - 0.001);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
color.rgb *= color.a;
|
color.rgb *= color.a;
|
||||||
|
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
ENDCG
|
ENDCG
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "com.coffee.ui-particle",
|
"name": "com.coffee.ui-particle",
|
||||||
"displayName": "UI Particle",
|
"displayName": "UI Particle",
|
||||||
"description": "This package provides a component to render particle effects for uGUI.\nThe particle rendering is maskable and sortable, without the need for an extra Camera, RenderTexture, or Canvas.",
|
"description": "This package provides a component to render particle effects for uGUI.\nThe particle rendering is maskable and sortable, without the need for an extra Camera, RenderTexture, or Canvas.",
|
||||||
"version": "4.10.2",
|
"version": "4.13.0",
|
||||||
"unity": "2018.2",
|
"unity": "2018.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
Reference in New Issue
Block a user