Compare commits

..

22 Commits

Author SHA1 Message Date
semantic-release-bot
79635e81d7 chore(release): 5.0.0-preview.2 [skip ci]
# [5.0.0-preview.2](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v5.0.0-preview.1...v5.0.0-preview.2) (2024-06-20)

### Bug Fixes

* 'Resource ID out of range in GetResource' error in overlay rendering mode ([ff78b6f](ff78b6fe32)), closes [#308](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/308)
* `UIParticle.transform.localScale` does not scale particles ([491ee7b](491ee7b0c3)), closes [#313](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/313)
* despite not using the size module, particles become smaller based on their z position ([c96ddf2](c96ddf293e)), closes [#316](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/316)
* the ParticleSystem's localPosition drifts at certain scales due to floating-point precision issues ([a9c2b19](a9c2b19edf)), closes [#299](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/299) [#312](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/312)
* UIParticle is scaled by canvas size even when `AutoScalingMode.None` and `ScalingMode.Local` ([63b24d8](63b24d8a8b)), closes [#313](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/313)
* UIParticle is scaled incorrectly with nested canvases ([c95d8c6](c95d8c6b17)), closes [#313](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/313)

### Features

* remove overlay window (editor) ([fc3fbdd](fc3fbdd230))
* reset previous position on start play for world space simulation ([e741584](e741584507)), closes [#303](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/303)
* restore `Transform.localScale` when setting `autoScalingMode` to something other than `Transform` ([dfb94f4](dfb94f4bad))
2024-06-20 15:27:14 +00:00
mob-sakai
f388eb45ec fix: despite not using the size module, particles become smaller based on their z position
close #316
2024-06-20 12:52:21 +09:00
mob-sakai
f08809772a feat: restore Transform.localScale when setting autoScalingMode to something other than Transform
# Conflicts:
#	Packages/src/Editor/UIParticleEditor.cs
#	Packages/src/Runtime/UIParticle.cs
2024-06-20 12:52:21 +09:00
mob-sakai
f96604ffc9 feat: reset previous position on start play for world space simulation
close #303

# Conflicts:
#	Packages/src/Runtime/UIParticleRenderer.cs
2024-06-20 12:52:21 +09:00
mob-sakai
beac3b8048 docs: update license 2024-06-20 12:52:21 +09:00
mob-sakai
a00bcd8a35 fix demo 2024-06-20 12:52:21 +09:00
mob-sakai
1f72048ef5 fix: UIParticle.transform.localScale does not scale particles
close #313
2024-06-19 13:49:00 +09:00
mob-sakai
65f7555482 fix: UIParticle is scaled by canvas size even when AutoScalingMode.None and ScalingMode.Local
close #313
2024-06-19 13:48:54 +09:00
mob-sakai
433f828ad9 fix: UIParticle is scaled incorrectly with nested canvases
close #313
2024-06-19 13:48:46 +09:00
mob-sakai
1b8f0aac4a refactor: refactor 2024-06-19 13:47:51 +09:00
SAMYTHEBIGJUICY
70271ae5e9 fix: 'Resource ID out of range in GetResource' error in overlay rendering mode
#308
2024-06-14 11:35:15 +09:00
mob-sakai
c3540a056c update coffee.internal 2024-06-14 11:27:47 +09:00
mob-sakai
0cf4ae88c5 fix: the ParticleSystem's localPosition drifts at certain scales due to floating-point precision issues
close #299, close #312
2024-05-24 17:49:07 +09:00
mob-sakai
e579f524ba feat: remove overlay window (editor) 2024-05-24 17:44:14 +09:00
semantic-release-bot
23dd7b5987 chore(release): 5.0.0-preview.1 [skip ci]
# [5.0.0-preview.1](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.6...v5.0.0-preview.1) (2024-05-23)

### Features

* add project settings for UIParticle ([b6e6185](b6e6185c52))
* change the default value of `UIParticle.scale` from `10` to `1` ([1b3c0f9](1b3c0f92dc)), closes [#310](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/310)
* UIParticle no longer inherits from MaskableGraphic ([b6d921b](b6d921b3e4))

### BREAKING CHANGES

* Some members inherited from MaskableGraphic will no longer be available.
2024-05-23 11:19:50 +00:00
mob-sakai
f476898b46 fix preview release workflow 2024-05-23 20:17:25 +09:00
mob-sakai
4e91b6f69b feat: change the default value of UIParticle.scale from 10 to 1
close #310
2024-05-23 20:10:33 +09:00
mob-sakai
1bf669e09f docs: update readme 2024-05-23 20:10:33 +09:00
mob-sakai
54be6bf588 refactor: refactor 2024-05-23 20:10:33 +09:00
mob-sakai
72f5f9441b feat: add project settings for UIParticle 2024-05-23 20:10:33 +09:00
mob-sakai
acebfb27f6 feat: UIParticle no longer inherits from MaskableGraphic
BREAKING CHANGE: Some members inherited from MaskableGraphic will no longer be available.
2024-05-23 20:10:33 +09:00
mob-sakai
a3e725fd5a refactor: using Coffee.Internal 2024-05-23 20:10:33 +09:00
31 changed files with 563 additions and 868 deletions

View File

@@ -1,115 +1,35 @@
## [4.10.3](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.2...v4.10.3) (2024-11-20) # [5.0.0-preview.2](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v5.0.0-preview.1...v5.0.0-preview.2) (2024-06-20)
### Bug Fixes ### 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) * 'Resource ID out of range in GetResource' error in overlay rendering mode ([ff78b6f](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/ff78b6fe32ceed8ddad50e63dcb7a202eab95266)), closes [#308](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/308)
* `UIParticle.transform.localScale` does not scale particles ([491ee7b](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/491ee7b0c3e528e1e577ae5ff2588d7c3bd8ecdb)), closes [#313](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/313)
## [4.10.2](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.1...v4.10.2) (2024-11-01) * despite not using the size module, particles become smaller based on their z position ([c96ddf2](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/c96ddf293e855f7ebccaaaf3b112092955545e61)), closes [#316](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/316)
* the ParticleSystem's localPosition drifts at certain scales due to floating-point precision issues ([a9c2b19](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/a9c2b19edf00e1c86c928ef23405906952ede852)), closes [#299](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/299) [#312](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/312)
* UIParticle is scaled by canvas size even when `AutoScalingMode.None` and `ScalingMode.Local` ([63b24d8](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/63b24d8a8b478b3165733ece3eec524e88b28855)), closes [#313](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/313)
### Bug Fixes * UIParticle is scaled incorrectly with nested canvases ([c95d8c6](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/c95d8c6b1774396ff252d13121ad32a3cab0fe5c)), closes [#313](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/313)
* trail incorrect offset ([afe00a1](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/afe00a1dde80eb1c0a7bb668b75f4c3733d3fa43)), closes [#335](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/335)
## [4.10.1](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.0...v4.10.1) (2024-09-29)
### Bug Fixes
* mainTex will be ignored ([2ee69d0](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/2ee69d04245fabce185f67dc9bd68c870e556564))
# [4.10.0](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.9.1...v4.10.0) (2024-09-29)
### Bug Fixes
* component icon is not set ([5ff6ec8](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/5ff6ec815a174de5d3f16d424f1204c60912a8d8))
### Features ### Features
* add project settings ([1ce4e31](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/1ce4e31a9681bf1a201d2723c8d97e07ecc16592)) * remove overlay window (editor) ([fc3fbdd](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/fc3fbdd230595ad3471875ec6fec384a3dad0d17))
* reset previous position on start play for world space simulation ([e741584](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/e7415845074143abae23e3ae7eedc767a01d020d)), closes [#303](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/303)
* restore `Transform.localScale` when setting `autoScalingMode` to something other than `Transform` ([dfb94f4](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/dfb94f4badfb3035a4374579c53293460b4fd946))
## [4.9.1](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.9.0...v4.9.1) (2024-08-07) # [5.0.0-preview.1](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.6...v5.0.0-preview.1) (2024-05-23)
### Bug Fixes
* ParticleSystem trails gain offset on parent canvas change ([2a1cd50](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/2a1cd502b452b5b56edf8bcfe91adf99d1bb5147)), closes [#323](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/323)
# [4.9.0](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.8.1...v4.9.0) (2024-07-18)
### Features ### Features
* ParticleAttractor supports multiple ParticleSystems ([3834780](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/3834780fdb43443fe6e1ef89df54d26a24d62a91)) * add project settings for UIParticle ([b6e6185](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/b6e6185c521fef0f118f61cfdfdac07b87555c01))
* change the default value of `UIParticle.scale` from `10` to `1` ([1b3c0f9](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/1b3c0f92dcc6a1974d1ea074821e5264200e9711)), closes [#310](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/310)
## [4.8.1](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.8.0...v4.8.1) (2024-06-27) * UIParticle no longer inherits from MaskableGraphic ([b6d921b](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/b6d921b3e47f749b5028d22b0e89b6eb3a1af7de))
### Bug Fixes ### BREAKING CHANGES
* remove debug code ([669deb4](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/669deb41d4ac589d9db93b29bc8e95383e7f28a5)) * Some members inherited from MaskableGraphic will no longer be available.
# [4.8.0](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.7.2...v4.8.0) (2024-06-27)
### Bug Fixes
* generated baking-camera object remains in the prefab or scene (again) ([de35cba](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/de35cba34c6312c1405ed522e9927c620c78e72d))
* SetParticleSystemInstance/Prefab APIs destroy generated objects ([ae3f3a8](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/ae3f3a8e62cc733420354d237ab765ac777127c8))
### Features
* add 'custom view' option. ([a703c29](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/a703c2921ca08c2280d0c8fde01e4c0b33b5c69e))
* remove overlay window (editor) ([8358170](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/835817049f4fcf00dd2bf98dbada14f041ad3544))
* restore `Transform.localScale` when setting `autoScalingMode` to something other than `Transform` (again) ([88a970d](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/88a970d93a2b69cf011d86bd1807569e90538e0e))
* the rendering order list in inspector is now more compact ([be90172](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/be901724e064befacf617f4940b0331e1d31e1ca))
## [4.7.2](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.7.1...v4.7.2) (2024-06-21)
### Bug Fixes
* generated baking-camera object remains in the prefab or scene ([0bb8438](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/0bb843830197d8c1252232928becc211c0ada08d))
## [4.7.1](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.7.0...v4.7.1) (2024-06-20)
### Bug Fixes
* despite not using the size module, particles become smaller based on their z position ([a8ed6e6](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/a8ed6e68584e1d9e45ed852eefcc03979ea7e0e1)), closes [#316](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/316)
# [4.7.0](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.8...v4.7.0) (2024-06-19)
### Bug Fixes
* `UIParticle.transform.localScale` does not scale particles ([1d40e24](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/1d40e24c742741e97f03c55468ccb1e505341133)), closes [#313](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/313)
* UIParticle is scaled by canvas size even when `AutoScalingMode.None` and `ScalingMode.Local` ([54a4b1c](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/54a4b1cdfd06400c7be89c1ee704bb42a659c7c2)), closes [#313](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/313)
* UIParticle is scaled incorrectly with nested canvases ([f26920f](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/f26920f9825547222a4afbb31cc5dc5a002c3e9b)), closes [#313](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/313)
### Features
* reset previous position on start play for world space simulation ([3880484](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/3880484ce5190c42fc79c81d0b69e3fbeda09dd0)), closes [#303](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/303)
* restore `Transform.localScale` when setting `autoScalingMode` to something other than `Transform` ([5505247](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/5505247a94a929ff89635fde512a9b95691e0043))
## [4.6.8](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.7...v4.6.8) (2024-06-14)
### Bug Fixes
* 'Resource ID out of range in GetResource' error in overlay rendering mode ([05286ce](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/05286cedfd17b1a0cb90a5e918513644f47cd831)), closes [#308](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/308)
## [4.6.7](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.6...v4.6.7) (2024-05-24)
### Bug Fixes
* the ParticleSystem's localPosition drifts at certain scales due to floating-point precision issues ([e924eb4](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/e924eb45968a112347471cabaeabc274e4c37ce4)), closes [#299](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/299) [#312](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/312)
## [4.6.6](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.5...v4.6.6) (2024-05-23) ## [4.6.6](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.5...v4.6.6) (2024-05-23)

View File

@@ -31,35 +31,35 @@ namespace Coffee.UIExtensions
} }
else else
{ {
result.Aggregate(s_Sb, (a, b) => result.Aggregate(s_Sb, (a, b) => s_Sb.AppendFormat("{0}, ", b));
{
s_Sb.Append(b);
return s_Sb.Append(", ");
});
s_Sb.Length -= 2; s_Sb.Length -= 2;
} }
return s_Sb.ToString(); return s_Sb.ToString();
} }
public static void Draw(SerializedProperty sp, List<Material> mats) public static void Draw(SerializedProperty sp, Material[] mats)
{ {
var pos = EditorGUILayout.GetControlRect(true); bool isClicked;
var label = new GUIContent(sp.displayName, sp.tooltip); using (new EditorGUILayout.HorizontalScope(GUILayout.ExpandWidth(false)))
var rect = EditorGUI.PrefixLabel(pos, label); {
var text = sp.hasMultipleDifferentValues var pos = EditorGUILayout.GetControlRect(true);
? "-" var label = new GUIContent(sp.displayName, sp.tooltip);
: CollectActiveNames(sp, s_ActiveNames); var rect = EditorGUI.PrefixLabel(pos, label);
var text = sp.hasMultipleDifferentValues
? "-"
: CollectActiveNames(sp, s_ActiveNames);
isClicked = GUI.Button(rect, text, EditorStyles.popup);
}
if (!GUI.Button(rect, text, EditorStyles.popup)) return; if (!isClicked) return;
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, () =>
{ {
var current = (SerializedProperty)x; sp.ClearArray();
current.ClearArray(); sp.serializedObject.ApplyModifiedProperties();
current.serializedObject.ApplyModifiedProperties(); });
}, sp);
if (!sp.hasMultipleDifferentValues) if (!sp.hasMultipleDifferentValues)
{ {
@@ -73,7 +73,7 @@ namespace Coffee.UIExtensions
} }
s_Names.Clear(); s_Names.Clear();
for (var j = 0; j < mats.Count; j++) for (var j = 0; j < mats.Length; j++)
{ {
var mat = mats[j]; var mat = mats[j];
if (!mat || !mat.shader) continue; if (!mat || !mat.shader) continue;
@@ -82,7 +82,8 @@ namespace Coffee.UIExtensions
{ {
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 = (AnimatableProperty.ShaderPropertyType)ShaderUtil.GetPropertyType(mat.shader, i);
if (!s_Names.Add(name)) continue; if (s_Names.Contains(name)) continue;
s_Names.Add(name);
AddMenu(gm, sp, new ShaderProperty(name, type), true); AddMenu(gm, sp, new ShaderProperty(name, type), true);

8
Editor/Internal.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3e440931f761e4e888510a4e6045287a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -2,19 +2,19 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using UnityEditor; using UnityEditor;
using UnityEditor.UI;
using UnityEditorInternal; using UnityEditorInternal;
using UnityEngine; using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.UI; using UnityEngine.UI;
using Coffee.UIParticleInternal;
#if UNITY_2021_2_OR_NEWER #if UNITY_2021_2_OR_NEWER
using UnityEditor.Overlays; using UnityEditor.Overlays;
#else #else
using System; using System;
using System.Reflection; using System.Reflection;
using Coffee.UIParticleInternal;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
#endif #endif
#if UNITY_2021_2_OR_NEWER #if UNITY_2021_2_OR_NEWER
using UnityEditor.SceneManagement; using UnityEditor.SceneManagement;
@@ -26,26 +26,19 @@ namespace Coffee.UIExtensions
{ {
[CustomEditor(typeof(UIParticle))] [CustomEditor(typeof(UIParticle))]
[CanEditMultipleObjects] [CanEditMultipleObjects]
internal class UIParticleEditor : GraphicEditor internal class UIParticleEditor : Editor
{ {
//################################ //################################
// Constant or Static Members. // Constant or Static Members.
//################################ //################################
private static readonly GUIContent[] s_ContentMaterials = new[]
{
new GUIContent("Material"),
new GUIContent("Trail Material")
};
private static readonly GUIContent s_ContentRenderingOrder = new GUIContent("Rendering Order"); private static readonly GUIContent s_ContentRenderingOrder = new GUIContent("Rendering Order");
private static readonly GUIContent s_ContentRefresh = new GUIContent("Refresh"); private static readonly GUIContent s_ContentRefresh = new GUIContent("Refresh");
private static readonly GUIContent s_ContentFix = new GUIContent("Fix"); private static readonly GUIContent s_ContentFix = new GUIContent("Fix");
private static readonly GUIContent s_ContentMaterial = new GUIContent("Material");
private static readonly GUIContent s_ContentTrailMaterial = new GUIContent("Trail Material");
private static readonly GUIContent s_Content3D = new GUIContent("3D"); private static readonly GUIContent s_Content3D = new GUIContent("3D");
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 Regex s_RegexBuiltInGuid = new Regex(@"^0{16}.0{15}$", RegexOptions.Compiled);
private static readonly List<Material> s_TempMaterials = new List<Material>();
private static bool s_XYZMode; private static bool s_XYZMode;
private SerializedProperty _maskable; private SerializedProperty _maskable;
@@ -56,8 +49,6 @@ namespace Coffee.UIExtensions
private SerializedProperty _groupMaxId; private SerializedProperty _groupMaxId;
private SerializedProperty _positionMode; private SerializedProperty _positionMode;
private SerializedProperty _autoScalingMode; private SerializedProperty _autoScalingMode;
private SerializedProperty _useCustomView;
private SerializedProperty _customViewSize;
private ReorderableList _ro; private ReorderableList _ro;
private bool _showMax; private bool _showMax;
@@ -81,10 +72,8 @@ namespace Coffee.UIExtensions
/// <summary> /// <summary>
/// This function is called when the object becomes enabled and active. /// This function is called when the object becomes enabled and active.
/// </summary> /// </summary>
protected override void OnEnable() private void OnEnable()
{ {
base.OnEnable();
_maskable = serializedObject.FindProperty("m_Maskable"); _maskable = serializedObject.FindProperty("m_Maskable");
_scale3D = serializedObject.FindProperty("m_Scale3D"); _scale3D = serializedObject.FindProperty("m_Scale3D");
_animatableProperties = serializedObject.FindProperty("m_AnimatableProperties"); _animatableProperties = serializedObject.FindProperty("m_AnimatableProperties");
@@ -93,43 +82,31 @@ namespace Coffee.UIExtensions
_groupMaxId = serializedObject.FindProperty("m_GroupMaxId"); _groupMaxId = serializedObject.FindProperty("m_GroupMaxId");
_positionMode = serializedObject.FindProperty("m_PositionMode"); _positionMode = serializedObject.FindProperty("m_PositionMode");
_autoScalingMode = serializedObject.FindProperty("m_AutoScalingMode"); _autoScalingMode = serializedObject.FindProperty("m_AutoScalingMode");
_useCustomView = serializedObject.FindProperty("m_UseCustomView");
_customViewSize = serializedObject.FindProperty("m_CustomViewSize");
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)
{ {
elementHeightCallback = index => elementHeight = EditorGUIUtility.singleLineHeight * 3 + 4,
{ elementHeightCallback = _ => 3 * (EditorGUIUtility.singleLineHeight + 2),
var ps = sp.GetArrayElementAtIndex(index).objectReferenceValue as ParticleSystem;
var materialCount = 0;
if (ps && ps.TryGetComponent<ParticleSystemRenderer>(out var psr))
{
materialCount = psr.sharedMaterials.Length;
}
return (materialCount + 1) * (EditorGUIUtility.singleLineHeight + 2);
},
drawElementCallback = (rect, index, _, __) => drawElementCallback = (rect, index, _, __) =>
{ {
rect.y += 2; EditorGUI.BeginDisabledGroup(sp.hasMultipleDifferentValues);
rect.y += 1;
rect.height = EditorGUIUtility.singleLineHeight; rect.height = EditorGUIUtility.singleLineHeight;
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;
if (!ps || !ps.TryGetComponent<ParticleSystemRenderer>(out var psr)) return;
rect.x += 15; rect.x += 15;
rect.width -= 15; rect.width -= 15;
var materials = new SerializedObject(psr).FindProperty("m_Materials"); var ps = p.objectReferenceValue as ParticleSystem;
var count = Mathf.Min(materials.arraySize, 2); var materials = ps
for (var i = 0; i < count; i++) ? new SerializedObject(ps.GetComponent<ParticleSystemRenderer>()).FindProperty("m_Materials")
{ : null;
rect.y += rect.height + 2; rect.y += rect.height + 1;
EditorGUI.PropertyField(rect, materials.GetArrayElementAtIndex(i), s_ContentMaterials[i]); MaterialField(rect, s_ContentMaterial, materials, 0);
} rect.y += rect.height + 1;
MaterialField(rect, s_ContentTrailMaterial, materials, 1);
if (materials.serializedObject.hasModifiedProperties) EditorGUI.EndDisabledGroup();
if (materials != null && materials.serializedObject.hasModifiedProperties)
{ {
materials.serializedObject.ApplyModifiedProperties(); materials.serializedObject.ApplyModifiedProperties();
} }
@@ -165,6 +142,20 @@ namespace Coffee.UIExtensions
} }
} }
private static void MaterialField(Rect rect, GUIContent label, SerializedProperty sp, int index)
{
if (sp == null || sp.arraySize <= index)
{
EditorGUI.BeginDisabledGroup(true);
EditorGUI.ObjectField(rect, label, null, typeof(Material), true);
EditorGUI.EndDisabledGroup();
}
else
{
EditorGUI.PropertyField(rect, sp.GetArrayElementAtIndex(index), label);
}
}
/// <summary> /// <summary>
/// Implement this function to make a custom inspector. /// Implement this function to make a custom inspector.
/// </summary> /// </summary>
@@ -173,7 +164,6 @@ namespace Coffee.UIExtensions
var current = target as UIParticle; var current = target as UIParticle;
if (!current) return; if (!current) return;
Profiler.BeginSample("(UIP:E) OnInspectorGUI");
serializedObject.Update(); serializedObject.Update();
// Maskable // Maskable
@@ -185,8 +175,13 @@ namespace Coffee.UIExtensions
EditorGUI.EndDisabledGroup(); EditorGUI.EndDisabledGroup();
// AnimatableProperties // AnimatableProperties
current.GetMaterials(s_TempMaterials); var mats = current.particles
AnimatablePropertyEditor.Draw(_animatableProperties, s_TempMaterials); .Where(x => x)
.Select(x => x.GetComponent<ParticleSystemRenderer>().sharedMaterial)
.Where(x => x)
.ToArray();
AnimatablePropertyEditor.Draw(_animatableProperties, mats);
// Mesh sharing // Mesh sharing
EditorGUI.BeginChangeCheck(); EditorGUI.BeginChangeCheck();
@@ -194,12 +189,9 @@ namespace Coffee.UIExtensions
if (EditorGUI.EndChangeCheck()) if (EditorGUI.EndChangeCheck())
{ {
serializedObject.ApplyModifiedProperties(); serializedObject.ApplyModifiedProperties();
foreach (var t in targets) foreach (var uip in targets.OfType<UIParticle>())
{ {
if (t is UIParticle uip) uip.ResetGroupId();
{
uip.ResetGroupId();
}
} }
} }
@@ -207,29 +199,16 @@ namespace Coffee.UIExtensions
EditorGUILayout.PropertyField(_positionMode); EditorGUILayout.PropertyField(_positionMode);
// Auto Scaling // Auto Scaling
EditorGUILayout.PropertyField(_autoScalingMode); DrawAutoScaling(_autoScalingMode, targets.OfType<UIParticle>());
// Custom View Size
EditorGUILayout.PropertyField(_useCustomView);
EditorGUI.BeginChangeCheck();
EditorGUI.BeginDisabledGroup(!_useCustomView.boolValue);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_customViewSize);
EditorGUI.indentLevel--;
EditorGUI.EndDisabledGroup();
if (EditorGUI.EndChangeCheck())
{
_customViewSize.floatValue = Mathf.Max(0.1f, _customViewSize.floatValue);
}
// Target ParticleSystems. // Target ParticleSystems.
EditorGUI.BeginChangeCheck(); EditorGUI.BeginChangeCheck();
EditorGUI.BeginDisabledGroup(targets.OfType<UIParticle>().Any(x => !x.canvas));
_ro.DoLayoutList(); _ro.DoLayoutList();
EditorGUI.EndDisabledGroup();
serializedObject.ApplyModifiedProperties(); serializedObject.ApplyModifiedProperties();
if (EditorGUI.EndChangeCheck()) if (EditorGUI.EndChangeCheck())
{ {
EditorApplication.QueuePlayerLoopUpdate();
foreach (var uip in targets.OfType<UIParticle>()) foreach (var uip in targets.OfType<UIParticle>())
{ {
uip.RefreshParticles(uip.particles); uip.RefreshParticles(uip.particles);
@@ -237,8 +216,7 @@ namespace Coffee.UIExtensions
} }
// Non-UI built-in shader is not supported. // Non-UI built-in shader is not supported.
Profiler.BeginSample("(UIP:E) Non-UI built-in shader is not supported."); foreach (var mat in current.materials)
foreach (var mat in s_TempMaterials)
{ {
if (!mat || !mat.shader) continue; if (!mat || !mat.shader) continue;
var shader = mat.shader; var shader = mat.shader;
@@ -251,18 +229,15 @@ namespace Coffee.UIExtensions
} }
} }
Profiler.EndSample();
// Does the shader support UI masks? // Does the shader support UI masks?
Profiler.BeginSample("(UIP:E) Does the shader support UI masks?");
if (current.maskable && current.GetComponentInParent<Mask>(false)) if (current.maskable && current.GetComponentInParent<Mask>(false))
{ {
foreach (var mat in s_TempMaterials) foreach (var mat in current.materials)
{ {
if (!mat || !mat.shader) continue; if (!mat || !mat.shader) continue;
var shader = mat.shader; var shader = mat.shader;
if (!s_Shaders.Add(shader)) continue; if (s_Shaders.Contains(shader)) continue;
s_Shaders.Add(shader);
foreach (var propName in s_MaskablePropertyNames) foreach (var propName in s_MaskablePropertyNames)
{ {
if (mat.HasProperty(propName)) continue; if (mat.HasProperty(propName)) continue;
@@ -276,9 +251,7 @@ namespace Coffee.UIExtensions
} }
} }
s_TempMaterials.Clear();
s_Shaders.Clear(); s_Shaders.Clear();
Profiler.EndSample();
// UIParticle for trail should be removed. // UIParticle for trail should be removed.
var label = "This UIParticle component should be removed. The UIParticle for trails is no longer needed."; var label = "This UIParticle component should be removed. The UIParticle for trails is no longer needed.";
@@ -317,14 +290,12 @@ namespace Coffee.UIExtensions
} }
} }
#endif #endif
Profiler.EndSample();
} }
private static bool IsBuiltInObject(Object obj) private bool IsBuiltInObject(Object obj)
{ {
return AssetDatabase.IsMainAsset(obj) return AssetDatabase.TryGetGUIDAndLocalFileIdentifier(obj, out var guid, out long _)
&& AssetDatabase.TryGetGUIDAndLocalFileIdentifier(obj, out var guid, out long _) && Regex.IsMatch(guid, "^0{16}.0{15}$", RegexOptions.Compiled);
&& s_RegexBuiltInGuid.IsMatch(guid);
} }
#if UNITY_2018 || UNITY_2019 #if UNITY_2018 || UNITY_2019
@@ -418,7 +389,7 @@ namespace Coffee.UIExtensions
{ {
EditorGUI.BeginDisabledGroup(true); EditorGUI.BeginDisabledGroup(true);
var obj = UIParticleUpdater.GetPrimary(spGroupId.intValue); var obj = UIParticleUpdater.GetPrimary(spGroupId.intValue);
EditorGUILayout.ObjectField(s_ContentPrimary, obj, typeof(UIParticle), false); EditorGUILayout.ObjectField("Primary", obj, typeof(UIParticle), false);
EditorGUI.EndDisabledGroup(); EditorGUI.EndDisabledGroup();
} }
@@ -428,7 +399,7 @@ namespace Coffee.UIExtensions
return showMax; return showMax;
} }
private static void DrawAutoScaling(SerializedProperty prop) private static void DrawAutoScaling(SerializedProperty prop, IEnumerable<UIParticle> uiParticles)
{ {
EditorGUILayout.PropertyField(prop); EditorGUILayout.PropertyField(prop);
} }
@@ -437,9 +408,7 @@ namespace Coffee.UIExtensions
{ {
if (!p || (ignoreCurrent && target == p)) return; if (!p || (ignoreCurrent && target == p)) return;
var cr = p.canvasRenderer;
DestroyImmediate(p); DestroyImmediate(p);
DestroyImmediate(cr);
#if UNITY_2018_3_OR_NEWER #if UNITY_2018_3_OR_NEWER
var stage = PrefabStageUtility.GetCurrentPrefabStage(); var stage = PrefabStageUtility.GetCurrentPrefabStage();

236
README.md
View File

@@ -1,85 +1,61 @@
# <img alt="UIParticleIcon" src="https://github.com/mob-sakai/ParticleEffectForUGUI/assets/12690315/d76e105e-a840-4f61-a1f6-8cf311c0812d" width="26"/> Particle Effect For UGUI (UI Particle) <!-- omit in toc --> # <img alt="UIParticleIcon" src="https://github.com/mob-sakai/ParticleEffectForUGUI/assets/12690315/d76e105e-a840-4f61-a1f6-8cf311c0812d" width="26"/> Particle Effect For UGUI (UI Particle)
This package provides a component to render particle effects for uGUI in Unity 2018.2 or later.
The particle rendering is maskable and sortable, without the need for an extra Camera, RenderTexture, or Canvas.
[![](https://img.shields.io/npm/v/com.coffee.ui-particle?label=openupm&registry_uri=https://package.openupm.com)](https://openupm.com/packages/com.coffee.ui-particle/) [![](https://img.shields.io/npm/v/com.coffee.ui-particle?label=openupm&registry_uri=https://package.openupm.com)](https://openupm.com/packages/com.coffee.ui-particle/)
[![](https://img.shields.io/github/v/release/mob-sakai/ParticleEffectForUGUI)](https://github.com/mob-sakai/ParticleEffectForUGUI/releases) [![](https://img.shields.io/github/v/release/mob-sakai/ParticleEffectForUGUI?include_prereleases)](https://github.com/mob-sakai/ParticleEffectForUGUI/releases)
[![](https://img.shields.io/github/license/mob-sakai/ParticleEffectForUGUI.svg)](https://github.com/mob-sakai/ParticleEffectForUGUI/blob/main/LICENSE.md) [![](https://img.shields.io/github/license/mob-sakai/ParticleEffectForUGUI.svg)](https://github.com/mob-sakai/ParticleEffectForUGUI/blob/main/LICENSE.txt)
![](https://img.shields.io/badge/Unity-2018.2+-57b9d3.svg?style=flat&logo=unity) ![](https://img.shields.io/badge/Unity-2018.2+-57b9d3.svg?style=flat&logo=unity)
![](https://img.shields.io/badge/uGUI_2.0_Ready-57b9d3.svg?style=flat)
![](https://img.shields.io/badge/UPR%2FHDPR_Ready-57b9d3.svg?style=flat)
![](https://github.com/mob-sakai/ParticleEffectForUGUI/actions/workflows/test.yml/badge.svg?branch=develop) ![](https://github.com/mob-sakai/ParticleEffectForUGUI/actions/workflows/test.yml/badge.svg?branch=develop)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-orange.svg)](http://makeapullrequest.com) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-orange.svg)](http://makeapullrequest.com)
[![](https://img.shields.io/github/watchers/mob-sakai/ParticleEffectForUGUI.svg?style=social&label=Watch)](https://github.com/mob-sakai/ParticleEffectForUGUI/subscription)
[![](https://img.shields.io/twitter/follow/mob_sakai.svg?label=Follow&style=social)](https://twitter.com/intent/follow?screen_name=mob_sakai) [![](https://img.shields.io/twitter/follow/mob_sakai.svg?label=Follow&style=social)](https://twitter.com/intent/follow?screen_name=mob_sakai)
<< [📝 Description](#-description-) | [📌 Key Features](#-key-features) | [🎮 Demo](#-demo) | [⚙ Installation](#-installation) | [🚀 Usage](#-usage) | [🛠 Development Note](#-development-note) | [🤝 Contributing](#-contributing) >> << [📝 Description](#-description) | [🎮 Demo](#-demo) | [⚙ Installation](#-installation) | [🚀 Usage](#-usage) | [🛠 Development Note](#-development-note) | [🤝 Contributing](#-contributing) >>
## 📝 Description <!-- omit in toc -->
![](https://user-images.githubusercontent.com/12690315/41771577-8da4b968-7650-11e8-9524-cd162c422d9d.gif)
This package uses the new APIs `MeshBake/MeshTrailBake` (introduced in Unity 2018.2) to render particles through `CanvasRenderer`.
You can render, mask, and sort your `ParticleSystems` for UI without the need for an additional `Camera`, `RenderTexture`, or `Canvas`.
- [📌 Key Features](#-key-features)
- [🎮 Demo](#-demo)
- [⚙ Installation](#-installation)
- [Install via OpenUPM](#install-via-openupm)
- [Install via UPM (with Package Manager UI)](#install-via-upm-with-package-manager-ui)
- [Install via UPM (Manually)](#install-via-upm-manually)
- [Install as Embedded Package](#install-as-embedded-package)
- [🚀 Usage](#-usage)
- [Component: UIParticle](#component-uiparticle)
- [Basic Usage](#basic-usage)
- [Usage with Your Existing ParticleSystem Prefab](#usage-with-your-existing-particlesystem-prefab)
- [Usage with `Mask` or `RectMask2D` Component](#usage-with-mask-or-rectmask2d-component)
- [Usage with Script](#usage-with-script)
- [Component: UIParticleAttractor](#component-uiparticleattractor)
- [Project Settings](#project-settings)
- [🛠 Development Note](#-development-note)
- [Compares the Baking mesh approach with the conventional approach](#compares-the-baking-mesh-approach-with-the-conventional-approach)
- [Performance test results](#performance-test-results)
- [🔍 FAQ: Why Are My UIParticles Not Displayed Correctly?](#-faq-why-are-my-uiparticles-not-displayed-correctly)
- [Shader Limitation](#shader-limitation)
- [Built-in shaders are not supported](#built-in-shaders-are-not-supported)
- [(Unity 2018 or 2019) UV.zw components will be discarded](#unity-2018-or-2019-uvzw-components-will-be-discarded)
- [(Unity 2018 or 2019) Custom vertex streams](#unity-2018-or-2019-custom-vertex-streams)
- [Overheads](#overheads)
- [How to Make a Custom Shader to Support `Mask` and `RectMask2D` Component](#how-to-make-a-custom-shader-to-support-mask-and-rectmask2d-component)
- [🤝 Contributing](#-contributing)
- [Issues](#issues)
- [Pull Requests](#pull-requests)
- [Support](#support)
- [License](#license)
- [Author](#author)
- [See Also](#see-also)
<br><br> <br><br>
## 📌 Key Features ## 📝 Description
* **Easy to use:** The package is ready to use out of the box. ![](https://user-images.githubusercontent.com/12690315/41771577-8da4b968-7650-11e8-9524-cd162c422d9d.gif)
* **Sortable:** Sort particle effects and other UI elements by sibling index.
* **Maskable:** Supports `Mask` or `RectMask2D`. This package utilizes the new APIs `MeshBake/MashTrailBake` (introduced with Unity 2018.2) to render particles through
* **No extra components required:** No need for an additional `Camera`, `RenderTexture`, or `Canvas`. CanvasRenderer.
* **Trail module support:** Fully supports the Trail module. You can render, mask, and sort your ParticleSystems for UI without the necessity of an additional Camera, RenderTexture,
* **CanvasGroup alpha support:** Integrates with `CanvasGroup` alpha. or Canvas.
* **No allocations:** Efficiently renders particles without allocations.
* **Any canvas render mode support:** Works with overlay, camera space, and world space. ### Features
* **Any Render pipeline support:** Compatible with Universal Render Pipeline (URP) and High Definition Render Pipeline (HDRP).
* **Disabling domain reload support:** Supports disabling `Enter Play Mode Options > Reload Domain`. * Easy to use: The package is ready to use out of the box.
* **Animatable material properties:** Supports changing material properties with AnimationClip (AnimatableProperty). * Sort particle effects and other UI by sibling index.
![AnimatableProperty.gif](https://user-images.githubusercontent.com/12690315/53286323-2d94a980-37b0-11e9-8afb-c4a207805ff2.gif) * No extra Camera, RenderTexture, or Canvas required.
* **Multiple materials:** Supports 8+ materials. * Masking options for Mask or RectMask2D.
* **Correct positioning:** Adjusts world space particle positions correctly when changing window size for standalone platforms (Windows, MacOSX, and Linux). * Support for the Trail module.
* **Adaptive scaling:** Provides adaptive scaling for UI (AutoScalingMode). * Support for CanvasGroup alpha.
* **Performance optimization:** Mesh sharing group to improve performance. * No allocations needed to render particles.
<img alt="MeshSharing.gif" src="https://user-images.githubusercontent.com/12690315/174311048-c882df81-6c34-4eba-b0aa-5645457692f1.gif" width="450"/> * Compatibility with overlay, camera space, and world space.
* **Particle attractor:** Includes a particle attractor component. * Support for Universal Render Pipeline (URP) and High Definition Render Pipeline (HDRP).
<img alt="ParticleAttractor.gif" src="https://user-images.githubusercontent.com/12690315/174311027-462929a4-13f0-4ec4-86ea-9c832f2eecf1.gif" width="450"/> * Support for disabling `Enter Play Mode Options > Reload Domain`.
* **Emission position mode:** Supports relative/absolute particle emission position modes. * Support for changing material property with AnimationClip (AnimatableProperty).
<img alt="AbsolutePosition.gif" src="https://user-images.githubusercontent.com/12690315/175751579-5a2357e8-2ecf-4afd-83c8-66e9771bde39.gif" width="450"/> ![AnimatableProperty.gif][AnimatableProperty.gif]
* **Custom view size:** Fixes min/max particle size mismatch. * [4.0.0+] Support for 8+ materials.
![CustomViewSize.gif](https://github.com/mob-sakai/ParticleEffectForUGUI/assets/12690315/dd929959-1a37-420b-b13d-e849022b9c9d) * [4.0.0+] Correct world space particle position adjustment when changing window size for standalone platforms (Windows,
MacOSX, and Linux).
* [4.0.0+] Adaptive scaling for UI.
* [4.0.0+] Mesh sharing group to improve performance.
![MeshSharing.gif][MeshSharing.gif]
* [4.0.0+] Particle attractor component.
![ParticleAttractor.gif][ParticleAttractor.gif]
* [4.1.0+] Relative/Absolute particle position mode.
![AbsolutePosition.gif][AbsolutePosition.gif]
[AnimatableProperty.gif]: https://user-images.githubusercontent.com/12690315/53286323-2d94a980-37b0-11e9-8afb-c4a207805ff2.gif
[MeshSharing.gif]: https://user-images.githubusercontent.com/12690315/174311048-c882df81-6c34-4eba-b0aa-5645457692f1.gif
[ParticleAttractor.gif]: https://user-images.githubusercontent.com/12690315/174311027-462929a4-13f0-4ec4-86ea-9c832f2eecf1.gif
[AbsolutePosition.gif]: https://user-images.githubusercontent.com/12690315/175751579-5a2357e8-2ecf-4afd-83c8-66e9771bde39.gif
<br><br> <br><br>
@@ -100,86 +76,76 @@ You can render, mask, and sort your `ParticleSystems` for UI without the need fo
[JMO]: https://assetstore.unity.com/publishers/1669 [JMO]: https://assetstore.unity.com/publishers/1669
<br><br> <br><br>
## ⚙ Installation ## ⚙ Installation
_This package requires **Unity 2018.3 or later**._ _This package requires Unity 2018.3 or later._
#### Install via OpenUPM #### Install via OpenUPM
- This package is available on [OpenUPM](https://openupm.com) package registry. This package is available on [OpenUPM](https://openupm.com) package registry.
- This is the preferred method of installation, as you can easily receive updates as they're released. This is the preferred method of installation, as you can easily receive updates as they're released.
- If you have [openupm-cli](https://github.com/openupm/openupm-cli) installed, then run the following command in your project's directory:
```
openupm add com.coffee.ui-particle
```
- To update the package, use Package Manager UI (`Window > Package Manager`) or run the following command with `@{version}`:
```
openupm add com.coffee.ui-particle@4.9.0
```
#### Install via UPM (with Package Manager UI) If you have [openupm-cli](https://github.com/openupm/openupm-cli) installed, then run the following command in your
project's directory:
- Click `Window > Package Manager` to open Package Manager UI. ```sh
- Click `+ > Add package from git URL...` and input the repository URL: `https://github.com/mob-sakai/ParticleEffectForUGUI.git` openupm add com.coffee.ui-particle
![](https://github.com/user-attachments/assets/f88f47ad-c606-44bd-9e86-ee3f72eac548) ```
- To update the package, change suffix `#{version}` to the target version.
- e.g. `https://github.com/mob-sakai/ParticleEffectForUGUI.git#4.9.0`
#### Install via UPM (Manually) #### Install via UPM (using Git URL)
- Open the `Packages/manifest.json` file in your project. Then add this package somewhere in the `dependencies` block: Navigate to your project's Packages folder and open the `manifest.json` file. Then add this package somewhere in
```json the `dependencies` block:
{
"dependencies": { ```json
"com.coffee.ui-particle": "https://github.com/mob-sakai/ParticleEffectForUGUI.git", {
... "dependencies": {
} "com.coffee.ui-particle": "https://github.com/mob-sakai/ParticleEffectForUGUI.git",
...
} }
``` }
```
- To update the package, change suffix `#{version}` to the target version. To update the package, change suffix `#{version}` to the target version.
- e.g. `"com.coffee.ui-particle": "https://github.com/mob-sakai/ParticleEffectForUGUI.git#4.9.0",`
#### Install as Embedded Package * e.g. `"com.coffee.ui-particle": "https://github.com/mob-sakai/ParticleEffectForUGUI.git#4.6.0",`
1. Download a source code zip file from [Releases](https://github.com/mob-sakai/ParticleEffectForUGUI.git/releases) and extract it. Or, use [UpmGitExtension](https://github.com/mob-sakai/UpmGitExtension) to install and update the package.
2. Place it in your project's `Packages` directory.
![](https://github.com/mob-sakai/mob-sakai/assets/12690315/0b7484b4-5fca-43b0-a9ef-e5dbd99bcdb4) <br><br>
- If you want to fix bugs or add features, install it as an embedded package.
- To update the package, you need to re-download it and replace the contents. ## ⚙ Upgrading from 3.x/4.x to 5.x
### Breaking Changes
- The default value of `UIParticle.scale` has been changed from `10` to `1`.
- `UIParticle` no longer inherits from `MaskableGraphic`.
-
<br><br> <br><br>
## 🚀 Usage ## 🚀 Usage
### Component: UIParticle ### UIParticle Component
`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.
![](https://github.com/mob-sakai/ParticleEffectForUGUI/assets/12690315/1cf5753b-33fc-4cef-91c3-413c515a954f) ![](https://github.com/mob-sakai/ParticleEffectForUGUI/assets/12690315/3559df45-63e7-4c4c-9233-f455779efa29)
- **Maskable**: Does this graphic allow maskable. - **Maskable**: Does this graphic allow masking.
- **Scale**: Scale the rendering particles. When the `3D` toggle is enabled, 3D scale (x, y, z) is supported. - **Scale**: Scale the rendering. 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 the changes.
- **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.
- **Auto:** Automatically select Primary/Replica.
- **Primary:** Provides particle simulation results to the same group.
- **Primary Simulator:** Primary, but do not render the particle (simulation only).
- **Replica:** Render simulation results provided by the primary.
- **Position Mode**: Emission position mode. - **Position Mode**: Emission position mode.
- **Absolute:** The particles will be emitted from the world position. - **Absolute:** Emit from the world position of the `ParticleSystem`.
- **Relative:** The particles will be emitted from the scaled position. - **Relative:** Emit from the scaled position of the `ParticleSystem`.
- **Auto Scaling Mode**: How to automatically adjust when the Canvas scale is changed by the screen size or reference resolution. - **Auto Scaling**: `Transform.lossyScale` (=world scale) will be set to `(1, 1, 1)` on update. It prevents the
- **None:** Do nothing. root-Canvas scale from affecting the hierarchy-scaled `ParticleSystem`.
- **Transform:** Transform.lossyScale (=world scale) will be set to (1, 1, 1).
- **UIParticle:** UIParticle.scale will be adjusted.
- **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.
- **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 **NOTE:** Press the `Refresh` button to reconstruct the rendering order based on children ParticleSystem's sorting order
@@ -187,7 +153,7 @@ and z-position.
<br><br> <br><br>
### Basic Usage #### Basic Usage
1. Select `GameObject/UI/ParticleSystem` to create UIParticle with a ParticleSystem. 1. Select `GameObject/UI/ParticleSystem` to create UIParticle with a ParticleSystem.
![particle](https://user-images.githubusercontent.com/12690315/95007361-cad0e880-0649-11eb-8835-f145d62c5977.png) ![particle](https://user-images.githubusercontent.com/12690315/95007361-cad0e880-0649-11eb-8835-f145d62c5977.png)
@@ -196,7 +162,7 @@ and z-position.
<br> <br>
### Usage with Your Existing ParticleSystem Prefab #### With Your Existing ParticleSystem Prefab
1. Select `GameObject/UI/ParticleSystem (Empty)` to create UIParticle. 1. Select `GameObject/UI/ParticleSystem (Empty)` to create UIParticle.
![empty](https://user-images.githubusercontent.com/12690315/95007362-cb697f00-0649-11eb-8a09-29b0a13791e4.png) ![empty](https://user-images.githubusercontent.com/12690315/95007362-cb697f00-0649-11eb-8a09-29b0a13791e4.png)
@@ -205,7 +171,7 @@ and z-position.
<br> <br>
### Usage with `Mask` or `RectMask2D` Component #### With `Mask` or `RectMask2D` Component
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.
@@ -217,7 +183,7 @@ section.
<br><br> <br><br>
### Usage with Script ### Script usage
```cs ```cs
// Instantiate ParticleSystem prefab with UIParticle on runtime. // Instantiate ParticleSystem prefab with UIParticle on runtime.
@@ -236,14 +202,14 @@ uiParticle.Stop();
<br><br> <br><br>
### Component: UIParticleAttractor ### UIParticleAttractor component
`UIParticleAttractor` attracts particles generated by the specified ParticleSystem. `UIParticleAttractor` attracts particles generated by the specified ParticleSystem.
![](https://github.com/mob-sakai/ParticleEffectForUGUI/assets/12690315/5c20ad73-4b9a-4f38-9cdc-119df5cce077) ![](https://github.com/mob-sakai/ParticleEffectForUGUI/assets/12690315/ea6ae0ed-f9a8-437c-8baa-47526303391e)
![](https://user-images.githubusercontent.com/12690315/174311027-462929a4-13f0-4ec4-86ea-9c832f2eecf1.gif) ![](https://user-images.githubusercontent.com/12690315/174311027-462929a4-13f0-4ec4-86ea-9c832f2eecf1.gif)
- **Particle Systems**: Attracts particles generated by the specified ParticleSystems. - **Particle System**: Attracts particles generated by the specified particle system.
- **Destination Radius**: Once the particle is within the radius, the particle lifetime will become 0, and `OnAttracted` - **Destination Radius**: Once the particle is within the radius, the particle lifetime will become 0, and `OnAttracted`
will be called. will be called.
- **Delay Rate**: Delay to start attracting. It is a percentage of the particle's start lifetime. - **Delay Rate**: Delay to start attracting. It is a percentage of the particle's start lifetime.
@@ -257,14 +223,6 @@ uiParticle.Stop();
<br><br> <br><br>
### Project Settings
![](https://github.com/user-attachments/assets/befc7f34-fb47-4006-831a-eba79fda11ca)
- Click `Edit > Project Settings` to open the Project Settings window and then select `UI > UI Particle` category.
<br><br>
## 🛠 Development Note ## 🛠 Development Note
### Compares the Baking mesh approach with the conventional approach ### Compares the Baking mesh approach with the conventional approach
@@ -396,7 +354,7 @@ When improving performance, keep the following in mind:
- Consider a single material, atlasing the sprites, and using `Sprite` mode in the `Texture Sheet Animation` module - Consider a single material, atlasing the sprites, and using `Sprite` mode in the `Texture Sheet Animation` module
in the ParticleSystem. in the ParticleSystem.
### How to Make a Custom Shader to Support `Mask` and `RectMask2D` Component ### How to Make a Custom Shader to Support Mask/RectMask2D Component
<details> <details>
<summary>Shader tips</summary> <summary>Shader tips</summary>

View File

@@ -50,7 +50,7 @@ namespace Coffee.UIParticleInternal
var childCount = tr.childCount; var childCount = tr.childCount;
for (var i = 0; i < childCount; i++) for (var i = 0; i < childCount; i++)
{ {
tr.GetChild(i).GetComponentsInChildren_Internal(results, depth - 1); tr.GetChild(i).GetComponentsInChildren(results, depth - 1);
} }
} }
@@ -134,35 +134,6 @@ 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
{ {

View File

@@ -0,0 +1,19 @@
using System.Collections.Generic;
namespace Coffee.UIParticleInternal
{
/// <summary>
/// Extension methods for Component class.
/// </summary>
internal static class ListExtensions
{
public static void RemoveAtFast<T>(this List<T> self, int index)
{
if (self == null) return;
var lastIndex = self.Count - 1;
self[index] = self[lastIndex];
self.RemoveAt(lastIndex);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 833553390099d40c9b212823f0852c46
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -6,15 +6,6 @@ namespace Coffee.UIParticleInternal
{ {
internal static class Misc internal static class Misc
{ {
public static T[] FindObjectsOfType<T>() where T : Object
{
#if 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) public static void Destroy(Object obj)
{ {
if (!obj) return; if (!obj) return;

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 182319ecc315e4858b119764af0fbcb0 guid: 39ed6a6b0a72e482488bd298b2ae762e
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@@ -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;
@@ -13,51 +13,41 @@ namespace Coffee.UIParticleInternal
{ {
public abstract class PreloadedProjectSettings : ScriptableObject public abstract class PreloadedProjectSettings : ScriptableObject
#if UNITY_EDITOR #if UNITY_EDITOR
, IPreprocessBuildWithReport
{ {
private class Postprocessor : AssetPostprocessor int IOrderedCallback.callbackOrder => 0;
void IPreprocessBuildWithReport.OnPreprocessBuild(BuildReport report)
{ {
private static void OnPostprocessAllAssets(string[] _, string[] __, string[] ___, string[] ____) Initialize();
{
Initialize();
}
}
private class PreprocessBuildWithReport : IPreprocessBuildWithReport
{
int IOrderedCallback.callbackOrder => 0;
void IPreprocessBuildWithReport.OnPreprocessBuild(BuildReport report)
{
Initialize();
}
} }
[InitializeOnLoadMethod]
[InitializeOnEnterPlayMode]
private static void Initialize() private static void Initialize()
{ {
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)
{ {
// 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;
SetDefaultSettings(defaultSettings); SetDefaultSettings(defaultSettings);
} }
else if (GetPreloadedSettings(t).Length != 1) else if (GetPreloadedSettings(t).Length != 1)
{ {
SetDefaultSettings(defaultSettings); SetDefaultSettings(defaultSettings);
} }
if (defaultSettings)
{
defaultSettings.OnInitialize();
}
} }
EditorApplication.QueuePlayerLoopUpdate();
} }
protected static string GetDefaultName(Type type, bool nicify) protected static string GetDefaultName(Type type, bool nicify)
{ {
var typeName = type.Name; var typeName = type.Name.Replace("ProjectSettings", "");
return nicify return nicify
? ObjectNames.NicifyVariableName(typeName) ? ObjectNames.NicifyVariableName(typeName)
: typeName; : typeName;
@@ -81,8 +71,6 @@ namespace Coffee.UIParticleInternal
protected static void SetDefaultSettings(PreloadedProjectSettings asset) protected static void SetDefaultSettings(PreloadedProjectSettings asset)
{ {
if (!asset) return;
var type = asset.GetType(); var type = asset.GetType();
if (string.IsNullOrEmpty(AssetDatabase.GetAssetPath(asset))) if (string.IsNullOrEmpty(AssetDatabase.GetAssetPath(asset)))
{ {
@@ -93,11 +81,7 @@ 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();
@@ -111,20 +95,13 @@ namespace Coffee.UIParticleInternal
AssetDatabase.Refresh(); AssetDatabase.Refresh();
} }
protected virtual void OnCreateAsset()
{
}
protected virtual void OnInitialize()
{
}
} }
#else #else
{ {
} }
#endif #endif
public abstract class PreloadedProjectSettings<T> : PreloadedProjectSettings public abstract class PreloadedProjectSettings<T> : PreloadedProjectSettings
where T : PreloadedProjectSettings<T> where T : PreloadedProjectSettings<T>
{ {

View File

@@ -20,7 +20,6 @@ namespace Coffee.UIParticleInternal
/// </summary> /// </summary>
public void Add(T rhs) public void Add(T rhs)
{ {
if (rhs == null) return;
Profiler.BeginSample("(COF)[FastAction] Add Action"); Profiler.BeginSample("(COF)[FastAction] Add Action");
var node = s_NodePool.Rent(); var node = s_NodePool.Rent();
node.Value = rhs; node.Value = rhs;
@@ -33,7 +32,6 @@ namespace Coffee.UIParticleInternal
/// </summary> /// </summary>
public void Remove(T rhs) public void Remove(T rhs)
{ {
if (rhs == null) return;
Profiler.BeginSample("(COF)[FastAction] Remove Action"); Profiler.BeginSample("(COF)[FastAction] Remove Action");
var node = _delegates.Find(rhs); var node = _delegates.Find(rhs);
if (node != null) if (node != null)
@@ -65,11 +63,6 @@ namespace Coffee.UIParticleInternal
node = node.Next; node = node.Next;
} }
} }
public void Clear()
{
_delegates.Clear();
}
} }
/// <summary> /// <summary>

View File

@@ -46,6 +46,7 @@ namespace Coffee.UIParticleInternal
GetFrameCache<T>().Set((key1.GetHashCode(), key2.GetHashCode()), result); GetFrameCache<T>().Set((key1.GetHashCode(), key2.GetHashCode()), result);
} }
/// <summary> /// <summary>
/// Sets a value in the frame cache with a specified key. /// Sets a value in the frame cache with a specified key.
/// </summary> /// </summary>

View File

@@ -5,6 +5,7 @@ using Object = UnityEngine.Object;
#if ENABLE_COFFEE_LOGGER #if ENABLE_COFFEE_LOGGER
using System.Reflection; using System.Reflection;
using System.Collections.Generic; using System.Collections.Generic;
#else #else
using Conditional = System.Diagnostics.ConditionalAttribute; using Conditional = System.Diagnostics.ConditionalAttribute;
#endif #endif
@@ -42,6 +43,7 @@ namespace Coffee.UIParticleInternal
#endif #endif
} }
#if !ENABLE_COFFEE_LOGGER #if !ENABLE_COFFEE_LOGGER
[Conditional(k_DisableSymbol)] [Conditional(k_DisableSymbol)]
#endif #endif
@@ -51,6 +53,7 @@ namespace Coffee.UIParticleInternal
Log_Internal(LogType.Log, tag, message, context ? context : tag as Object); Log_Internal(LogType.Log, tag, message, context ? context : tag as Object);
} }
#if !ENABLE_COFFEE_LOGGER #if !ENABLE_COFFEE_LOGGER
[Conditional(k_DisableSymbol)] [Conditional(k_DisableSymbol)]
#endif #endif
@@ -59,6 +62,7 @@ namespace Coffee.UIParticleInternal
Log_Internal(LogType.Log, tag, message, context ? context : tag as Object); Log_Internal(LogType.Log, tag, message, context ? context : tag as Object);
} }
#if !ENABLE_COFFEE_LOGGER #if !ENABLE_COFFEE_LOGGER
[Conditional(k_DisableSymbol)] [Conditional(k_DisableSymbol)]
#endif #endif
@@ -76,6 +80,7 @@ namespace Coffee.UIParticleInternal
#endif #endif
} }
#if !ENABLE_COFFEE_LOGGER #if !ENABLE_COFFEE_LOGGER
[Conditional(k_DisableSymbol)] [Conditional(k_DisableSymbol)]
#endif #endif
@@ -119,6 +124,7 @@ namespace Coffee.UIParticleInternal
#endif #endif
} }
#if !ENABLE_COFFEE_LOGGER #if !ENABLE_COFFEE_LOGGER
[Conditional(k_DisableSymbol)] [Conditional(k_DisableSymbol)]
#endif #endif
@@ -135,9 +141,6 @@ namespace Coffee.UIParticleInternal
switch (tag) switch (tag)
{ {
case string name:
sb.Append(name);
break;
case Type type: case Type type:
AppendType(sb, type); AppendType(sb, type);
break; break;
@@ -164,6 +167,7 @@ namespace Coffee.UIParticleInternal
#endif #endif
} }
#if !ENABLE_COFFEE_LOGGER #if !ENABLE_COFFEE_LOGGER
[Conditional(k_DisableSymbol)] [Conditional(k_DisableSymbol)]
#endif #endif
@@ -202,6 +206,7 @@ namespace Coffee.UIParticleInternal
#endif #endif
} }
#if !ENABLE_COFFEE_LOGGER #if !ENABLE_COFFEE_LOGGER
[Conditional(k_DisableSymbol)] [Conditional(k_DisableSymbol)]
#endif #endif

View File

@@ -55,6 +55,7 @@ namespace Coffee.UIParticleInternal
Profiler.EndSample(); Profiler.EndSample();
} }
/// <summary> /// <summary>
/// Adds or retrieves a cached material based on the hash. /// Adds or retrieves a cached material based on the hash.
/// </summary> /// </summary>

View File

@@ -8,11 +8,10 @@ namespace Coffee.UIParticleInternal
{ {
internal class ObjectRepository<T> where T : Object internal class ObjectRepository<T> where T : Object
{ {
private readonly Dictionary<Hash128, Entry> _cache = new Dictionary<Hash128, Entry>(8); private readonly List<Entry> _cache = new List<Entry>();
private readonly Dictionary<int, Hash128> _objectKey = new Dictionary<int, Hash128>(8);
private readonly string _name; private readonly string _name;
private readonly Action<T> _onRelease; private readonly Action<T> _onRelease;
private readonly Stack<Entry> _pool = new Stack<Entry>(8); private readonly Stack<Entry> _pool = new Stack<Entry>();
public ObjectRepository(Action<T> onRelease = null) public ObjectRepository(Action<T> onRelease = null)
{ {
@@ -37,33 +36,40 @@ namespace Coffee.UIParticleInternal
{ {
_onRelease = onRelease; _onRelease = onRelease;
} }
for (var i = 0; i < 8; i++)
{
_pool.Push(new Entry());
}
} }
public int count => _cache.Count; public int count => _cache.Count;
public void Clear() public void Clear()
{ {
foreach (var kv in _cache) for (var i = 0; i < _cache.Count; i++)
{ {
var entry = kv.Value; var entry = _cache[i];
if (entry == null) continue; if (entry == null) continue;
entry.Release(_onRelease); entry.Release(_onRelease);
_pool.Push(entry);
} }
_cache.Clear(); _cache.Clear();
_objectKey.Clear();
} }
public bool Valid(Hash128 hash, T obj) public bool Valid(Hash128 hash, T obj)
{ {
return _cache.TryGetValue(hash, out var entry) && entry.storedObject == obj; // Find existing entry.
Profiler.BeginSample("(COF)[ObjectRepository] Valid > Find existing entry");
for (var i = 0; i < _cache.Count; ++i)
{
var entry = _cache[i];
if (entry.hash != hash) continue;
Profiler.EndSample();
// Existing entry found.
return entry.storedObject == obj;
}
Profiler.EndSample();
return false;
} }
/// <summary> /// <summary>
@@ -71,69 +77,82 @@ namespace Coffee.UIParticleInternal
/// </summary> /// </summary>
public void Get(Hash128 hash, ref T obj, Func<T> onCreate) public void Get(Hash128 hash, ref T obj, Func<T> onCreate)
{ {
if (GetFromCache(hash, ref obj)) return; // Find existing entry.
Add(hash, ref obj, onCreate()); Profiler.BeginSample("(COF)[ObjectRepository] Get > Find existing entry");
for (var i = 0; i < _cache.Count; ++i)
{
var entry = _cache[i];
if (entry.hash != hash) continue;
// Existing entry found.
if (entry.storedObject != obj)
{
// if the object is different, release the old one.
Release(ref obj);
++entry.reference;
obj = entry.storedObject;
Logging.Log(_name, $"Get(#{count}): {entry}");
}
Profiler.EndSample();
return;
}
Profiler.EndSample();
// Create new entry.
Profiler.BeginSample("(COF)[ObjectRepository] Get > Create new entry");
var newEntry = 0 < _pool.Count ? _pool.Pop() : new Entry();
newEntry.storedObject = onCreate();
newEntry.hash = hash;
newEntry.reference = 1;
_cache.Add(newEntry);
Logging.Log(_name, $"Get(#{count}): {newEntry}");
Release(ref obj);
obj = newEntry.storedObject;
Profiler.EndSample();
} }
/// <summary> /// <summary>
/// Adds or retrieves a cached object based on the hash. /// Adds or retrieves a cached object based on the hash.
/// </summary> /// </summary>
public void Get<TS>(Hash128 hash, ref T obj, Func<TS, T> onCreate, TS source) public void Get<TS>(Hash128 hash, ref T obj, Func<TS, T> onCreate, TS source)
{
if (GetFromCache(hash, ref obj)) return;
Add(hash, ref obj, onCreate(source));
}
private bool GetFromCache(Hash128 hash, ref T obj)
{ {
// Find existing entry. // Find existing entry.
Profiler.BeginSample("(COF)[ObjectRepository] GetFromCache"); Profiler.BeginSample("(COF)[ObjectRepository] Get > Find existing entry");
if (_cache.TryGetValue(hash, out var entry)) for (var i = 0; i < _cache.Count; ++i)
{ {
if (!entry.storedObject) var entry = _cache[i];
{ if (entry.hash != hash) continue;
Release(ref entry.storedObject);
Profiler.EndSample();
return false;
}
// Existing entry found.
if (entry.storedObject != obj) if (entry.storedObject != obj)
{ {
// if the object is different, release the old one. // if the object is different, release the old one.
Release(ref obj); Release(ref obj);
++entry.reference; ++entry.reference;
obj = entry.storedObject; obj = entry.storedObject;
Logging.Log(_name, $"Get(total#{count}): {entry}"); Logging.Log(_name, $"Get(#{count}): {entry}");
} }
Profiler.EndSample(); Profiler.EndSample();
return true;
}
Profiler.EndSample();
return false;
}
private void Add(Hash128 hash, ref T obj, T newObject)
{
if (!newObject)
{
Release(ref obj);
obj = newObject;
return; return;
} }
// Create and add a new entry. Profiler.EndSample();
Profiler.BeginSample("(COF)[ObjectRepository] Add");
// Create new entry.
Profiler.BeginSample("(COF)[ObjectRepository] Get > Create new entry");
var newEntry = 0 < _pool.Count ? _pool.Pop() : new Entry(); var newEntry = 0 < _pool.Count ? _pool.Pop() : new Entry();
newEntry.storedObject = newObject; newEntry.storedObject = onCreate(source);
newEntry.hash = hash; newEntry.hash = hash;
newEntry.reference = 1; newEntry.reference = 1;
_cache[hash] = newEntry; _cache.Add(newEntry);
_objectKey[newObject.GetInstanceID()] = hash; Logging.Log(_name, $"Get(#{count}): {newEntry}");
Logging.Log(_name, $"<color=#03c700>Add</color>(total#{count}): {newEntry}");
Release(ref obj); Release(ref obj);
obj = newObject; obj = newEntry.storedObject;
Profiler.EndSample(); Profiler.EndSample();
} }
@@ -144,45 +163,35 @@ namespace Coffee.UIParticleInternal
{ {
if (ReferenceEquals(obj, null)) return; if (ReferenceEquals(obj, null)) return;
// Find and release the entry.
Profiler.BeginSample("(COF)[ObjectRepository] Release"); Profiler.BeginSample("(COF)[ObjectRepository] Release");
var id = obj.GetInstanceID(); for (var i = 0; i < _cache.Count; i++)
if (_objectKey.TryGetValue(id, out var hash)
&& _cache.TryGetValue(hash, out var entry))
{ {
entry.reference--; var entry = _cache[i];
if (entry.reference <= 0 || !entry.storedObject)
if (entry.storedObject != obj)
{ {
Remove(entry); continue;
} }
else
if (--entry.reference <= 0)
{ {
Logging.Log(_name, $"Release(total#{_cache.Count}): {entry}"); Profiler.BeginSample("(COF)[ObjectRepository] Release > RemoveAt");
_cache.RemoveAtFast(i);
Logging.Log(_name, $"Release(#{_cache.Count}): {entry}");
entry.Release(_onRelease);
_pool.Push(entry);
Profiler.EndSample();
break;
} }
}
else Logging.Log(_name, $"Release(#{count}): {entry}");
{ break;
Logging.Log(_name, $"Release(total#{_cache.Count}): <color=red>Already released: {obj}</color>");
} }
obj = null; obj = null;
Profiler.EndSample(); Profiler.EndSample();
} }
private void Remove(Entry entry)
{
if (ReferenceEquals(entry, null)) return;
Profiler.BeginSample("(COF)[ObjectRepository] Remove");
_cache.Remove(entry.hash);
_objectKey.Remove(entry.storedObject.GetInstanceID());
_pool.Push(entry);
entry.reference = 0;
Logging.Log(_name, $"<color=#f29e03>Remove</color>(total#{_cache.Count}): {entry}");
entry.Release(_onRelease);
Profiler.EndSample();
}
private class Entry private class Entry
{ {
public Hash128 hash; public Hash128 hash;
@@ -202,7 +211,7 @@ namespace Coffee.UIParticleInternal
public override string ToString() public override string ToString()
{ {
return $"h{(uint)hash.GetHashCode()} (refs#{reference}), {storedObject}"; return $"h{(uint)hash.GetHashCode()} (#{reference}), {storedObject}";
} }
} }
} }

View File

@@ -14,8 +14,6 @@ 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()
{ {
@@ -50,15 +48,6 @@ 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>
@@ -75,12 +64,11 @@ namespace Coffee.UIParticleInternal
#if UNITY_EDITOR #if UNITY_EDITOR
[InitializeOnLoadMethod] [InitializeOnLoadMethod]
#endif #else
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
#endif
private static void InitializeOnLoad() private static void InitializeOnLoad()
{ {
Canvas.willRenderCanvases -= OnAfterCanvasRebuild;
s_IsInitializedAfterCanvasRebuild = false;
} }
/// <summary> /// <summary>
@@ -88,17 +76,6 @@ 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();
} }

View File

@@ -3,14 +3,12 @@ using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Coffee.UIParticleInternal; using Coffee.UIParticleInternal;
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Rendering; using UnityEngine.Rendering;
using UnityEngine.Serialization; using UnityEngine.Serialization;
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.PerformanceDemo")]
[assembly: InternalsVisibleTo("Coffee.UIParticle.Demo")]
namespace Coffee.UIExtensions namespace Coffee.UIExtensions
{ {
@@ -20,7 +18,7 @@ namespace Coffee.UIExtensions
[ExecuteAlways] [ExecuteAlways]
[RequireComponent(typeof(RectTransform))] [RequireComponent(typeof(RectTransform))]
[RequireComponent(typeof(CanvasRenderer))] [RequireComponent(typeof(CanvasRenderer))]
public class UIParticle : MaskableGraphic, ISerializationCallbackReceiver public class UIParticle : UIBehaviour, ISerializationCallbackReceiver
{ {
public enum AutoScalingMode public enum AutoScalingMode
{ {
@@ -60,12 +58,12 @@ namespace Coffee.UIExtensions
[Obsolete] [Obsolete]
internal bool m_AbsoluteMode; internal bool m_AbsoluteMode;
[Tooltip("Scale the rendering particles. When the `3D` toggle is enabled, 3D scale (x, y, z) is supported.")] [Tooltip("Particle effect scale")]
[SerializeField] [SerializeField]
private Vector3 m_Scale3D = new Vector3(10, 10, 10); private Vector3 m_Scale3D = new Vector3(1, 1, 1);
[Tooltip("If you want to update material properties (e.g. _MainTex_ST, _Color) in AnimationClip, " + [Tooltip("Animatable material properties.\n" +
"use this to mark as animatable.")] "If you want to change the material properties of the ParticleSystem in Animation, enable it.")]
[SerializeField] [SerializeField]
internal AnimatableProperty[] m_AnimatableProperties = new AnimatableProperty[0]; internal AnimatableProperty[] m_AnimatableProperties = new AnimatableProperty[0];
@@ -73,13 +71,12 @@ namespace Coffee.UIExtensions
[SerializeField] [SerializeField]
private List<ParticleSystem> m_Particles = new List<ParticleSystem>(); private List<ParticleSystem> m_Particles = new List<ParticleSystem>();
[Tooltip("Particle simulation results are shared within the same group. " + [Tooltip("Mesh sharing.\n" +
"A large number of the same effects can be displayed with a small load.\n" + "None: disable mesh sharing.\n" +
"None: Disable mesh sharing.\n" + "Auto: automatically select Primary/Replica.\n" +
"Auto: Automatically select Primary/Replica.\n" + "Primary: provides particle simulation results to the same group.\n" +
"Primary: Provides particle simulation results to the same group.\n" +
"Primary Simulator: Primary, but do not render the particle (simulation only).\n" + "Primary Simulator: Primary, but do not render the particle (simulation only).\n" +
"Replica: Render simulation results provided by the primary.")] "Replica: render simulation results provided by the primary.")]
[SerializeField] [SerializeField]
private MeshSharing m_MeshSharing = MeshSharing.None; private MeshSharing m_MeshSharing = MeshSharing.None;
@@ -91,53 +88,67 @@ namespace Coffee.UIExtensions
[SerializeField] [SerializeField]
private int m_GroupMaxId; private int m_GroupMaxId;
[Tooltip("Emission position mode.\n" + [Tooltip("Relative: The particles will be emitted from the scaled position of ParticleSystem.\n" +
"Relative: The particles will be emitted from the scaled position.\n" + "Absolute: The particles will be emitted from the world position of ParticleSystem.")]
"Absolute: The particles will be emitted from the world position.")]
[SerializeField] [SerializeField]
private PositionMode m_PositionMode = PositionMode.Relative; private PositionMode m_PositionMode = PositionMode.Relative;
[SerializeField] [SerializeField]
[Tooltip("Prevent the root-Canvas scale from affecting the hierarchy-scaled ParticleSystem.")]
[Obsolete] [Obsolete]
internal bool m_AutoScaling; internal bool m_AutoScaling;
[SerializeField] [SerializeField]
[Tooltip( [Tooltip("Transform: Transform.lossyScale (=world scale) will be set to (1, 1, 1)." +
"How to automatically adjust when the Canvas scale is changed by the screen size or reference resolution.\n" + "UIParticle: UIParticle.scale will be adjusted.")]
"None: Do nothing.\n" +
"Transform: Transform.lossyScale (=world scale) will be set to (1, 1, 1).\n" +
"UIParticle: UIParticle.scale will be adjusted.")]
private AutoScalingMode m_AutoScalingMode = AutoScalingMode.Transform; private AutoScalingMode m_AutoScalingMode = AutoScalingMode.Transform;
[SerializeField] [SerializeField]
[Tooltip("Use a custom view.\n" + private bool m_Maskable = true;
"Use this if the particles are not displayed correctly due to min/max particle size.")]
private bool m_UseCustomView;
[SerializeField]
[Tooltip("Custom view size.\n" +
"Change the bake view size.")]
private float m_CustomViewSize = 10;
private readonly List<UIParticleRenderer> _renderers = new List<UIParticleRenderer>(); private readonly List<UIParticleRenderer> _renderers = new List<UIParticleRenderer>();
private Camera _bakeCamera; private Canvas _canvas;
private int _groupId; private int _groupId;
private bool _isScaleStored; private Camera _bakeCamera;
private Vector3 _storedScale;
private DrivenRectTransformTracker _tracker; private DrivenRectTransformTracker _tracker;
private Vector3 _storedScale;
private bool _isScaleStored;
/// <summary> public RectTransform rectTransform => transform as RectTransform;
/// Should this graphic be considered a target for ray-casting?
/// </summary> public Canvas canvas
public override bool raycastTarget
{ {
get => false; get
set { } {
if (_canvas) return _canvas;
var tr = transform;
while (tr && !_canvas)
{
if (tr.TryGetComponent(out _canvas)) return _canvas;
tr = tr.parent;
}
return null;
}
} }
/// <summary> /// <summary>
/// Particle simulation results are shared within the same group. /// Does this graphic allow masking.
/// A large number of the same effects can be displayed with a small load. /// </summary>
public bool maskable
{
get => m_Maskable;
set
{
if (value == m_Maskable) return;
m_Maskable = value;
UpdateRendererMaterial();
}
}
/// <summary>
/// Mesh sharing.
/// None: disable mesh sharing. /// None: disable mesh sharing.
/// Auto: automatically select Primary/Replica. /// Auto: automatically select Primary/Replica.
/// Primary: provides particle simulation results to the same group. /// Primary: provides particle simulation results to the same group.
@@ -180,9 +191,9 @@ namespace Coffee.UIExtensions
} }
/// <summary> /// <summary>
/// Emission position mode. /// Particle position mode.
/// Relative: The particles will be emitted from the scaled position. /// Relative: The particles will be emitted from the scaled position of the ParticleSystem.
/// Absolute: The particles will be emitted from the world position. /// Absolute: The particles will be emitted from the world position of the ParticleSystem.
/// </summary> /// </summary>
public PositionMode positionMode public PositionMode positionMode
{ {
@@ -195,7 +206,6 @@ namespace Coffee.UIExtensions
/// Relative: The particles will be emitted from the scaled position of the ParticleSystem. /// Relative: The particles will be emitted from the scaled position of the ParticleSystem.
/// Absolute: The particles will be emitted from the world position of the ParticleSystem. /// Absolute: The particles will be emitted from the world position of the ParticleSystem.
/// </summary> /// </summary>
[Obsolete("The absoluteMode is now obsolete. Please use the autoScalingMode instead.", false)]
public bool absoluteMode public bool absoluteMode
{ {
get => m_PositionMode == PositionMode.Absolute; get => m_PositionMode == PositionMode.Absolute;
@@ -213,12 +223,8 @@ namespace Coffee.UIExtensions
} }
/// <summary> /// <summary>
/// How to automatically adjust when the Canvas scale is changed by the screen size or reference resolution. /// Auto scaling mode.
/// <para/>
/// None: Do nothing.
/// <para/>
/// Transform: Transform.lossyScale (=world scale) will be set to (1, 1, 1). /// Transform: Transform.lossyScale (=world scale) will be set to (1, 1, 1).
/// <para/>
/// UIParticle: UIParticle.scale will be adjusted. /// UIParticle: UIParticle.scale will be adjusted.
/// </summary> /// </summary>
public AutoScalingMode autoScalingMode public AutoScalingMode autoScalingMode
@@ -237,26 +243,6 @@ namespace Coffee.UIExtensions
} }
} }
/// <summary>
/// Use a custom view.
/// Use this if the particles are not displayed correctly due to min/max particle size.
/// </summary>
public bool useCustomView
{
get => m_UseCustomView;
set => m_UseCustomView = value;
}
/// <summary>
/// Custom view size.
/// Change the bake view size.
/// </summary>
public float customViewSize
{
get => m_CustomViewSize;
set => m_CustomViewSize = Mathf.Max(0.1f, value);
}
internal bool useMeshSharing => m_MeshSharing != MeshSharing.None; internal bool useMeshSharing => m_MeshSharing != MeshSharing.None;
internal bool isPrimary => internal bool isPrimary =>
@@ -302,6 +288,22 @@ namespace Coffee.UIExtensions
public List<ParticleSystem> particles => m_Particles; public List<ParticleSystem> particles => m_Particles;
/// <summary>
/// Get all base materials to render.
/// </summary>
public IEnumerable<Material> materials
{
get
{
for (var i = 0; i < _renderers.Count; i++)
{
var r = _renderers[i];
if (!r || !r.material) continue;
yield return r.material;
}
}
}
/// <summary> /// <summary>
/// Paused. /// Paused.
/// </summary> /// </summary>
@@ -309,15 +311,15 @@ namespace Coffee.UIExtensions
public Vector3 parentScale { get; private set; } public Vector3 parentScale { get; private set; }
public Vector3 canvasScale { get; private set; } private Vector3 canvasScale { get; set; }
protected override void OnEnable() protected override void OnEnable()
{ {
_isScaleStored = false; _isScaleStored = false;
ResetGroupId(); ResetGroupId();
UIParticleUpdater.Register(this); UIParticleUpdater.Register(this);
RegisterDirtyMaterialCallback(UpdateRendererMaterial);
//
if (0 < particles.Count) if (0 < particles.Count)
{ {
RefreshParticles(particles); RefreshParticles(particles);
@@ -327,7 +329,7 @@ namespace Coffee.UIExtensions
RefreshParticles(); RefreshParticles();
} }
base.OnEnable(); UpdateRendererMaterial();
} }
/// <summary> /// <summary>
@@ -344,9 +346,15 @@ namespace Coffee.UIExtensions
_isScaleStored = false; _isScaleStored = false;
UIParticleUpdater.Unregister(this); UIParticleUpdater.Unregister(this);
_renderers.ForEach(r => r.Reset()); _renderers.ForEach(r => r.Reset());
UnregisterDirtyMaterialCallback(UpdateRendererMaterial); _canvas = null;
}
base.OnDisable(); /// <summary>
/// Called when the state of the parent Canvas is changed.
/// </summary>
protected override void OnCanvasHierarchyChanged()
{
_canvas = null;
} }
/// <summary> /// <summary>
@@ -356,6 +364,14 @@ namespace Coffee.UIExtensions
{ {
} }
/// <summary>
/// This function is called when a direct or indirect parent of the transform of the GameObject has changed.
/// </summary>
protected override void OnTransformParentChanged()
{
_canvas = null;
}
void ISerializationCallbackReceiver.OnBeforeSerialize() void ISerializationCallbackReceiver.OnBeforeSerialize()
{ {
} }
@@ -446,21 +462,6 @@ namespace Coffee.UIExtensions
isPaused = true; isPaused = true;
} }
/// <summary>
/// Get all base materials to render.
/// </summary>
public void GetMaterials(List<Material> result)
{
if (result == null) return;
for (var i = 0; i < _renderers.Count; i++)
{
var r = _renderers[i];
if (!r || !r.material) continue;
result.Add(r.material);
}
}
/// <summary> /// <summary>
/// Refresh UIParticle using the ParticleSystem instance. /// Refresh UIParticle using the ParticleSystem instance.
/// </summary> /// </summary>
@@ -480,9 +481,6 @@ namespace Coffee.UIExtensions
for (var i = 0; i < childCount; i++) for (var i = 0; i < childCount; i++)
{ {
var go = transform.GetChild(i).gameObject; var go = transform.GetChild(i).gameObject;
if (go.TryGetComponent<Camera>(out var cam) && cam == _bakeCamera) continue;
if (go.TryGetComponent<UIParticleRenderer>(out var _)) continue;
go.SetActive(false); go.SetActive(false);
if (destroyOldParticles) if (destroyOldParticles)
{ {
@@ -604,12 +602,8 @@ namespace Coffee.UIExtensions
} }
var currentScale = transform.localScale; var currentScale = transform.localScale;
if (!_isScaleStored) _storedScale = currentScale;
{ _isScaleStored = true;
_storedScale = currentScale.IsVisible() ? currentScale : Vector3.one;
_isScaleStored = true;
}
_tracker.Add(this, rectTransform, DrivenTransformProperties.Scale); _tracker.Add(this, rectTransform, DrivenTransformProperties.Scale);
var newScale = parentScale.Inverse(); var newScale = parentScale.Inverse();
if (currentScale != newScale) if (currentScale != newScale)
@@ -648,17 +642,6 @@ namespace Coffee.UIExtensions
: Random.Range(m_GroupId, m_GroupMaxId + 1); : Random.Range(m_GroupId, m_GroupMaxId + 1);
} }
protected override void UpdateMaterial()
{
}
/// <summary>
/// Call to update the geometry of the Graphic onto the CanvasRenderer.
/// </summary>
protected override void UpdateGeometry()
{
}
private void UpdateRendererMaterial() private void UpdateRendererMaterial()
{ {
for (var i = 0; i < _renderers.Count; i++) for (var i = 0; i < _renderers.Count; i++)
@@ -688,16 +671,7 @@ namespace Coffee.UIExtensions
private Camera GetBakeCamera() private Camera GetBakeCamera()
{ {
if (!canvas) return Camera.main; if (!canvas) return Camera.main;
if (!useCustomView && canvas.renderMode != RenderMode.ScreenSpaceOverlay && canvas.rootCanvas.worldCamera) if (_bakeCamera) return _bakeCamera;
{
return canvas.rootCanvas.worldCamera;
}
if (_bakeCamera)
{
_bakeCamera.orthographicSize = useCustomView ? customViewSize : 10;
return _bakeCamera;
}
// Find existing baking camera. // Find existing baking camera.
var childCount = transform.childCount; var childCount = transform.childCount;
@@ -714,7 +688,11 @@ namespace Coffee.UIExtensions
// Create baking camera. // Create baking camera.
if (!_bakeCamera) if (!_bakeCamera)
{ {
var go = new GameObject("[generated] UIParticle BakingCamera"); var go = new GameObject("[generated] UIParticle BakingCamera")
{
// hideFlags = HideFlags.HideAndDontSave
hideFlags = HideFlags.DontSave
};
go.SetActive(false); go.SetActive(false);
go.transform.SetParent(transform, false); go.transform.SetParent(transform, false);
_bakeCamera = go.AddComponent<Camera>(); _bakeCamera = go.AddComponent<Camera>();
@@ -722,7 +700,7 @@ namespace Coffee.UIExtensions
// Setup baking camera. // Setup baking camera.
_bakeCamera.enabled = false; _bakeCamera.enabled = false;
_bakeCamera.orthographicSize = useCustomView ? customViewSize : 10; _bakeCamera.orthographicSize = 1000;
_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;
@@ -733,9 +711,6 @@ namespace Coffee.UIExtensions
_bakeCamera.renderingPath = RenderingPath.Forward; _bakeCamera.renderingPath = RenderingPath.Forward;
_bakeCamera.useOcclusionCulling = false; _bakeCamera.useOcclusionCulling = false;
_bakeCamera.gameObject.SetActive(false);
_bakeCamera.gameObject.hideFlags = UIParticleProjectSettings.globalHideFlags;
return _bakeCamera; return _bakeCamera;
} }
} }

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Generic;
using Coffee.UIParticleInternal; using Coffee.UIParticleInternal;
using UnityEngine; using UnityEngine;
using UnityEngine.Events; using UnityEngine.Events;
@@ -7,7 +6,7 @@ using UnityEngine.Events;
namespace Coffee.UIExtensions namespace Coffee.UIExtensions
{ {
[ExecuteAlways] [ExecuteAlways]
public class UIParticleAttractor : MonoBehaviour, ISerializationCallbackReceiver public class UIParticleAttractor : MonoBehaviour
{ {
public enum Movement public enum Movement
{ {
@@ -23,12 +22,8 @@ namespace Coffee.UIExtensions
} }
[SerializeField] [SerializeField]
[HideInInspector]
private ParticleSystem m_ParticleSystem; private ParticleSystem m_ParticleSystem;
[SerializeField]
private List<ParticleSystem> m_ParticleSystems = new List<ParticleSystem>();
[Range(0.1f, 10f)] [Range(0.1f, 10f)]
[SerializeField] [SerializeField]
private float m_DestinationRadius = 1; private float m_DestinationRadius = 1;
@@ -50,7 +45,7 @@ namespace Coffee.UIExtensions
[SerializeField] [SerializeField]
private UnityEvent m_OnAttracted; private UnityEvent m_OnAttracted;
private List<UIParticle> _uiParticles = new List<UIParticle>(); private UIParticle _uiParticle;
public float destinationRadius public float destinationRadius
{ {
@@ -89,46 +84,25 @@ namespace Coffee.UIExtensions
} }
/// <summary> /// <summary>
/// The target ParticleSystems to attract. Use <see cref="AddParticleSystem"/> and /// The target ParticleSystem to attract.
/// <see cref="RemoveParticleSystem"/> to modify the list.
/// </summary> /// </summary>
public IReadOnlyList<ParticleSystem> particleSystems => m_ParticleSystems; #if UNITY_EDITOR
public new ParticleSystem particleSystem
public void AddParticleSystem(ParticleSystem ps) #else
public ParticleSystem particleSystem
#endif
{ {
if (m_ParticleSystems == null) get => m_ParticleSystem;
set
{ {
m_ParticleSystems = new List<ParticleSystem>(); m_ParticleSystem = value;
ApplyParticleSystem();
} }
var i = m_ParticleSystems.IndexOf(ps);
if (0 <= i) return; // Already added: skip
m_ParticleSystems.Add(ps);
_uiParticles.Clear();
}
public void RemoveParticleSystem(ParticleSystem ps)
{
if (m_ParticleSystems == null)
{
return;
}
var i = m_ParticleSystems.IndexOf(ps);
if (i < 0) return; // Not found. skip
m_ParticleSystems.RemoveAt(i);
_uiParticles.Clear();
}
private void Awake()
{
UpgradeIfNeeded();
} }
private void OnEnable() private void OnEnable()
{ {
ApplyParticleSystem();
UIParticleUpdater.Register(this); UIParticleUpdater.Register(this);
} }
@@ -139,96 +113,85 @@ namespace Coffee.UIExtensions
private void OnDestroy() private void OnDestroy()
{ {
_uiParticles = null; _uiParticle = null;
m_ParticleSystems = null; m_ParticleSystem = null;
} }
internal void Attract() internal void Attract()
{ {
// Collect UIParticle if needed (same size as m_ParticleSystems) if (m_ParticleSystem == null) return;
CollectUIParticlesIfNeeded();
for (var particleIndex = 0; particleIndex < m_ParticleSystems.Count; particleIndex++) var count = m_ParticleSystem.particleCount;
if (count == 0) return;
var particles = ParticleSystemExtensions.GetParticleArray(count);
m_ParticleSystem.GetParticles(particles, count);
var dstPos = GetDestinationPosition();
for (var i = 0; i < count; i++)
{ {
var particleSystem = m_ParticleSystems[particleIndex]; // Attracted
var p = particles[i];
// Skip: The ParticleSystem is not active if (0f < p.remainingLifetime && Vector3.Distance(p.position, dstPos) < m_DestinationRadius)
if (particleSystem == null || !particleSystem.gameObject.activeInHierarchy) continue;
// Skip: No active particles
var count = particleSystem.particleCount;
if (count == 0) continue;
var particles = ParticleSystemExtensions.GetParticleArray(count);
particleSystem.GetParticles(particles, count);
var uiParticle = _uiParticles[particleIndex];
var dstPos = GetDestinationPosition(uiParticle, particleSystem);
for (var i = 0; i < count; i++)
{ {
// Attracted p.remainingLifetime = 0f;
var p = particles[i]; particles[i] = p;
if (0f < p.remainingLifetime && Vector3.Distance(p.position, dstPos) < m_DestinationRadius)
if (m_OnAttracted != null)
{ {
p.remainingLifetime = 0f; try
particles[i] = p;
if (m_OnAttracted != null)
{ {
try m_OnAttracted.Invoke();
{ }
m_OnAttracted.Invoke(); catch (Exception e)
} {
catch (Exception e) Debug.LogException(e);
{
Debug.LogException(e);
}
} }
continue;
} }
// Calc attracting time continue;
var delayTime = p.startLifetime * m_DelayRate;
var duration = p.startLifetime - delayTime;
var time = Mathf.Max(0, p.startLifetime - p.remainingLifetime - delayTime);
// Delay
if (time <= 0) continue;
// Attract
p.position = GetAttractedPosition(p.position, dstPos, duration, time);
p.velocity *= 0.5f;
particles[i] = p;
} }
particleSystem.SetParticles(particles, count); // Calc attracting time
var delayTime = p.startLifetime * m_DelayRate;
var duration = p.startLifetime - delayTime;
var time = Mathf.Max(0, p.startLifetime - p.remainingLifetime - delayTime);
// Delay
if (time <= 0) continue;
// Attract
p.position = GetAttractedPosition(p.position, dstPos, duration, time);
p.velocity *= 0.5f;
particles[i] = p;
} }
m_ParticleSystem.SetParticles(particles, count);
} }
private Vector3 GetDestinationPosition(UIParticle uiParticle, ParticleSystem particleSystem) private Vector3 GetDestinationPosition()
{ {
var isUI = uiParticle && uiParticle.enabled; var isUI = _uiParticle && _uiParticle.enabled;
var psPos = particleSystem.transform.position; var psPos = m_ParticleSystem.transform.position;
var attractorPos = transform.position; var attractorPos = transform.position;
var dstPos = attractorPos; var dstPos = attractorPos;
var isLocalSpace = particleSystem.IsLocalSpace(); var isLocalSpace = m_ParticleSystem.IsLocalSpace();
if (isLocalSpace) if (isLocalSpace)
{ {
dstPos = particleSystem.transform.InverseTransformPoint(dstPos); dstPos = m_ParticleSystem.transform.InverseTransformPoint(dstPos);
} }
if (isUI) if (isUI)
{ {
var inverseScale = uiParticle.parentScale.Inverse(); var inverseScale = _uiParticle.parentScale.Inverse();
var scale3d = uiParticle.scale3DForCalc; var scale3d = _uiParticle.scale3DForCalc;
dstPos = dstPos.GetScaled(inverseScale, scale3d.Inverse()); dstPos = dstPos.GetScaled(inverseScale, scale3d.Inverse());
// Relative mode // Relative mode
if (uiParticle.positionMode == UIParticle.PositionMode.Relative) if (_uiParticle.positionMode == UIParticle.PositionMode.Relative)
{ {
var diff = uiParticle.transform.position - psPos; var diff = _uiParticle.transform.position - psPos;
diff.Scale(scale3d - inverseScale); diff.Scale(scale3d - inverseScale);
diff.Scale(scale3d.Inverse()); diff.Scale(scale3d.Inverse());
dstPos += diff; dstPos += diff;
@@ -274,59 +237,25 @@ namespace Coffee.UIExtensions
return Vector3.MoveTowards(current, target, speed); return Vector3.MoveTowards(current, target, speed);
} }
private void CollectUIParticlesIfNeeded() private void ApplyParticleSystem()
{ {
if (m_ParticleSystems.Count == 0 || _uiParticles.Count != 0) return; _uiParticle = null;
if (m_ParticleSystem == null)
// Expand capacity
if (_uiParticles.Capacity < m_ParticleSystems.Capacity)
{ {
_uiParticles.Capacity = m_ParticleSystems.Capacity;
}
// Find UIParticle that controls the ParticleSystem
for (var i = 0; i < m_ParticleSystems.Count; i++)
{
var ps = m_ParticleSystems[i];
if (ps == null)
{
_uiParticles.Add(null);
continue;
}
var uiParticle = ps.GetComponentInParent<UIParticle>(true);
_uiParticles.Add(uiParticle.particles.Contains(ps) ? uiParticle : null);
}
}
#if UNITY_EDITOR #if UNITY_EDITOR
private void OnValidate() if (Application.isPlaying)
{
_uiParticles.Clear();
}
#endif #endif
void ISerializationCallbackReceiver.OnBeforeSerialize()
{
UpgradeIfNeeded();
}
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
}
private void UpgradeIfNeeded()
{
// Multiple ParticleSystems support: from 'm_ParticleSystem' to 'm_ParticleSystems'
if (m_ParticleSystem != null)
{
if (!m_ParticleSystems.Contains(m_ParticleSystem))
{ {
m_ParticleSystems.Add(m_ParticleSystem); Debug.LogError("No particle system attached to particle attractor script", this);
} }
m_ParticleSystem = null; return;
Debug.Log($"Upgraded!"); }
_uiParticle = m_ParticleSystem.GetComponentInParent<UIParticle>(true);
if (_uiParticle && !_uiParticle.particles.Contains(m_ParticleSystem))
{
_uiParticle = null;
} }
} }
} }

View File

@@ -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:

View File

@@ -17,18 +17,6 @@ namespace Coffee.UIExtensions
set => instance.m_EnableLinearToGamma = value; set => instance.m_EnableLinearToGamma = value;
} }
[Header("Editor")]
[Tooltip("Hide the automatically generated objects.\n" +
" - UIParticleRenderer\n" +
" - UIParticle BakingCamera")]
[SerializeField]
private bool m_HideGeneratedObjects = true;
public static HideFlags globalHideFlags => instance.m_HideGeneratedObjects
? HideFlags.DontSave | HideFlags.NotEditable | HideFlags.HideInHierarchy | HideFlags.HideInInspector
: HideFlags.DontSave | HideFlags.NotEditable;
#if UNITY_EDITOR #if UNITY_EDITOR
[SettingsProvider] [SettingsProvider]
private static SettingsProvider CreateSettingsProvider() private static SettingsProvider CreateSettingsProvider()

View File

@@ -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:

View File

@@ -4,6 +4,7 @@
#elif UNITY_2022_3_OR_NEWER #elif UNITY_2022_3_OR_NEWER
#define PS_BAKE_API_V2 #define PS_BAKE_API_V2
#endif #endif
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Coffee.UIParticleInternal; using Coffee.UIParticleInternal;
@@ -24,10 +25,10 @@ namespace Coffee.UIExtensions
private static readonly CombineInstance[] s_CombineInstances = { new CombineInstance() }; private static readonly CombineInstance[] s_CombineInstances = { new CombineInstance() };
private static readonly List<Material> s_Materials = new List<Material>(2); private static readonly List<Material> s_Materials = new List<Material>(2);
private static MaterialPropertyBlock s_Mpb; private static MaterialPropertyBlock s_Mpb;
private static readonly List<UIParticleRenderer> s_Renderers = new List<UIParticleRenderer>(8);
private static readonly Vector3[] s_Corners = new Vector3[4]; private static readonly Vector3[] s_Corners = new Vector3[4];
private bool _delay; private bool _delay;
private int _index; private int _index;
private bool _isPrevStored;
private bool _isTrail; private bool _isTrail;
private Bounds _lastBounds; private Bounds _lastBounds;
private Material _materialForRendering; private Material _materialForRendering;
@@ -37,6 +38,7 @@ namespace Coffee.UIExtensions
private float _prevCanvasScale; private float _prevCanvasScale;
private Vector3 _prevPsPos; private Vector3 _prevPsPos;
private Vector3 _prevScale; private Vector3 _prevScale;
private bool _isPrevStored;
private Vector2Int _prevScreenSize; private Vector2Int _prevScreenSize;
private bool _preWarm; private bool _preWarm;
private ParticleSystemRenderer _renderer; private ParticleSystemRenderer _renderer;
@@ -136,7 +138,6 @@ namespace Coffee.UIExtensions
{ {
base.OnEnable(); base.OnEnable();
hideFlags = UIParticleProjectSettings.globalHideFlags;
if (!s_CombineInstances[0].mesh) if (!s_CombineInstances[0].mesh)
{ {
s_CombineInstances[0].mesh = new Mesh s_CombineInstances[0].mesh = new Mesh
@@ -161,7 +162,7 @@ namespace Coffee.UIExtensions
// Create renderer object. // Create renderer object.
var go = new GameObject("[generated] UIParticleRenderer", typeof(UIParticleRenderer)) var go = new GameObject("[generated] UIParticleRenderer", typeof(UIParticleRenderer))
{ {
hideFlags = UIParticleProjectSettings.globalHideFlags, hideFlags = HideFlags.HideAndDontSave,
layer = parent.gameObject.layer layer = parent.gameObject.layer
}; };
@@ -201,6 +202,7 @@ namespace Coffee.UIExtensions
return modifiedMaterial; return modifiedMaterial;
} }
//
var hash = new Hash128( var hash = new Hash128(
modifiedMaterial ? (uint)modifiedMaterial.GetInstanceID() : 0, modifiedMaterial ? (uint)modifiedMaterial.GetInstanceID() : 0,
texture ? (uint)texture.GetInstanceID() : 0, texture ? (uint)texture.GetInstanceID() : 0,
@@ -423,12 +425,13 @@ namespace Coffee.UIExtensions
var components = ListPool<Component>.Rent(); var components = ListPool<Component>.Rent();
GetComponents(typeof(IMeshModifier), components); GetComponents(typeof(IMeshModifier), components);
#pragma warning disable CS0618 // Type or member is obsolete
for (var i = 0; i < components.Count; i++) for (var i = 0; i < components.Count; i++)
{ {
#pragma warning disable CS0618 // Type or member is obsolete
((IMeshModifier)components[i]).ModifyMesh(workerMesh); ((IMeshModifier)components[i]).ModifyMesh(workerMesh);
#pragma warning restore CS0618 // Type or member is obsolete
} }
#pragma warning restore CS0618 // Type or member is obsolete
ListPool<Component>.Return(ref components); ListPool<Component>.Return(ref components);
} }
@@ -442,25 +445,22 @@ namespace Coffee.UIExtensions
// Get grouped renderers. // Get grouped renderers.
Profiler.BeginSample("[UIParticleRenderer] Set Mesh"); Profiler.BeginSample("[UIParticleRenderer] Set Mesh");
var renderers = ListPool<UIParticleRenderer>.Rent(); s_Renderers.Clear();
if (_parent.useMeshSharing) if (_parent.useMeshSharing)
{ {
UIParticleUpdater.GetGroupedRenderers(_parent.groupId, _index, renderers); UIParticleUpdater.GetGroupedRenderers(_parent.groupId, _index, s_Renderers);
} }
for (var i = 0; i < renderers.Count; i++) for (var i = 0; i < s_Renderers.Count; i++)
{ {
var r = renderers[i]; if (s_Renderers[i] == this) continue;
if (r == this) continue;
r.canvasRenderer.SetMesh(workerMesh); s_Renderers[i].canvasRenderer.SetMesh(workerMesh);
r._lastBounds = _lastBounds; s_Renderers[i]._lastBounds = _lastBounds;
r.canvasRenderer.materialCount = 1; s_Renderers[i].canvasRenderer.materialCount = 1;
r.canvasRenderer.SetMaterial(materialForRendering, 0); s_Renderers[i].canvasRenderer.SetMaterial(materialForRendering, 0);
} }
ListPool<UIParticleRenderer>.Return(ref renderers);
if (_parent.canRender) if (_parent.canRender)
{ {
canvasRenderer.SetMesh(workerMesh); canvasRenderer.SetMesh(workerMesh);
@@ -471,6 +471,8 @@ namespace Coffee.UIExtensions
} }
Profiler.EndSample(); Profiler.EndSample();
s_Renderers.Clear();
} }
public override void SetMaterialDirty() public override void SetMaterialDirty()
@@ -542,12 +544,6 @@ namespace Coffee.UIExtensions
return Matrix4x4.Translate(psPos) return Matrix4x4.Translate(psPos)
* Matrix4x4.Scale(scale); * Matrix4x4.Scale(scale);
case ParticleSystemSimulationSpace.World: case ParticleSystemSimulationSpace.World:
if (_isTrail)
{
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))

View File

@@ -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:

View File

@@ -1,4 +1,3 @@
using Coffee.UIParticleInternal;
using UnityEngine; using UnityEngine;
using UnityEngine.Serialization; using UnityEngine.Serialization;
using UnityEngine.UI; using UnityEngine.UI;
@@ -52,7 +51,7 @@ namespace Coffee.UIExtensions.Demo
public void EnableAnimations(bool flag) public void EnableAnimations(bool flag)
{ {
foreach (var animator in Misc.FindObjectsOfType<Animator>()) foreach (var animator in FindObjectsOfType<Animator>())
{ {
animator.enabled = flag; animator.enabled = flag;
} }
@@ -80,7 +79,7 @@ namespace Coffee.UIExtensions.Demo
public void UIParticle_Scale(float scale) public void UIParticle_Scale(float scale)
{ {
foreach (var uip in Misc.FindObjectsOfType<UIParticle>()) foreach (var uip in FindObjectsOfType<UIParticle>())
{ {
uip.scale = scale; uip.scale = scale;
} }
@@ -88,7 +87,7 @@ namespace Coffee.UIExtensions.Demo
public void ParticleSystem_WorldSpaseSimulation(bool flag) public void ParticleSystem_WorldSpaseSimulation(bool flag)
{ {
foreach (var p in Misc.FindObjectsOfType<ParticleSystem>()) foreach (var p in FindObjectsOfType<ParticleSystem>())
{ {
var main = p.main; var main = p.main;
main.simulationSpace = flag main.simulationSpace = flag
@@ -124,7 +123,7 @@ namespace Coffee.UIExtensions.Demo
public void ParticleSystem_SetScale(float scale) public void ParticleSystem_SetScale(float scale)
{ {
foreach (var ps in Misc.FindObjectsOfType<ParticleSystem>()) foreach (var ps in FindObjectsOfType<ParticleSystem>())
{ {
ps.transform.localScale = new Vector3(scale, scale, scale); ps.transform.localScale = new Vector3(scale, scale, scale);
} }

View File

@@ -42,12 +42,12 @@ TextureImporter:
compressionQuality: 50 compressionQuality: 50
spriteMode: 1 spriteMode: 1
spriteExtrude: 1 spriteExtrude: 1
spriteMeshType: 0 spriteMeshType: 1
alignment: 0 alignment: 0
spritePivot: {x: 0.5, y: 0.5} spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100 spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0} spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 0 spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1 alphaUsage: 1
alphaIsTransparency: 1 alphaIsTransparency: 1
spriteTessellationDetail: -1 spriteTessellationDetail: -1

View File

@@ -27,7 +27,7 @@ TextureImporter:
generateCubemap: 6 generateCubemap: 6
cubemapConvolution: 0 cubemapConvolution: 0
seamlessCubemap: 0 seamlessCubemap: 0
textureFormat: 4 textureFormat: 1
maxTextureSize: 2048 maxTextureSize: 2048
textureSettings: textureSettings:
serializedVersion: 2 serializedVersion: 2
@@ -42,12 +42,12 @@ TextureImporter:
compressionQuality: 50 compressionQuality: 50
spriteMode: 1 spriteMode: 1
spriteExtrude: 1 spriteExtrude: 1
spriteMeshType: 0 spriteMeshType: 1
alignment: 0 alignment: 0
spritePivot: {x: 0.5, y: 0.5} spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100 spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0} spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 0 spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1 alphaUsage: 1
alphaIsTransparency: 1 alphaIsTransparency: 1
spriteTessellationDetail: -1 spriteTessellationDetail: -1

View File

@@ -42,12 +42,12 @@ TextureImporter:
compressionQuality: 50 compressionQuality: 50
spriteMode: 1 spriteMode: 1
spriteExtrude: 1 spriteExtrude: 1
spriteMeshType: 0 spriteMeshType: 1
alignment: 0 alignment: 0
spritePivot: {x: 0.5, y: 0.5} spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100 spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0} spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 0 spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 0 alphaUsage: 0
alphaIsTransparency: 1 alphaIsTransparency: 1
spriteTessellationDetail: -1 spriteTessellationDetail: -1

View File

@@ -27,7 +27,7 @@ TextureImporter:
generateCubemap: 6 generateCubemap: 6
cubemapConvolution: 0 cubemapConvolution: 0
seamlessCubemap: 0 seamlessCubemap: 0
textureFormat: 4 textureFormat: -1
maxTextureSize: 2048 maxTextureSize: 2048
textureSettings: textureSettings:
serializedVersion: 2 serializedVersion: 2
@@ -42,12 +42,12 @@ TextureImporter:
compressionQuality: 50 compressionQuality: 50
spriteMode: 1 spriteMode: 1
spriteExtrude: 1 spriteExtrude: 1
spriteMeshType: 0 spriteMeshType: 1
alignment: 0 alignment: 0
spritePivot: {x: 0.5, y: 0.5} spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100 spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0} spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 0 spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1 alphaUsage: 1
alphaIsTransparency: 1 alphaIsTransparency: 1
spriteTessellationDetail: -1 spriteTessellationDetail: -1

View File

@@ -40,10 +40,7 @@
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]
@@ -64,21 +61,21 @@
struct appdata_t struct appdata_t
{ {
float4 vertex : POSITION; float4 vertex : POSITION;
float4 color : COLOR; float4 color : COLOR;
float2 texcoord : TEXCOORD0; float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID UNITY_VERTEX_INPUT_INSTANCE_ID
}; };
struct v2f struct v2f
{ {
float4 vertex : SV_POSITION; float4 vertex : SV_POSITION;
fixed4 color : COLOR; fixed4 color : COLOR;
float2 texcoord : TEXCOORD0; float2 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1; float4 worldPosition : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO UNITY_VERTEX_OUTPUT_STEREO
}; };
fixed4 _Color; fixed4 _Color;
sampler2D _MainTex; sampler2D _MainTex;
float4 _MainTex_ST; float4 _MainTex_ST;
@@ -117,4 +114,4 @@
ENDCG ENDCG
} }
} }
} }

View File

@@ -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.3", "version": "5.0.0-preview.2",
"unity": "2018.2", "unity": "2018.2",
"license": "MIT", "license": "MIT",
"repository": { "repository": {