You've already forked ParticleEffectForUGUI
mirror of
https://github.com/mob-sakai/ParticleEffectForUGUI.git
synced 2026-05-14 20:20:06 +00:00
Compare commits
8 Commits
4.12.0
...
5.0.0-prev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23dd7b5987 | ||
|
|
f476898b46 | ||
|
|
4e91b6f69b | ||
|
|
1bf669e09f | ||
|
|
54be6bf588 | ||
|
|
72f5f9441b | ||
|
|
acebfb27f6 | ||
|
|
a3e725fd5a |
1
.coffee.internal.sed
Normal file
1
.coffee.internal.sed
Normal file
@@ -0,0 +1 @@
|
||||
s/Coffee.Internal/Coffee.UIParticleInternal/g
|
||||
@@ -4,7 +4,7 @@
|
||||
"release-4.x",
|
||||
{
|
||||
"name": "release-preview",
|
||||
"prerelease": true
|
||||
"prerelease": "preview"
|
||||
}
|
||||
],
|
||||
"plugins": [
|
||||
|
||||
14
CHANGELOG.md
14
CHANGELOG.md
@@ -1,3 +1,17 @@
|
||||
# [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](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)
|
||||
* UIParticle no longer inherits from MaskableGraphic ([b6d921b](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/b6d921b3e47f749b5028d22b0e89b6eb3a1af7de))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* Some members inherited from MaskableGraphic will no longer be available.
|
||||
|
||||
## [4.6.6](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.5...v4.6.6) (2024-05-23)
|
||||
|
||||
|
||||
|
||||
8
Editor/Internal.meta
Normal file
8
Editor/Internal.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e440931f761e4e888510a4e6045287a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -2,20 +2,22 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UI;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using Coffee.UIParticleExtensions;
|
||||
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
using UnityEditor.Overlays;
|
||||
#else
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Coffee.UIParticleInternal;
|
||||
using Object = UnityEngine.Object;
|
||||
#endif
|
||||
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
using UnityEditor.SceneManagement;
|
||||
|
||||
#elif UNITY_2018_3_OR_NEWER
|
||||
using UnityEditor.Experimental.SceneManagement;
|
||||
#endif
|
||||
@@ -24,7 +26,7 @@ namespace Coffee.UIExtensions
|
||||
{
|
||||
[CustomEditor(typeof(UIParticle))]
|
||||
[CanEditMultipleObjects]
|
||||
internal class UIParticleEditor : GraphicEditor
|
||||
internal class UIParticleEditor : Editor
|
||||
{
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
@@ -144,10 +146,8 @@ namespace Coffee.UIExtensions
|
||||
/// <summary>
|
||||
/// This function is called when the object becomes enabled and active.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
private void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
_maskable = serializedObject.FindProperty("m_Maskable");
|
||||
_scale3D = serializedObject.FindProperty("m_Scale3D");
|
||||
_animatableProperties = serializedObject.FindProperty("m_AnimatableProperties");
|
||||
@@ -527,9 +527,7 @@ namespace Coffee.UIExtensions
|
||||
{
|
||||
if (!p || (ignoreCurrent && target == p)) return;
|
||||
|
||||
var cr = p.canvasRenderer;
|
||||
DestroyImmediate(p);
|
||||
DestroyImmediate(cr);
|
||||
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
var stage = PrefabStageUtility.GetCurrentPrefabStage();
|
||||
|
||||
15
README.md
15
README.md
@@ -1,4 +1,4 @@
|
||||
# Particle Effect For UGUI (UI Particle)
|
||||
# <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.
|
||||
@@ -117,6 +117,16 @@ Or, use [UpmGitExtension](https://github.com/mob-sakai/UpmGitExtension) to insta
|
||||
|
||||
<br><br>
|
||||
|
||||
## ⚙ 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>
|
||||
|
||||
## 🚀 Usage
|
||||
|
||||
### UIParticle Component
|
||||
@@ -176,9 +186,10 @@ section.
|
||||
### Script usage
|
||||
|
||||
```cs
|
||||
// Instant ParticleSystem prefab with UIParticle on runtime.
|
||||
// Instantiate ParticleSystem prefab with UIParticle on runtime.
|
||||
var go = GameObject.Instantiate(prefab);
|
||||
var uiParticle = go.AddComponent<UIParticle>();
|
||||
uiParticle.scale = 100;
|
||||
|
||||
// Control by ParticleSystem.
|
||||
particleSystem.Play();
|
||||
|
||||
8
Runtime/Internal.meta
Normal file
8
Runtime/Internal.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53aa3f36032944b3fb1455e774c52396
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Runtime/Internal/Extensions.meta
Normal file
8
Runtime/Internal/Extensions.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8cf8018dee45a4c42a19eec890eaa5b1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
134
Runtime/Internal/Extensions/CanvasExtensions.cs
Normal file
134
Runtime/Internal/Extensions/CanvasExtensions.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
#if UNITY_2021_3_0 || UNITY_2021_3_1 || UNITY_2021_3_2 || UNITY_2021_3_3 || UNITY_2021_3_4 || UNITY_2021_3_5 || UNITY_2021_3_6 || UNITY_2021_3_7 || UNITY_2021_3_8 || UNITY_2021_3_9
|
||||
#elif UNITY_2021_3_10 || UNITY_2021_3_11 || UNITY_2021_3_12 || UNITY_2021_3_13 || UNITY_2021_3_14 || UNITY_2021_3_15 || UNITY_2021_3_16 || UNITY_2021_3_17 || UNITY_2021_3_18 || UNITY_2021_3_19
|
||||
#elif UNITY_2021_3_20 || UNITY_2021_3_21 || UNITY_2021_3_22 || UNITY_2021_3_23 || UNITY_2021_3_24 || UNITY_2021_3_25 || UNITY_2021_3_26 || UNITY_2021_3_27 || UNITY_2021_3_28 || UNITY_2021_3_29
|
||||
#elif UNITY_2021_3_30 || UNITY_2021_3_31 || UNITY_2021_3_32 || UNITY_2021_3_33
|
||||
#elif UNITY_2022_2_0 || UNITY_2022_2_1 || UNITY_2022_2_2 || UNITY_2022_2_3 || UNITY_2022_2_4 || UNITY_2022_2_5 || UNITY_2022_2_6 || UNITY_2022_2_7 || UNITY_2022_2_8 || UNITY_2022_2_9
|
||||
#elif UNITY_2022_2_10 || UNITY_2022_2_11 || UNITY_2022_2_12 || UNITY_2022_2_13 || UNITY_2022_2_14
|
||||
#elif UNITY_2021_3 || UNITY_2022_2 || UNITY_2022_3 || UNITY_2023_2_OR_NEWER
|
||||
#define CANVAS_SUPPORT_ALWAYS_GAMMA
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
#if UNITY_MODULE_VR
|
||||
using UnityEngine.XR;
|
||||
#endif
|
||||
|
||||
namespace Coffee.UIParticleInternal
|
||||
{
|
||||
internal static class CanvasExtensions
|
||||
{
|
||||
public static bool ShouldGammaToLinearInShader(this Canvas canvas)
|
||||
{
|
||||
return QualitySettings.activeColorSpace == ColorSpace.Linear &&
|
||||
#if CANVAS_SUPPORT_ALWAYS_GAMMA
|
||||
canvas.vertexColorAlwaysGammaSpace;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static bool ShouldGammaToLinearInMesh(this Canvas canvas)
|
||||
{
|
||||
return QualitySettings.activeColorSpace == ColorSpace.Linear &&
|
||||
#if CANVAS_SUPPORT_ALWAYS_GAMMA
|
||||
!canvas.vertexColorAlwaysGammaSpace;
|
||||
#else
|
||||
true;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static bool IsStereoCanvas(this Canvas canvas)
|
||||
{
|
||||
#if UNITY_MODULE_VR
|
||||
if (FrameCache.TryGet<bool>(canvas, nameof(IsStereoCanvas), out var stereo)) return stereo;
|
||||
|
||||
stereo =
|
||||
canvas != null && canvas.renderMode != RenderMode.ScreenSpaceOverlay && canvas.worldCamera != null
|
||||
&& XRSettings.enabled && !string.IsNullOrEmpty(XRSettings.loadedDeviceName);
|
||||
FrameCache.Set(canvas, nameof(IsStereoCanvas), stereo);
|
||||
return stereo;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the view-projection matrix for a Canvas.
|
||||
/// </summary>
|
||||
public static void GetViewProjectionMatrix(this Canvas canvas, out Matrix4x4 vpMatrix)
|
||||
{
|
||||
canvas.GetViewProjectionMatrix(Camera.MonoOrStereoscopicEye.Mono, out vpMatrix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the view-projection matrix for a Canvas.
|
||||
/// </summary>
|
||||
public static void GetViewProjectionMatrix(this Canvas canvas, Camera.MonoOrStereoscopicEye eye,
|
||||
out Matrix4x4 vpMatrix)
|
||||
{
|
||||
if (FrameCache.TryGet(canvas, nameof(GetViewProjectionMatrix), out vpMatrix)) return;
|
||||
|
||||
canvas.GetViewProjectionMatrix(eye, out var viewMatrix, out var projectionMatrix);
|
||||
vpMatrix = viewMatrix * projectionMatrix;
|
||||
FrameCache.Set(canvas, nameof(GetViewProjectionMatrix), vpMatrix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the view and projection matrices for a Canvas.
|
||||
/// </summary>
|
||||
public static void GetViewProjectionMatrix(this Canvas canvas, out Matrix4x4 vMatrix, out Matrix4x4 pMatrix)
|
||||
{
|
||||
canvas.GetViewProjectionMatrix(Camera.MonoOrStereoscopicEye.Mono, out vMatrix, out pMatrix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the view and projection matrices for a Canvas.
|
||||
/// </summary>
|
||||
public static void GetViewProjectionMatrix(this Canvas canvas, Camera.MonoOrStereoscopicEye eye,
|
||||
out Matrix4x4 vMatrix, out Matrix4x4 pMatrix)
|
||||
{
|
||||
if (FrameCache.TryGet(canvas, "GetViewMatrix", (int)eye, out vMatrix) &&
|
||||
FrameCache.TryGet(canvas, "GetProjectionMatrix", (int)eye, out pMatrix))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get view and projection matrices.
|
||||
Profiler.BeginSample("(COF)[CanvasExt] GetViewProjectionMatrix");
|
||||
var rootCanvas = canvas.rootCanvas;
|
||||
var cam = rootCanvas.worldCamera;
|
||||
if (rootCanvas && rootCanvas.renderMode != RenderMode.ScreenSpaceOverlay && cam)
|
||||
{
|
||||
if (eye == Camera.MonoOrStereoscopicEye.Mono)
|
||||
{
|
||||
vMatrix = cam.worldToCameraMatrix;
|
||||
pMatrix = GL.GetGPUProjectionMatrix(cam.projectionMatrix, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
pMatrix = cam.GetStereoProjectionMatrix((Camera.StereoscopicEye)eye);
|
||||
vMatrix = cam.GetStereoViewMatrix((Camera.StereoscopicEye)eye);
|
||||
pMatrix = GL.GetGPUProjectionMatrix(pMatrix, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var pos = rootCanvas.transform.position;
|
||||
vMatrix = Matrix4x4.TRS(
|
||||
new Vector3(-pos.x, -pos.y, -1000),
|
||||
Quaternion.identity,
|
||||
new Vector3(1, 1, -1f));
|
||||
pMatrix = Matrix4x4.TRS(
|
||||
new Vector3(0, 0, -1),
|
||||
Quaternion.identity,
|
||||
new Vector3(1 / pos.x, 1 / pos.y, -2 / 10000f));
|
||||
}
|
||||
|
||||
FrameCache.Set(canvas, "GetViewMatrix", (int)eye, vMatrix);
|
||||
FrameCache.Set(canvas, "GetProjectionMatrix", (int)eye, pMatrix);
|
||||
|
||||
Profiler.EndSample();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0beae5bb1cb142b9ab90dc0d371f026
|
||||
guid: 9dd767b8c0f95478386e7d5079cd44df
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
77
Runtime/Internal/Extensions/Color32Extensions.cs
Normal file
77
Runtime/Internal/Extensions/Color32Extensions.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
namespace Coffee.UIParticleInternal
|
||||
{
|
||||
internal static class Color32Extensions
|
||||
{
|
||||
private static readonly List<Color32> s_Colors = new List<Color32>();
|
||||
private static byte[] s_LinearToGammaLut;
|
||||
private static byte[] s_GammaToLinearLut;
|
||||
|
||||
public static byte LinearToGamma(this byte self)
|
||||
{
|
||||
if (s_LinearToGammaLut == null)
|
||||
{
|
||||
s_LinearToGammaLut = new byte[256];
|
||||
for (var i = 0; i < 256; i++)
|
||||
{
|
||||
s_LinearToGammaLut[i] = (byte)(Mathf.LinearToGammaSpace(i / 255f) * 255f);
|
||||
}
|
||||
}
|
||||
|
||||
return s_LinearToGammaLut[self];
|
||||
}
|
||||
|
||||
public static byte GammaToLinear(this byte self)
|
||||
{
|
||||
if (s_GammaToLinearLut == null)
|
||||
{
|
||||
s_GammaToLinearLut = new byte[256];
|
||||
for (var i = 0; i < 256; i++)
|
||||
{
|
||||
s_GammaToLinearLut[i] = (byte)(Mathf.GammaToLinearSpace(i / 255f) * 255f);
|
||||
}
|
||||
}
|
||||
|
||||
return s_GammaToLinearLut[self];
|
||||
}
|
||||
|
||||
public static void LinearToGamma(this Mesh self)
|
||||
{
|
||||
Profiler.BeginSample("(COF)[ColorExt] LinearToGamma (Mesh)");
|
||||
self.GetColors(s_Colors);
|
||||
var count = s_Colors.Count;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var c = s_Colors[i];
|
||||
c.r = c.r.LinearToGamma();
|
||||
c.g = c.g.LinearToGamma();
|
||||
c.b = c.b.LinearToGamma();
|
||||
s_Colors[i] = c;
|
||||
}
|
||||
|
||||
self.SetColors(s_Colors);
|
||||
Profiler.EndSample();
|
||||
}
|
||||
|
||||
public static void GammaToLinear(this Mesh self)
|
||||
{
|
||||
Profiler.BeginSample("(COF)[ColorExt] GammaToLinear (Mesh)");
|
||||
self.GetColors(s_Colors);
|
||||
var count = s_Colors.Count;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var c = s_Colors[i];
|
||||
c.r = c.r.GammaToLinear();
|
||||
c.g = c.g.GammaToLinear();
|
||||
c.b = c.b.GammaToLinear();
|
||||
s_Colors[i] = c;
|
||||
}
|
||||
|
||||
self.SetColors(s_Colors);
|
||||
Profiler.EndSample();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d188d31b140094ebc84a9caafbc7ac71
|
||||
guid: 0ef431b9df32c410ea5fa46be81def6b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
155
Runtime/Internal/Extensions/ComponentExtensions.cs
Normal file
155
Runtime/Internal/Extensions/ComponentExtensions.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Coffee.UIParticleInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for Component class.
|
||||
/// </summary>
|
||||
internal static class ComponentExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get or add a component of a specific type to a GameObject.
|
||||
/// </summary>
|
||||
public static T GetOrAddComponent<T>(this Component self) where T : Component
|
||||
{
|
||||
if (!self) return null;
|
||||
return self.TryGetComponent<T>(out var component)
|
||||
? component
|
||||
: self.gameObject.AddComponent<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the root component of a specific type in the hierarchy of a GameObject.
|
||||
/// </summary>
|
||||
public static T GetRootComponent<T>(this Component self) where T : Component
|
||||
{
|
||||
T component = null;
|
||||
var transform = self.transform;
|
||||
while (transform)
|
||||
{
|
||||
if (transform.TryGetComponent<T>(out var c))
|
||||
{
|
||||
component = c;
|
||||
}
|
||||
|
||||
transform = transform.parent;
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a component of a specific type in the parent hierarchy of a GameObject.
|
||||
/// </summary>
|
||||
public static T GetComponentInParent<T>(this Component self, bool includeSelf, Transform stopAfter,
|
||||
Predicate<T> valid)
|
||||
where T : Component
|
||||
{
|
||||
var tr = includeSelf ? self.transform : self.transform.parent;
|
||||
while (tr)
|
||||
{
|
||||
if (tr.TryGetComponent<T>(out var c) && valid(c)) return c;
|
||||
if (tr == stopAfter) return null;
|
||||
tr = tr.parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a component of a specific type to the children of a GameObject.
|
||||
/// </summary>
|
||||
public static void AddComponentOnChildren<T>(this Component self, HideFlags hideFlags, bool includeSelf)
|
||||
where T : Component
|
||||
{
|
||||
if (self == null) return;
|
||||
|
||||
Profiler.BeginSample("(COF)[ComponentExt] AddComponentOnChildren > Self");
|
||||
if (includeSelf && !self.TryGetComponent<T>(out _))
|
||||
{
|
||||
var c = self.gameObject.AddComponent<T>();
|
||||
c.hideFlags = hideFlags;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
var c = child.gameObject.AddComponent<T>();
|
||||
c.hideFlags = hideFlags;
|
||||
}
|
||||
|
||||
Profiler.EndSample();
|
||||
}
|
||||
|
||||
#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
|
||||
{
|
||||
if (!self) return null;
|
||||
if (!includeInactive) return self.GetComponentInParent<T>();
|
||||
|
||||
var current = self.transform;
|
||||
while (current)
|
||||
{
|
||||
if (current.TryGetComponent<T>(out var c)) return c;
|
||||
current = current.parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// Verify whether it can be converted to the specified component.
|
||||
/// </summary>
|
||||
internal static bool CanConvertTo<T>(this Object context) where T : MonoBehaviour
|
||||
{
|
||||
return context && context.GetType() != typeof(T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert to the specified component.
|
||||
/// </summary>
|
||||
internal static void ConvertTo<T>(this Object context) where T : MonoBehaviour
|
||||
{
|
||||
var target = context as MonoBehaviour;
|
||||
if (target == null) return;
|
||||
|
||||
var so = new SerializedObject(target);
|
||||
so.Update();
|
||||
|
||||
var oldEnable = target.enabled;
|
||||
target.enabled = false;
|
||||
|
||||
// Find MonoScript of the specified component.
|
||||
foreach (var script in Resources.FindObjectsOfTypeAll<MonoScript>())
|
||||
{
|
||||
if (script.GetClass() != typeof(T))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set 'm_Script' to convert.
|
||||
so.FindProperty("m_Script").objectReferenceValue = script;
|
||||
so.ApplyModifiedProperties();
|
||||
break;
|
||||
}
|
||||
|
||||
if (so.targetObject is MonoBehaviour mb)
|
||||
{
|
||||
mb.enabled = oldEnable;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
11
Runtime/Internal/Extensions/ComponentExtensions.cs.meta
Normal file
11
Runtime/Internal/Extensions/ComponentExtensions.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8455ee485a5ee4cacbdf558f66af65fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
19
Runtime/Internal/Extensions/ListExtensions.cs
Normal file
19
Runtime/Internal/Extensions/ListExtensions.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Internal/Extensions/ListExtensions.cs.meta
Normal file
11
Runtime/Internal/Extensions/ListExtensions.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 833553390099d40c9b212823f0852c46
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
48
Runtime/Internal/Extensions/Misc.cs
Normal file
48
Runtime/Internal/Extensions/Misc.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System.Diagnostics;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Coffee.UIParticleInternal
|
||||
{
|
||||
internal static class Misc
|
||||
{
|
||||
public static void Destroy(Object obj)
|
||||
{
|
||||
if (!obj) return;
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
Object.DestroyImmediate(obj);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
Object.Destroy(obj);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DestroyImmediate(Object obj)
|
||||
{
|
||||
if (!obj) return;
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isEditor)
|
||||
{
|
||||
Object.DestroyImmediate(obj);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
Object.Destroy(obj);
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("UNITY_EDITOR")]
|
||||
public static void SetDirty(Object obj)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!obj) return;
|
||||
EditorUtility.SetDirty(obj);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Internal/Extensions/Misc.cs.meta
Normal file
11
Runtime/Internal/Extensions/Misc.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39ed6a6b0a72e482488bd298b2ae762e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
58
Runtime/Internal/Extensions/SpriteExtensions.cs
Normal file
58
Runtime/Internal/Extensions/SpriteExtensions.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.U2D;
|
||||
#if UNITY_EDITOR
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
||||
namespace Coffee.UIParticleInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for Sprite class.
|
||||
/// </summary>
|
||||
internal static class SpriteExtensions
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
private static readonly Type s_SpriteEditorExtensionType =
|
||||
Type.GetType("UnityEditor.Experimental.U2D.SpriteEditorExtension, UnityEditor")
|
||||
?? Type.GetType("UnityEditor.U2D.SpriteEditorExtension, UnityEditor");
|
||||
|
||||
private static readonly MethodInfo s_GetActiveAtlasTextureMethod = s_SpriteEditorExtensionType
|
||||
.GetMethod("GetActiveAtlasTexture", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
|
||||
private static readonly MethodInfo s_GetActiveAtlasMethod = s_SpriteEditorExtensionType
|
||||
.GetMethod("GetActiveAtlas", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
|
||||
/// <summary>
|
||||
/// Get the actual texture of a sprite in play mode or edit mode.
|
||||
/// </summary>
|
||||
public static Texture2D GetActualTexture(this Sprite self)
|
||||
{
|
||||
if (!self) return null;
|
||||
|
||||
if (Application.isPlaying) return self.texture;
|
||||
|
||||
var ret = s_GetActiveAtlasTextureMethod.Invoke(null, new object[] { self }) as Texture2D;
|
||||
return ret ? ret : self.texture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the active sprite atlas of a sprite in play mode or edit mode.
|
||||
/// </summary>
|
||||
public static SpriteAtlas GetActiveAtlas(this Sprite self)
|
||||
{
|
||||
if (!self) return null;
|
||||
|
||||
return s_GetActiveAtlasMethod.Invoke(null, new object[] { self }) as SpriteAtlas;
|
||||
}
|
||||
#else
|
||||
/// <summary>
|
||||
/// Get the actual texture of a sprite in play mode.
|
||||
/// </summary>
|
||||
internal static Texture2D GetActualTexture(this Sprite self)
|
||||
{
|
||||
return self ? self.texture : null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
11
Runtime/Internal/Extensions/SpriteExtensions.cs.meta
Normal file
11
Runtime/Internal/Extensions/SpriteExtensions.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7a2e11131111447cb7fc0394a14da65
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
46
Runtime/Internal/Extensions/Vector3Extensions.cs
Normal file
46
Runtime/Internal/Extensions/Vector3Extensions.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Coffee.UIParticleInternal
|
||||
{
|
||||
internal static class Vector3Extensions
|
||||
{
|
||||
public static Vector3 Inverse(this Vector3 self)
|
||||
{
|
||||
self.x = Mathf.Approximately(self.x, 0) ? 1 : 1 / self.x;
|
||||
self.y = Mathf.Approximately(self.y, 0) ? 1 : 1 / self.y;
|
||||
self.z = Mathf.Approximately(self.z, 0) ? 1 : 1 / self.z;
|
||||
return self;
|
||||
}
|
||||
|
||||
public static Vector3 GetScaled(this Vector3 self, Vector3 other1)
|
||||
{
|
||||
self.Scale(other1);
|
||||
return self;
|
||||
}
|
||||
|
||||
public static Vector3 GetScaled(this Vector3 self, Vector3 other1, Vector3 other2)
|
||||
{
|
||||
self.Scale(other1);
|
||||
self.Scale(other2);
|
||||
return self;
|
||||
}
|
||||
|
||||
public static Vector3 GetScaled(this Vector3 self, Vector3 other1, Vector3 other2, Vector3 other3)
|
||||
{
|
||||
self.Scale(other1);
|
||||
self.Scale(other2);
|
||||
self.Scale(other3);
|
||||
return self;
|
||||
}
|
||||
|
||||
public static bool IsVisible(this Vector3 self)
|
||||
{
|
||||
return 0 < Mathf.Abs(self.x * self.y * self.z);
|
||||
}
|
||||
|
||||
public static bool IsVisible2D(this Vector3 self)
|
||||
{
|
||||
return 0 < Mathf.Abs(self.x * self.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Internal/Extensions/Vector3Extensions.cs.meta
Normal file
11
Runtime/Internal/Extensions/Vector3Extensions.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a7b5fb989e4b48c8bc7ecce834060f5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Runtime/Internal/ProjectSettings.meta
Normal file
8
Runtime/Internal/ProjectSettings.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 398e06c9985ad4291a95f0749c2927fb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
217
Runtime/Internal/ProjectSettings/PreloadedProjectSettings.cs
Normal file
217
Runtime/Internal/ProjectSettings/PreloadedProjectSettings.cs
Normal file
@@ -0,0 +1,217 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build;
|
||||
using UnityEditor.Build.Reporting;
|
||||
#endif
|
||||
|
||||
namespace Coffee.UIParticleInternal
|
||||
{
|
||||
public abstract class PreloadedProjectSettings : ScriptableObject
|
||||
#if UNITY_EDITOR
|
||||
, IPreprocessBuildWithReport
|
||||
{
|
||||
int IOrderedCallback.callbackOrder => 0;
|
||||
|
||||
void IPreprocessBuildWithReport.OnPreprocessBuild(BuildReport report)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
[InitializeOnLoadMethod]
|
||||
[InitializeOnEnterPlayMode]
|
||||
private static void Initialize()
|
||||
{
|
||||
const BindingFlags flags = BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy;
|
||||
foreach (var t in TypeCache.GetTypesDerivedFrom(typeof(PreloadedProjectSettings<>)))
|
||||
{
|
||||
var defaultSettings = GetDefaultSettings(t);
|
||||
if (!defaultSettings)
|
||||
{
|
||||
defaultSettings = t.GetProperty("instance", flags)
|
||||
?.GetValue(null, null) as PreloadedProjectSettings;
|
||||
SetDefaultSettings(defaultSettings);
|
||||
}
|
||||
else if (GetPreloadedSettings(t).Length != 1)
|
||||
{
|
||||
SetDefaultSettings(defaultSettings);
|
||||
}
|
||||
}
|
||||
|
||||
EditorApplication.QueuePlayerLoopUpdate();
|
||||
}
|
||||
|
||||
protected static string GetDefaultName(Type type, bool nicify)
|
||||
{
|
||||
var typeName = type.Name.Replace("ProjectSettings", "");
|
||||
return nicify
|
||||
? ObjectNames.NicifyVariableName(typeName)
|
||||
: typeName;
|
||||
}
|
||||
|
||||
private static Object[] GetPreloadedSettings(Type type)
|
||||
{
|
||||
return PlayerSettings.GetPreloadedAssets()
|
||||
.Where(x => x && x.GetType() == type)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
protected static PreloadedProjectSettings GetDefaultSettings(Type type)
|
||||
{
|
||||
return GetPreloadedSettings(type).FirstOrDefault() as PreloadedProjectSettings
|
||||
?? AssetDatabase.FindAssets($"t:{nameof(PreloadedProjectSettings)}")
|
||||
.Select(AssetDatabase.GUIDToAssetPath)
|
||||
.Select(AssetDatabase.LoadAssetAtPath<PreloadedProjectSettings>)
|
||||
.FirstOrDefault(x => x && x.GetType() == type);
|
||||
}
|
||||
|
||||
protected static void SetDefaultSettings(PreloadedProjectSettings asset)
|
||||
{
|
||||
var type = asset.GetType();
|
||||
if (string.IsNullOrEmpty(AssetDatabase.GetAssetPath(asset)))
|
||||
{
|
||||
if (!AssetDatabase.IsValidFolder("Assets/ProjectSettings"))
|
||||
{
|
||||
AssetDatabase.CreateFolder("Assets", "ProjectSettings");
|
||||
}
|
||||
|
||||
var assetPath = $"Assets/ProjectSettings/{GetDefaultName(type, false)}.asset";
|
||||
assetPath = AssetDatabase.GenerateUniqueAssetPath(assetPath);
|
||||
AssetDatabase.CreateAsset(asset, assetPath);
|
||||
}
|
||||
|
||||
var preloadedAssets = PlayerSettings.GetPreloadedAssets();
|
||||
var projectSettings = GetPreloadedSettings(type);
|
||||
PlayerSettings.SetPreloadedAssets(preloadedAssets
|
||||
.Where(x => x)
|
||||
.Except(projectSettings.Except(new[] { asset }))
|
||||
.Append(asset)
|
||||
.Distinct()
|
||||
.ToArray());
|
||||
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
public abstract class PreloadedProjectSettings<T> : PreloadedProjectSettings
|
||||
where T : PreloadedProjectSettings<T>
|
||||
{
|
||||
private static T s_Instance;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private string _jsonText;
|
||||
|
||||
public static T instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_Instance) return s_Instance;
|
||||
|
||||
s_Instance = GetDefaultSettings(typeof(T)) as T;
|
||||
if (s_Instance) return s_Instance;
|
||||
|
||||
s_Instance = CreateInstance<T>();
|
||||
if (!s_Instance)
|
||||
{
|
||||
s_Instance = null;
|
||||
return s_Instance;
|
||||
}
|
||||
|
||||
SetDefaultSettings(s_Instance);
|
||||
return s_Instance;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPlayModeStateChanged(PlayModeStateChange state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case PlayModeStateChange.ExitingEditMode:
|
||||
_jsonText = EditorJsonUtility.ToJson(this);
|
||||
break;
|
||||
case PlayModeStateChange.ExitingPlayMode:
|
||||
if (_jsonText != null)
|
||||
{
|
||||
EditorJsonUtility.FromJsonOverwrite(_jsonText, this);
|
||||
_jsonText = null;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
public static T instance => s_Instance ? s_Instance : s_Instance = CreateInstance<T>();
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// This function is called when the object becomes enabled and active.
|
||||
/// </summary>
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
var isDefaultSettings = !s_Instance || s_Instance == this || GetDefaultSettings(typeof(T)) == this;
|
||||
if (!isDefaultSettings)
|
||||
{
|
||||
DestroyImmediate(this, true);
|
||||
return;
|
||||
}
|
||||
|
||||
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||
#endif
|
||||
|
||||
if (s_Instance) return;
|
||||
s_Instance = this as T;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function is called when the behaviour becomes disabled.
|
||||
/// </summary>
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
||||
#endif
|
||||
if (s_Instance != this) return;
|
||||
|
||||
s_Instance = null;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected sealed class PreloadedProjectSettingsProvider : SettingsProvider
|
||||
{
|
||||
private Editor _editor;
|
||||
private PreloadedProjectSettings<T> _target;
|
||||
|
||||
public PreloadedProjectSettingsProvider(string path) : base(path, SettingsScope.Project)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnGUI(string searchContext)
|
||||
{
|
||||
if (!_target)
|
||||
{
|
||||
if (_editor)
|
||||
{
|
||||
DestroyImmediate(_editor);
|
||||
_editor = null;
|
||||
}
|
||||
|
||||
_target = instance;
|
||||
_editor = Editor.CreateEditor(_target);
|
||||
}
|
||||
|
||||
_editor.OnInspectorGUI();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 790ea008741dc411497c8794745319eb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Runtime/Internal/Utilities.meta
Normal file
8
Runtime/Internal/Utilities.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b2877595f27c4a70a426991d515434f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
81
Runtime/Internal/Utilities/FastAction.cs
Executable file
81
Runtime/Internal/Utilities/FastAction.cs
Executable file
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
namespace Coffee.UIParticleInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for a fast action.
|
||||
/// </summary>
|
||||
internal class FastActionBase<T>
|
||||
{
|
||||
private static readonly ObjectPool<LinkedListNode<T>> s_NodePool =
|
||||
new ObjectPool<LinkedListNode<T>>(() => new LinkedListNode<T>(default), _ => true, x => x.Value = default);
|
||||
|
||||
private readonly LinkedList<T> _delegates = new LinkedList<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a delegate to the action.
|
||||
/// </summary>
|
||||
public void Add(T rhs)
|
||||
{
|
||||
Profiler.BeginSample("(COF)[FastAction] Add Action");
|
||||
var node = s_NodePool.Rent();
|
||||
node.Value = rhs;
|
||||
_delegates.AddLast(node);
|
||||
Profiler.EndSample();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a delegate from the action.
|
||||
/// </summary>
|
||||
public void Remove(T rhs)
|
||||
{
|
||||
Profiler.BeginSample("(COF)[FastAction] Remove Action");
|
||||
var node = _delegates.Find(rhs);
|
||||
if (node != null)
|
||||
{
|
||||
_delegates.Remove(node);
|
||||
s_NodePool.Return(ref node);
|
||||
}
|
||||
|
||||
Profiler.EndSample();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the action with a callback function.
|
||||
/// </summary>
|
||||
protected void Invoke(Action<T> callback)
|
||||
{
|
||||
var node = _delegates.First;
|
||||
while (node != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
callback(node.Value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
|
||||
node = node.Next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A fast action without parameters.
|
||||
/// </summary>
|
||||
internal class FastAction : FastActionBase<Action>
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoke all the registered delegates.
|
||||
/// </summary>
|
||||
public void Invoke()
|
||||
{
|
||||
Invoke(action => action.Invoke());
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Internal/Utilities/FastAction.cs.meta
Normal file
11
Runtime/Internal/Utilities/FastAction.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7c8c268a827b4787a8e050f1fe95ad5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
102
Runtime/Internal/Utilities/FrameCache.cs
Normal file
102
Runtime/Internal/Utilities/FrameCache.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Coffee.UIParticleInternal
|
||||
{
|
||||
internal static class FrameCache
|
||||
{
|
||||
private static readonly Dictionary<Type, IFrameCache> s_Caches = new Dictionary<Type, IFrameCache>();
|
||||
|
||||
static FrameCache()
|
||||
{
|
||||
s_Caches.Clear();
|
||||
UIExtraCallbacks.onLateAfterCanvasRebuild += ClearAllCache;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
private static void Clear()
|
||||
{
|
||||
s_Caches.Clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Tries to retrieve a value from the frame cache with a specified key.
|
||||
/// </summary>
|
||||
public static bool TryGet<T>(object key1, string key2, out T result)
|
||||
{
|
||||
return GetFrameCache<T>().TryGet((key1.GetHashCode(), key2.GetHashCode()), out result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to retrieve a value from the frame cache with a specified key.
|
||||
/// </summary>
|
||||
public static bool TryGet<T>(object key1, string key2, int key3, out T result)
|
||||
{
|
||||
return GetFrameCache<T>().TryGet((key1.GetHashCode(), key2.GetHashCode() + key3), out result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a value in the frame cache with a specified key.
|
||||
/// </summary>
|
||||
public static void Set<T>(object key1, string key2, T result)
|
||||
{
|
||||
GetFrameCache<T>().Set((key1.GetHashCode(), key2.GetHashCode()), result);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets a value in the frame cache with a specified key.
|
||||
/// </summary>
|
||||
public static void Set<T>(object key1, string key2, int key3, T result)
|
||||
{
|
||||
GetFrameCache<T>().Set((key1.GetHashCode(), key2.GetHashCode() + key3), result);
|
||||
}
|
||||
|
||||
private static void ClearAllCache()
|
||||
{
|
||||
foreach (var cache in s_Caches.Values)
|
||||
{
|
||||
cache.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static FrameCacheContainer<T> GetFrameCache<T>()
|
||||
{
|
||||
var t = typeof(T);
|
||||
if (s_Caches.TryGetValue(t, out var frameCache)) return frameCache as FrameCacheContainer<T>;
|
||||
|
||||
frameCache = new FrameCacheContainer<T>();
|
||||
s_Caches.Add(t, frameCache);
|
||||
|
||||
return (FrameCacheContainer<T>)frameCache;
|
||||
}
|
||||
|
||||
private interface IFrameCache
|
||||
{
|
||||
void Clear();
|
||||
}
|
||||
|
||||
private class FrameCacheContainer<T> : IFrameCache
|
||||
{
|
||||
private readonly Dictionary<(int, int), T> _caches = new Dictionary<(int, int), T>();
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_caches.Clear();
|
||||
}
|
||||
|
||||
public bool TryGet((int, int) key, out T result)
|
||||
{
|
||||
return _caches.TryGetValue(key, out result);
|
||||
}
|
||||
|
||||
public void Set((int, int) key, T result)
|
||||
{
|
||||
_caches[key] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Internal/Utilities/FrameCache.cs.meta
Normal file
11
Runtime/Internal/Utilities/FrameCache.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f129e3b07ffb4d3bbb4cc5f6bd94087
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
257
Runtime/Internal/Utilities/Logging.cs
Normal file
257
Runtime/Internal/Utilities/Logging.cs
Normal file
@@ -0,0 +1,257 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using Conditional = System.Diagnostics.ConditionalAttribute;
|
||||
using Object = UnityEngine.Object;
|
||||
#if ENABLE_COFFEE_LOGGER
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
#endif
|
||||
|
||||
namespace Coffee.UIParticleInternal
|
||||
{
|
||||
internal static class Logging
|
||||
{
|
||||
#if !ENABLE_COFFEE_LOGGER
|
||||
private const string k_DisableSymbol = "DISABLE_COFFEE_LOGGER";
|
||||
|
||||
[Conditional(k_DisableSymbol)]
|
||||
#endif
|
||||
private static void Log_Internal(LogType type, object tag, object message, Object context)
|
||||
{
|
||||
#if ENABLE_COFFEE_LOGGER
|
||||
AppendTag(s_Sb, tag);
|
||||
s_Sb.Append(message);
|
||||
switch (type)
|
||||
{
|
||||
case LogType.Error:
|
||||
case LogType.Assert:
|
||||
case LogType.Exception:
|
||||
Debug.LogError(s_Sb, context);
|
||||
break;
|
||||
case LogType.Warning:
|
||||
Debug.LogWarning(s_Sb, context);
|
||||
break;
|
||||
case LogType.Log:
|
||||
Debug.Log(s_Sb, context);
|
||||
break;
|
||||
}
|
||||
|
||||
s_Sb.Length = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if !ENABLE_COFFEE_LOGGER
|
||||
[Conditional(k_DisableSymbol)]
|
||||
#endif
|
||||
public static void LogIf(bool enable, object tag, object message, Object context = null)
|
||||
{
|
||||
if (!enable) return;
|
||||
Log_Internal(LogType.Log, tag, message, context ? context : tag as Object);
|
||||
}
|
||||
|
||||
|
||||
#if !ENABLE_COFFEE_LOGGER
|
||||
[Conditional(k_DisableSymbol)]
|
||||
#endif
|
||||
public static void Log(object tag, object message, Object context = null)
|
||||
{
|
||||
Log_Internal(LogType.Log, tag, message, context ? context : tag as Object);
|
||||
}
|
||||
|
||||
|
||||
#if !ENABLE_COFFEE_LOGGER
|
||||
[Conditional(k_DisableSymbol)]
|
||||
#endif
|
||||
public static void LogWarning(object tag, object message, Object context = null)
|
||||
{
|
||||
Log_Internal(LogType.Warning, tag, message, context ? context : tag as Object);
|
||||
}
|
||||
|
||||
public static void LogError(object tag, object message, Object context = null)
|
||||
{
|
||||
#if ENABLE_COFFEE_LOGGER
|
||||
Log_Internal(LogType.Error, tag, message, context ? context : tag as Object);
|
||||
#else
|
||||
Debug.LogError($"{tag}: {message}", context);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if !ENABLE_COFFEE_LOGGER
|
||||
[Conditional(k_DisableSymbol)]
|
||||
#endif
|
||||
public static void LogMulticast(Type type, string fieldName, object instance = null, string message = null)
|
||||
{
|
||||
#if ENABLE_COFFEE_LOGGER
|
||||
AppendTag(s_Sb, instance ?? type);
|
||||
|
||||
var handler = type
|
||||
.GetField(fieldName,
|
||||
BindingFlags.Static | BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic)
|
||||
?.GetValue(instance);
|
||||
|
||||
var list = ((MulticastDelegate)handler)?.GetInvocationList() ?? Array.Empty<Delegate>();
|
||||
s_Sb.Append("<color=orange>");
|
||||
s_Sb.Append(type.Name);
|
||||
s_Sb.Append(".");
|
||||
s_Sb.Append(fieldName);
|
||||
s_Sb.Append(" has ");
|
||||
s_Sb.Append(list.Length);
|
||||
s_Sb.Append(" callbacks");
|
||||
if (message != null)
|
||||
{
|
||||
s_Sb.Append(" (");
|
||||
s_Sb.Append(message);
|
||||
s_Sb.Append(")");
|
||||
}
|
||||
|
||||
s_Sb.Append(":</color>");
|
||||
|
||||
for (var i = 0; i < list.Length; i++)
|
||||
{
|
||||
s_Sb.Append("\n - ");
|
||||
s_Sb.Append(list[i].Method.DeclaringType?.Name);
|
||||
s_Sb.Append(".");
|
||||
s_Sb.Append(list[i].Method.Name);
|
||||
}
|
||||
|
||||
Debug.Log(s_Sb);
|
||||
s_Sb.Length = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if !ENABLE_COFFEE_LOGGER
|
||||
[Conditional(k_DisableSymbol)]
|
||||
#endif
|
||||
private static void AppendTag(StringBuilder sb, object tag)
|
||||
{
|
||||
#if ENABLE_COFFEE_LOGGER
|
||||
try
|
||||
{
|
||||
sb.Append("f");
|
||||
sb.Append(Time.frameCount);
|
||||
sb.Append(":<color=#");
|
||||
AppendReadableCode(sb, tag);
|
||||
sb.Append("><b>[");
|
||||
|
||||
switch (tag)
|
||||
{
|
||||
case Type type:
|
||||
AppendType(sb, type);
|
||||
break;
|
||||
case Object uObject:
|
||||
AppendType(sb, tag.GetType());
|
||||
sb.Append(" #");
|
||||
sb.Append(uObject.name);
|
||||
break;
|
||||
default:
|
||||
AppendType(sb, tag.GetType());
|
||||
break;
|
||||
}
|
||||
|
||||
sb.Append("]</b></color> ");
|
||||
}
|
||||
catch
|
||||
{
|
||||
sb.Append("f");
|
||||
sb.Append(Time.frameCount);
|
||||
sb.Append(":<b>[");
|
||||
sb.Append(tag);
|
||||
sb.Append("]</b> ");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if !ENABLE_COFFEE_LOGGER
|
||||
[Conditional(k_DisableSymbol)]
|
||||
#endif
|
||||
private static void AppendType(StringBuilder sb, Type type)
|
||||
{
|
||||
#if ENABLE_COFFEE_LOGGER
|
||||
if (s_TypeNameCache.TryGetValue(type, out var name))
|
||||
{
|
||||
sb.Append(name);
|
||||
return;
|
||||
}
|
||||
|
||||
// New type found
|
||||
var start = sb.Length;
|
||||
if (0 < start && sb[start - 1] == '<' && (type.Name == "Material" || type.Name == "Color"))
|
||||
{
|
||||
sb.Append('@');
|
||||
}
|
||||
|
||||
sb.Append(type.Name);
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
sb.Length -= 2;
|
||||
sb.Append("<");
|
||||
foreach (var gType in type.GetGenericArguments())
|
||||
{
|
||||
AppendType(sb, gType);
|
||||
sb.Append(", ");
|
||||
}
|
||||
|
||||
sb.Length -= 2;
|
||||
sb.Append(">");
|
||||
}
|
||||
|
||||
s_TypeNameCache.Add(type, sb.ToString(start, sb.Length - start));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if !ENABLE_COFFEE_LOGGER
|
||||
[Conditional(k_DisableSymbol)]
|
||||
#endif
|
||||
private static void AppendReadableCode(StringBuilder sb, object tag)
|
||||
{
|
||||
#if ENABLE_COFFEE_LOGGER
|
||||
int hash;
|
||||
try
|
||||
{
|
||||
switch (tag)
|
||||
{
|
||||
case string text:
|
||||
hash = text.GetHashCode();
|
||||
break;
|
||||
case Type type:
|
||||
type = type.IsGenericType ? type.GetGenericTypeDefinition() : type;
|
||||
hash = type.FullName?.GetHashCode() ?? 0;
|
||||
break;
|
||||
default:
|
||||
hash = tag.GetType().FullName?.GetHashCode() ?? 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
sb.Append("FFFFFF");
|
||||
return;
|
||||
}
|
||||
|
||||
hash = hash & (s_Codes.Length - 1);
|
||||
if (s_Codes[hash] == null)
|
||||
{
|
||||
var hue = hash / (float)s_Codes.Length;
|
||||
var modifier = 1f - Mathf.Clamp01(Mathf.Abs(hue - 0.65f) / 0.2f);
|
||||
var saturation = 0.7f + modifier * -0.2f;
|
||||
var value = 0.8f + modifier * 0.3f;
|
||||
s_Codes[hash] = ColorUtility.ToHtmlStringRGB(Color.HSVToRGB(hue, saturation, value));
|
||||
}
|
||||
|
||||
sb.Append(s_Codes[hash]);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ENABLE_COFFEE_LOGGER
|
||||
private static readonly StringBuilder s_Sb = new StringBuilder();
|
||||
private static readonly string[] s_Codes = new string[64];
|
||||
private static readonly Dictionary<Type, string> s_TypeNameCache = new Dictionary<Type, string>();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
11
Runtime/Internal/Utilities/Logging.cs.meta
Normal file
11
Runtime/Internal/Utilities/Logging.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8255313895da84e7cbdc876be3795334
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
65
Runtime/Internal/Utilities/MaterialRepository.cs
Normal file
65
Runtime/Internal/Utilities/MaterialRepository.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
namespace Coffee.UIParticleInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides functionality to manage materials.
|
||||
/// </summary>
|
||||
internal static class MaterialRepository
|
||||
{
|
||||
private static readonly ObjectRepository<Material> s_Repository = new ObjectRepository<Material>();
|
||||
|
||||
public static int count => s_Repository.count;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
private static void Clear()
|
||||
{
|
||||
s_Repository.Clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a cached material based on the hash.
|
||||
/// </summary>
|
||||
public static bool Valid(Hash128 hash, Material material)
|
||||
{
|
||||
Profiler.BeginSample("(COF)[MaterialRegistry] Valid");
|
||||
var ret = s_Repository.Valid(hash, material);
|
||||
Profiler.EndSample();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds or retrieves a cached material based on the hash.
|
||||
/// </summary>
|
||||
public static void Get(Hash128 hash, ref Material material, Func<Material> onCreate)
|
||||
{
|
||||
Profiler.BeginSample("(COF)[MaterialRepository] Get");
|
||||
s_Repository.Get(hash, ref material, onCreate);
|
||||
Profiler.EndSample();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds or retrieves a cached material based on the hash.
|
||||
/// </summary>
|
||||
public static void Get<T>(Hash128 hash, ref Material material, Func<T, Material> onCreate, T source)
|
||||
{
|
||||
Profiler.BeginSample("(COF)[MaterialRepository] Get");
|
||||
s_Repository.Get(hash, ref material, onCreate, source);
|
||||
Profiler.EndSample();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a soft mask material from the cache.
|
||||
/// </summary>
|
||||
public static void Release(ref Material material)
|
||||
{
|
||||
Profiler.BeginSample("(COF)[MaterialRepository] Release");
|
||||
s_Repository.Release(ref material);
|
||||
Profiler.EndSample();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Internal/Utilities/MaterialRepository.cs.meta
Normal file
11
Runtime/Internal/Utilities/MaterialRepository.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 702912f2ee2ec49bb8003a64151ae4f7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
85
Runtime/Internal/Utilities/ObjectPool.cs
Normal file
85
Runtime/Internal/Utilities/ObjectPool.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Coffee.UIParticleInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Object pool.
|
||||
/// </summary>
|
||||
internal class ObjectPool<T>
|
||||
{
|
||||
private readonly Func<T> _onCreate; // Delegate for creating instances
|
||||
private readonly Action<T> _onReturn; // Delegate for returning instances to the pool
|
||||
private readonly Predicate<T> _onValid; // Delegate for checking if instances are valid
|
||||
private readonly Stack<T> _pool = new Stack<T>(32); // Object pool
|
||||
private int _count; // Total count of created instances
|
||||
|
||||
public ObjectPool(Func<T> onCreate, Predicate<T> onValid, Action<T> onReturn)
|
||||
{
|
||||
_onCreate = onCreate;
|
||||
_onValid = onValid;
|
||||
_onReturn = onReturn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rent an instance from the pool.
|
||||
/// When you no longer need it, return it with <see cref="Return" />.
|
||||
/// </summary>
|
||||
public T Rent()
|
||||
{
|
||||
while (0 < _pool.Count)
|
||||
{
|
||||
var instance = _pool.Pop();
|
||||
if (_onValid(instance))
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no instances in the pool, create a new one.
|
||||
Logging.Log(this, $"A new instance is created (pooled: {_pool.Count}, created: {++_count}).");
|
||||
return _onCreate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return an instance to the pool and assign null.
|
||||
/// Be sure to return the instance obtained with <see cref="Rent" /> with this method.
|
||||
/// </summary>
|
||||
public void Return(ref T instance)
|
||||
{
|
||||
if (instance == null || _pool.Contains(instance)) return; // Ignore if already pooled or null.
|
||||
|
||||
_onReturn(instance); // Return the instance to the pool.
|
||||
_pool.Push(instance);
|
||||
Logging.Log(this, $"An instance is released (pooled: {_pool.Count}, created: {_count}).");
|
||||
instance = default; // Set the reference to null.
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Object pool for <see cref="List{T}" />.
|
||||
/// </summary>
|
||||
internal static class ListPool<T>
|
||||
{
|
||||
private static readonly ObjectPool<List<T>> s_ListPool =
|
||||
new ObjectPool<List<T>>(() => new List<T>(), _ => true, x => x.Clear());
|
||||
|
||||
/// <summary>
|
||||
/// Rent an instance from the pool.
|
||||
/// When you no longer need it, return it with <see cref="Return" />.
|
||||
/// </summary>
|
||||
public static List<T> Rent()
|
||||
{
|
||||
return s_ListPool.Rent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return an instance to the pool and assign null.
|
||||
/// Be sure to return the instance obtained with <see cref="Rent" /> with this method.
|
||||
/// </summary>
|
||||
public static void Return(ref List<T> toRelease)
|
||||
{
|
||||
s_ListPool.Return(ref toRelease);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Internal/Utilities/ObjectPool.cs.meta
Normal file
11
Runtime/Internal/Utilities/ObjectPool.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 632cb1ba34e6a4e80b55a32bb63ca369
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
218
Runtime/Internal/Utilities/ObjectRepository.cs
Normal file
218
Runtime/Internal/Utilities/ObjectRepository.cs
Normal file
@@ -0,0 +1,218 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Coffee.UIParticleInternal
|
||||
{
|
||||
internal class ObjectRepository<T> where T : Object
|
||||
{
|
||||
private readonly List<Entry> _cache = new List<Entry>();
|
||||
private readonly string _name;
|
||||
private readonly Action<T> _onRelease;
|
||||
private readonly Stack<Entry> _pool = new Stack<Entry>();
|
||||
|
||||
public ObjectRepository(Action<T> onRelease = null)
|
||||
{
|
||||
_name = $"{typeof(T).Name}Repository";
|
||||
if (onRelease == null)
|
||||
{
|
||||
_onRelease = x =>
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
Object.DestroyImmediate(x, false);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
Object.Destroy(x);
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
_onRelease = onRelease;
|
||||
}
|
||||
}
|
||||
|
||||
public int count => _cache.Count;
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
for (var i = 0; i < _cache.Count; i++)
|
||||
{
|
||||
var entry = _cache[i];
|
||||
if (entry == null) continue;
|
||||
|
||||
entry.Release(_onRelease);
|
||||
}
|
||||
|
||||
_cache.Clear();
|
||||
}
|
||||
|
||||
public bool Valid(Hash128 hash, T 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>
|
||||
/// Adds or retrieves a cached object based on the hash.
|
||||
/// </summary>
|
||||
public void Get(Hash128 hash, ref T obj, Func<T> onCreate)
|
||||
{
|
||||
// Find existing entry.
|
||||
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>
|
||||
/// Adds or retrieves a cached object based on the hash.
|
||||
/// </summary>
|
||||
public void Get<TS>(Hash128 hash, ref T obj, Func<TS, T> onCreate, TS source)
|
||||
{
|
||||
// Find existing entry.
|
||||
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(source);
|
||||
newEntry.hash = hash;
|
||||
newEntry.reference = 1;
|
||||
_cache.Add(newEntry);
|
||||
Logging.Log(_name, $"Get(#{count}): {newEntry}");
|
||||
|
||||
Release(ref obj);
|
||||
obj = newEntry.storedObject;
|
||||
Profiler.EndSample();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release a object.
|
||||
/// </summary>
|
||||
public void Release(ref T obj)
|
||||
{
|
||||
if (ReferenceEquals(obj, null)) return;
|
||||
|
||||
Profiler.BeginSample("(COF)[ObjectRepository] Release");
|
||||
for (var i = 0; i < _cache.Count; i++)
|
||||
{
|
||||
var entry = _cache[i];
|
||||
|
||||
if (entry.storedObject != obj)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (--entry.reference <= 0)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
Logging.Log(_name, $"Release(#{count}): {entry}");
|
||||
break;
|
||||
}
|
||||
|
||||
obj = null;
|
||||
Profiler.EndSample();
|
||||
}
|
||||
|
||||
private class Entry
|
||||
{
|
||||
public Hash128 hash;
|
||||
public int reference;
|
||||
public T storedObject;
|
||||
|
||||
public void Release(Action<T> onRelease)
|
||||
{
|
||||
reference = 0;
|
||||
if (storedObject)
|
||||
{
|
||||
onRelease?.Invoke(storedObject);
|
||||
}
|
||||
|
||||
storedObject = null;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"h{(uint)hash.GetHashCode()} (#{reference}), {storedObject}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Internal/Utilities/ObjectRepository.cs.meta
Normal file
11
Runtime/Internal/Utilities/ObjectRepository.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a713d67bdb31e45e296e5f18460717e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
92
Runtime/Internal/Utilities/UIExtraCallbacks.cs
Executable file
92
Runtime/Internal/Utilities/UIExtraCallbacks.cs
Executable file
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Coffee.UIParticleInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides additional callbacks related to canvas and UI system.
|
||||
/// </summary>
|
||||
internal static class UIExtraCallbacks
|
||||
{
|
||||
private static bool s_IsInitializedAfterCanvasRebuild;
|
||||
private static readonly FastAction s_AfterCanvasRebuildAction = new FastAction();
|
||||
private static readonly FastAction s_LateAfterCanvasRebuildAction = new FastAction();
|
||||
private static readonly FastAction s_BeforeCanvasRebuildAction = new FastAction();
|
||||
|
||||
static UIExtraCallbacks()
|
||||
{
|
||||
Canvas.willRenderCanvases += OnBeforeCanvasRebuild;
|
||||
Logging.LogMulticast(typeof(Canvas), "willRenderCanvases", message: "ctor");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event that occurs after canvas rebuilds.
|
||||
/// </summary>
|
||||
public static event Action onLateAfterCanvasRebuild
|
||||
{
|
||||
add => s_LateAfterCanvasRebuildAction.Add(value);
|
||||
remove => s_LateAfterCanvasRebuildAction.Remove(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event that occurs before canvas rebuilds.
|
||||
/// </summary>
|
||||
public static event Action onBeforeCanvasRebuild
|
||||
{
|
||||
add => s_BeforeCanvasRebuildAction.Add(value);
|
||||
remove => s_BeforeCanvasRebuildAction.Remove(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event that occurs after canvas rebuilds.
|
||||
/// </summary>
|
||||
public static event Action onAfterCanvasRebuild
|
||||
{
|
||||
add => s_AfterCanvasRebuildAction.Add(value);
|
||||
remove => s_AfterCanvasRebuildAction.Remove(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the UIExtraCallbacks to ensure proper event handling.
|
||||
/// </summary>
|
||||
private static void InitializeAfterCanvasRebuild()
|
||||
{
|
||||
if (s_IsInitializedAfterCanvasRebuild) return;
|
||||
s_IsInitializedAfterCanvasRebuild = true;
|
||||
|
||||
CanvasUpdateRegistry.IsRebuildingLayout();
|
||||
Canvas.willRenderCanvases += OnAfterCanvasRebuild;
|
||||
Logging.LogMulticast(typeof(Canvas), "willRenderCanvases",
|
||||
message: "InitializeAfterCanvasRebuild");
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[InitializeOnLoadMethod]
|
||||
#else
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||
#endif
|
||||
private static void InitializeOnLoad()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback method called before canvas rebuilds.
|
||||
/// </summary>
|
||||
private static void OnBeforeCanvasRebuild()
|
||||
{
|
||||
s_BeforeCanvasRebuildAction.Invoke();
|
||||
InitializeAfterCanvasRebuild();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback method called after canvas rebuilds.
|
||||
/// </summary>
|
||||
private static void OnAfterCanvasRebuild()
|
||||
{
|
||||
s_AfterCanvasRebuildAction.Invoke();
|
||||
s_LateAfterCanvasRebuildAction.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Internal/Utilities/UIExtraCallbacks.cs.meta
Normal file
11
Runtime/Internal/Utilities/UIExtraCallbacks.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9ea318e6e3e6c46aa97c72e28230bdc9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,72 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Coffee.UIParticleExtensions
|
||||
{
|
||||
internal class ModifiedMaterial
|
||||
{
|
||||
private static readonly List<MatEntry> s_Entries = new List<MatEntry>();
|
||||
|
||||
public static Material Add(Material baseMat, Texture texture, int id, int props)
|
||||
{
|
||||
MatEntry e;
|
||||
for (var i = 0; i < s_Entries.Count; i++)
|
||||
{
|
||||
e = s_Entries[i];
|
||||
if (e.baseMat != baseMat || e.texture != texture || e.id != id || e.props != props) continue;
|
||||
++e.count;
|
||||
return e.customMat;
|
||||
}
|
||||
|
||||
e = new MatEntry
|
||||
{
|
||||
count = 1,
|
||||
baseMat = baseMat,
|
||||
texture = texture,
|
||||
id = id,
|
||||
props = props,
|
||||
customMat = new Material(baseMat)
|
||||
{
|
||||
name = $"{baseMat.name}_{id}",
|
||||
hideFlags = HideFlags.DontSave | HideFlags.NotEditable,
|
||||
mainTexture = texture ? texture : baseMat.mainTexture
|
||||
}
|
||||
};
|
||||
s_Entries.Add(e);
|
||||
//Debug.LogFormat(">>>> ModifiedMaterial.Add -> count = count:{0}, mat:{1}, tex:{2}, id:{3}", s_Entries.Count, baseMat, texture, id);
|
||||
return e.customMat;
|
||||
}
|
||||
|
||||
public static void Remove(Material customMat)
|
||||
{
|
||||
if (!customMat) return;
|
||||
|
||||
for (var i = 0; i < s_Entries.Count; ++i)
|
||||
{
|
||||
var e = s_Entries[i];
|
||||
if (e.customMat != customMat) continue;
|
||||
if (--e.count == 0)
|
||||
{
|
||||
//Debug.LogFormat(">>>> ModifiedMaterial.Remove -> count:{0}, mat:{1}, tex:{2}, id:{3}", s_Entries.Count - 1, e.customMat, e.texture, e.id);
|
||||
Misc.DestroyImmediate(e.customMat);
|
||||
e.customMat = null;
|
||||
e.baseMat = null;
|
||||
e.texture = null;
|
||||
s_Entries.RemoveAt(i);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private class MatEntry
|
||||
{
|
||||
public Material baseMat;
|
||||
public int count;
|
||||
public Material customMat;
|
||||
public int id;
|
||||
public int props;
|
||||
public Texture texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Coffee.UIParticleExtensions;
|
||||
using UnityEditor;
|
||||
using Coffee.UIParticleInternal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Serialization;
|
||||
using UnityEngine.UI;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
[assembly: InternalsVisibleTo("Coffee.UIParticle.Editor")]
|
||||
@@ -19,7 +18,7 @@ namespace Coffee.UIExtensions
|
||||
[ExecuteAlways]
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
[RequireComponent(typeof(CanvasRenderer))]
|
||||
public class UIParticle : MaskableGraphic, ISerializationCallbackReceiver
|
||||
public class UIParticle : UIBehaviour, ISerializationCallbackReceiver
|
||||
{
|
||||
public enum AutoScalingMode
|
||||
{
|
||||
@@ -45,20 +44,23 @@ namespace Coffee.UIExtensions
|
||||
|
||||
[HideInInspector]
|
||||
[SerializeField]
|
||||
[Obsolete]
|
||||
internal bool m_IsTrail;
|
||||
|
||||
[HideInInspector]
|
||||
[FormerlySerializedAs("m_IgnoreParent")]
|
||||
[SerializeField]
|
||||
[Obsolete]
|
||||
private bool m_IgnoreCanvasScaler;
|
||||
|
||||
[HideInInspector]
|
||||
[SerializeField]
|
||||
private bool m_AbsoluteMode;
|
||||
[Obsolete]
|
||||
internal bool m_AbsoluteMode;
|
||||
|
||||
[Tooltip("Particle effect scale")]
|
||||
[SerializeField]
|
||||
private Vector3 m_Scale3D = new Vector3(10, 10, 10);
|
||||
private Vector3 m_Scale3D = new Vector3(1, 1, 1);
|
||||
|
||||
[Tooltip("Animatable material properties.\n" +
|
||||
"If you want to change the material properties of the ParticleSystem in Animation, enable it.")]
|
||||
@@ -93,25 +95,54 @@ namespace Coffee.UIExtensions
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Prevent the root-Canvas scale from affecting the hierarchy-scaled ParticleSystem.")]
|
||||
private bool m_AutoScaling = true;
|
||||
[Obsolete]
|
||||
internal bool m_AutoScaling;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Transform: Transform.lossyScale (=world scale) will be set to (1, 1, 1)." +
|
||||
"UIParticle: UIParticle.scale will be adjusted.")]
|
||||
private AutoScalingMode m_AutoScalingMode = AutoScalingMode.Transform;
|
||||
|
||||
[SerializeField]
|
||||
private bool m_Maskable = true;
|
||||
|
||||
private readonly List<UIParticleRenderer> _renderers = new List<UIParticleRenderer>();
|
||||
private Canvas _canvas;
|
||||
private int _groupId;
|
||||
private Camera _orthoCamera;
|
||||
private Camera _orthographicCamera;
|
||||
private DrivenRectTransformTracker _tracker;
|
||||
|
||||
/// <summary>
|
||||
/// Should this graphic be considered a target for ray-casting?
|
||||
/// </summary>
|
||||
public override bool raycastTarget
|
||||
public RectTransform rectTransform => transform as RectTransform;
|
||||
|
||||
public Canvas canvas
|
||||
{
|
||||
get => false;
|
||||
set { }
|
||||
get
|
||||
{
|
||||
if (_canvas) return _canvas;
|
||||
|
||||
var tr = transform;
|
||||
while (tr && !_canvas)
|
||||
{
|
||||
if (tr.TryGetComponent(out _canvas)) return _canvas;
|
||||
tr = tr.parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does this graphic allow masking.
|
||||
/// </summary>
|
||||
public bool maskable
|
||||
{
|
||||
get => m_Maskable;
|
||||
set
|
||||
{
|
||||
if (value == m_Maskable) return;
|
||||
m_Maskable = value;
|
||||
UpdateRendererMaterial();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -266,8 +297,6 @@ namespace Coffee.UIExtensions
|
||||
}
|
||||
}
|
||||
|
||||
public override Material materialForRendering => null;
|
||||
|
||||
/// <summary>
|
||||
/// Paused.
|
||||
/// </summary>
|
||||
@@ -275,15 +304,15 @@ namespace Coffee.UIExtensions
|
||||
|
||||
public Vector3 parentScale { get; private set; }
|
||||
|
||||
public Vector3 canvasScale { get; private set; }
|
||||
private Vector3 canvasScale { get; set; }
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
ResetGroupId();
|
||||
UpdateTracker();
|
||||
UIParticleUpdater.Register(this);
|
||||
RegisterDirtyMaterialCallback(UpdateRendererMaterial);
|
||||
|
||||
//
|
||||
if (0 < particles.Count)
|
||||
{
|
||||
RefreshParticles(particles);
|
||||
@@ -293,7 +322,7 @@ namespace Coffee.UIExtensions
|
||||
RefreshParticles();
|
||||
}
|
||||
|
||||
base.OnEnable();
|
||||
UpdateRendererMaterial();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -304,9 +333,15 @@ namespace Coffee.UIExtensions
|
||||
UpdateTracker();
|
||||
UIParticleUpdater.Unregister(this);
|
||||
_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>
|
||||
@@ -316,11 +351,20 @@ 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;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
UpdateTracker();
|
||||
UpdateRendererMaterial();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -330,6 +374,7 @@ namespace Coffee.UIExtensions
|
||||
|
||||
void ISerializationCallbackReceiver.OnAfterDeserialize()
|
||||
{
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
if (m_IgnoreCanvasScaler || m_AutoScaling)
|
||||
{
|
||||
m_IgnoreCanvasScaler = false;
|
||||
@@ -342,31 +387,47 @@ namespace Coffee.UIExtensions
|
||||
m_AbsoluteMode = false;
|
||||
m_PositionMode = PositionMode.Absolute;
|
||||
}
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play the ParticleSystems.
|
||||
/// </summary>
|
||||
public void Play()
|
||||
{
|
||||
particles.Exec(p => p.Simulate(0, false, true));
|
||||
isPaused = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pause the ParticleSystems.
|
||||
/// </summary>
|
||||
public void Pause()
|
||||
{
|
||||
particles.Exec(p => p.Pause());
|
||||
isPaused = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpause the ParticleSystems.
|
||||
/// </summary>
|
||||
public void Resume()
|
||||
{
|
||||
isPaused = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop the ParticleSystems.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
particles.Exec(p => p.Stop());
|
||||
isPaused = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start emission of the ParticleSystems.
|
||||
/// </summary>
|
||||
public void StartEmission()
|
||||
{
|
||||
particles.Exec(p =>
|
||||
@@ -376,6 +437,9 @@ namespace Coffee.UIExtensions
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop emission of the ParticleSystems.
|
||||
/// </summary>
|
||||
public void StopEmission()
|
||||
{
|
||||
particles.Exec(p =>
|
||||
@@ -385,24 +449,34 @@ namespace Coffee.UIExtensions
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the particles of the ParticleSystems.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
particles.Exec(p => p.Clear());
|
||||
isPaused = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh UIParticle using the ParticleSystem instance.
|
||||
/// </summary>
|
||||
public void SetParticleSystemInstance(GameObject instance)
|
||||
{
|
||||
SetParticleSystemInstance(instance, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh UIParticle using the ParticleSystem instance.
|
||||
/// </summary>
|
||||
public void SetParticleSystemInstance(GameObject instance, bool destroyOldParticles)
|
||||
{
|
||||
if (!instance) return;
|
||||
|
||||
foreach (Transform child in transform)
|
||||
var childCount = transform.childCount;
|
||||
for (var i = 0; i < childCount; i++)
|
||||
{
|
||||
var go = child.gameObject;
|
||||
var go = transform.GetChild(i).gameObject;
|
||||
go.SetActive(false);
|
||||
if (destroyOldParticles)
|
||||
{
|
||||
@@ -417,6 +491,10 @@ namespace Coffee.UIExtensions
|
||||
RefreshParticles(instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh UIParticle using the prefab.
|
||||
/// The prefab is automatically instantiated.
|
||||
/// </summary>
|
||||
public void SetParticleSystemPrefab(GameObject prefab)
|
||||
{
|
||||
if (!prefab) return;
|
||||
@@ -424,16 +502,31 @@ namespace Coffee.UIExtensions
|
||||
SetParticleSystemInstance(Instantiate(prefab.gameObject), true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh UIParticle.
|
||||
/// Collect ParticleSystems under the GameObject and refresh the UIParticle.
|
||||
/// </summary>
|
||||
public void RefreshParticles()
|
||||
{
|
||||
RefreshParticles(gameObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh UIParticle.
|
||||
/// Collect ParticleSystems under the GameObject and refresh the UIParticle.
|
||||
/// </summary>
|
||||
private void RefreshParticles(GameObject root)
|
||||
{
|
||||
if (!root) return;
|
||||
root.GetComponentsInChildren(true, particles);
|
||||
particles.RemoveAll(x => x.GetComponentInParent<UIParticle>(true) != this);
|
||||
for (var i = particles.Count - 1; 0 <= i; i--)
|
||||
{
|
||||
var ps = particles[i];
|
||||
if (!ps || ps.GetComponentInParent<UIParticle>(true) != this)
|
||||
{
|
||||
particles.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < particles.Count; i++)
|
||||
{
|
||||
@@ -448,31 +541,39 @@ namespace Coffee.UIExtensions
|
||||
RefreshParticles(particles);
|
||||
}
|
||||
|
||||
public void RefreshParticles(List<ParticleSystem> particles)
|
||||
/// <summary>
|
||||
/// Refresh UIParticle using a list of ParticleSystems.
|
||||
/// </summary>
|
||||
public void RefreshParticles(List<ParticleSystem> particleSystems)
|
||||
{
|
||||
// Collect children UIParticleRenderer components.
|
||||
// #246: Nullptr exceptions when using nested UIParticle components in hierarchy
|
||||
_renderers.Clear();
|
||||
foreach (Transform child in transform)
|
||||
var childCount = transform.childCount;
|
||||
for (var i = 0; i < childCount; i++)
|
||||
{
|
||||
var uiParticleRenderer = child.GetComponent<UIParticleRenderer>();
|
||||
|
||||
if (uiParticleRenderer != null)
|
||||
var child = transform.GetChild(i);
|
||||
if (child.TryGetComponent(out UIParticleRenderer uiParticleRenderer))
|
||||
{
|
||||
_renderers.Add(uiParticleRenderer);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the UIParticleRenderer components.
|
||||
for (var i = 0; i < _renderers.Count; i++)
|
||||
{
|
||||
_renderers[i].Reset(i);
|
||||
}
|
||||
|
||||
// Set the ParticleSystem to the UIParticleRenderer. If the trail is enabled, set it additionally.
|
||||
var j = 0;
|
||||
for (var i = 0; i < particles.Count; i++)
|
||||
for (var i = 0; i < particleSystems.Count; i++)
|
||||
{
|
||||
var ps = particles[i];
|
||||
var ps = particleSystems[i];
|
||||
if (!ps) continue;
|
||||
GetRenderer(j++).Set(this, ps, false);
|
||||
|
||||
// If the trail is enabled, set it additionally.
|
||||
if (ps.trails.enabled)
|
||||
{
|
||||
GetRenderer(j++).Set(this, ps, true);
|
||||
@@ -500,18 +601,18 @@ namespace Coffee.UIExtensions
|
||||
for (var i = 0; i < _renderers.Count; i++)
|
||||
{
|
||||
var r = _renderers[i];
|
||||
if (!r)
|
||||
{
|
||||
if (r) continue;
|
||||
|
||||
RefreshParticles(particles);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var bakeCamera = GetBakeCamera();
|
||||
for (var i = 0; i < _renderers.Count; i++)
|
||||
{
|
||||
var r = _renderers[i];
|
||||
if (!r) continue;
|
||||
|
||||
r.UpdateMesh(bakeCamera);
|
||||
}
|
||||
}
|
||||
@@ -523,17 +624,6 @@ namespace Coffee.UIExtensions
|
||||
: 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()
|
||||
{
|
||||
for (var i = 0; i < _renderers.Count; i++)
|
||||
@@ -571,22 +661,23 @@ namespace Coffee.UIExtensions
|
||||
return root.worldCamera ? root.worldCamera : Camera.main;
|
||||
}
|
||||
|
||||
// When render mode is ScreenSpaceOverlay, use ortho-camera.
|
||||
if (!_orthoCamera)
|
||||
// When render mode is ScreenSpaceOverlay, use orthographic-camera.
|
||||
if (!_orthographicCamera)
|
||||
{
|
||||
// Find existing ortho-camera.
|
||||
foreach (Transform child in transform)
|
||||
// Find existing orthographic-camera.
|
||||
var childCount = transform.childCount;
|
||||
for (var i = 0; i < childCount; i++)
|
||||
{
|
||||
var cam = child.GetComponent<Camera>();
|
||||
if (cam && cam.name == "[generated] UIParticleOverlayCamera")
|
||||
if (transform.GetChild(i).TryGetComponent<Camera>(out var cam)
|
||||
&& cam.name == "[generated] UIParticleOverlayCamera")
|
||||
{
|
||||
_orthoCamera = cam;
|
||||
_orthographicCamera = cam;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create ortho-camera.
|
||||
if (!_orthoCamera)
|
||||
// Create orthographic-camera.
|
||||
if (!_orthographicCamera)
|
||||
{
|
||||
var go = new GameObject("[generated] UIParticleOverlayCamera")
|
||||
{
|
||||
@@ -594,25 +685,24 @@ namespace Coffee.UIExtensions
|
||||
};
|
||||
go.SetActive(false);
|
||||
go.transform.SetParent(transform, false);
|
||||
_orthoCamera = go.AddComponent<Camera>();
|
||||
_orthoCamera.enabled = false;
|
||||
_orthographicCamera = go.AddComponent<Camera>();
|
||||
_orthographicCamera.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
var size = ((RectTransform)root.transform).rect.size;
|
||||
_orthoCamera.orthographicSize = Mathf.Max(size.x, size.y) * root.scaleFactor;
|
||||
_orthoCamera.transform.SetPositionAndRotation(new Vector3(0, 0, -1000), Quaternion.identity);
|
||||
_orthoCamera.orthographic = true;
|
||||
_orthoCamera.farClipPlane = 2000f;
|
||||
_orthographicCamera.orthographicSize = 10;
|
||||
_orthographicCamera.transform.SetPositionAndRotation(new Vector3(0, 0, -1000), Quaternion.identity);
|
||||
_orthographicCamera.orthographic = true;
|
||||
_orthographicCamera.farClipPlane = 2000f;
|
||||
|
||||
return _orthoCamera;
|
||||
return _orthographicCamera;
|
||||
}
|
||||
|
||||
private void UpdateTracker()
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (!enabled || !autoScaling || autoScalingMode != AutoScalingMode.Transform)
|
||||
if (!enabled || autoScalingMode != AutoScalingMode.Transform)
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
{
|
||||
_tracker.Clear();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using Coffee.UIParticleExtensions;
|
||||
using Coffee.UIParticleInternal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
|
||||
28
Runtime/UIParticleProjectSettings.cs
Normal file
28
Runtime/UIParticleProjectSettings.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma warning disable CS0414
|
||||
using Coffee.UIParticleInternal;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Coffee.UIExtensions
|
||||
{
|
||||
public class UIParticleProjectSettings : PreloadedProjectSettings<UIParticleProjectSettings>
|
||||
{
|
||||
[Header("Setting")]
|
||||
[SerializeField]
|
||||
internal bool m_EnableLinearToGamma = true;
|
||||
|
||||
public static bool enableLinearToGamma
|
||||
{
|
||||
get => instance.m_EnableLinearToGamma;
|
||||
set => instance.m_EnableLinearToGamma = value;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[SettingsProvider]
|
||||
private static SettingsProvider CreateSettingsProvider()
|
||||
{
|
||||
return new PreloadedProjectSettingsProvider("Project/UI/UI Particle");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
11
Runtime/UIParticleProjectSettings.cs.meta
Normal file
11
Runtime/UIParticleProjectSettings.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f22a23b9d98e440478697f4adf30e61c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -4,9 +4,10 @@
|
||||
#elif UNITY_2022_3_OR_NEWER
|
||||
#define PS_BAKE_API_V2
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Coffee.UIParticleExtensions;
|
||||
using Coffee.UIParticleInternal;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
@@ -21,18 +22,16 @@ namespace Coffee.UIExtensions
|
||||
[AddComponentMenu("")]
|
||||
internal class UIParticleRenderer : MaskableGraphic
|
||||
{
|
||||
private static readonly List<Component> s_Components = new List<Component>();
|
||||
private static readonly CombineInstance[] s_CombineInstances = { new CombineInstance() };
|
||||
private static readonly List<Material> s_Materials = new List<Material>(2);
|
||||
private static MaterialPropertyBlock s_Mpb;
|
||||
private static readonly List<UIParticleRenderer> s_Renderers = new List<UIParticleRenderer>();
|
||||
private static readonly List<Color32> s_Colors = new List<Color32>();
|
||||
private static readonly List<UIParticleRenderer> s_Renderers = new List<UIParticleRenderer>(8);
|
||||
private static readonly Vector3[] s_Corners = new Vector3[4];
|
||||
private Material _currentMaterialForRendering;
|
||||
private bool _delay;
|
||||
private int _index;
|
||||
private bool _isTrail;
|
||||
private Bounds _lastBounds;
|
||||
private Material _materialForRendering;
|
||||
private Material _modifiedMaterial;
|
||||
private UIParticle _parent;
|
||||
private ParticleSystem _particleSystem;
|
||||
@@ -40,7 +39,7 @@ namespace Coffee.UIExtensions
|
||||
private Vector3 _prevPsPos;
|
||||
private Vector3 _prevScale;
|
||||
private Vector2Int _prevScreenSize;
|
||||
private bool _prewarm;
|
||||
private bool _preWarm;
|
||||
private ParticleSystemRenderer _renderer;
|
||||
|
||||
public override Texture mainTexture => _isTrail ? null : _particleSystem.GetTextureForSprite();
|
||||
@@ -91,6 +90,19 @@ namespace Coffee.UIExtensions
|
||||
}
|
||||
}
|
||||
|
||||
public override Material materialForRendering
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_materialForRendering)
|
||||
{
|
||||
_materialForRendering = base.materialForRendering;
|
||||
}
|
||||
|
||||
return _materialForRendering;
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset(int index = -1)
|
||||
{
|
||||
if (_renderer)
|
||||
@@ -110,16 +122,14 @@ namespace Coffee.UIExtensions
|
||||
if (this && isActiveAndEnabled)
|
||||
{
|
||||
material = null;
|
||||
workerMesh.Clear();
|
||||
canvasRenderer.SetMesh(workerMesh);
|
||||
canvasRenderer.Clear();
|
||||
_lastBounds = new Bounds();
|
||||
enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ModifiedMaterial.Remove(_modifiedMaterial);
|
||||
_modifiedMaterial = null;
|
||||
_currentMaterialForRendering = null;
|
||||
MaterialRepository.Release(ref _modifiedMaterial);
|
||||
_materialForRendering = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,17 +145,14 @@ namespace Coffee.UIExtensions
|
||||
hideFlags = HideFlags.HideAndDontSave
|
||||
};
|
||||
}
|
||||
|
||||
_currentMaterialForRendering = null;
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
ModifiedMaterial.Remove(_modifiedMaterial);
|
||||
_modifiedMaterial = null;
|
||||
_currentMaterialForRendering = null;
|
||||
MaterialRepository.Release(ref _modifiedMaterial);
|
||||
_materialForRendering = null;
|
||||
}
|
||||
|
||||
public static UIParticleRenderer AddRenderer(UIParticle parent, int index)
|
||||
@@ -177,12 +184,9 @@ namespace Coffee.UIExtensions
|
||||
/// </summary>
|
||||
public override Material GetModifiedMaterial(Material baseMaterial)
|
||||
{
|
||||
_currentMaterialForRendering = null;
|
||||
|
||||
if (!IsActive() || !_parent)
|
||||
{
|
||||
ModifiedMaterial.Remove(_modifiedMaterial);
|
||||
_modifiedMaterial = null;
|
||||
MaterialRepository.Release(ref _modifiedMaterial);
|
||||
return baseMaterial;
|
||||
}
|
||||
|
||||
@@ -192,23 +196,31 @@ namespace Coffee.UIExtensions
|
||||
var texture = mainTexture;
|
||||
if (texture == null && _parent.m_AnimatableProperties.Length == 0)
|
||||
{
|
||||
ModifiedMaterial.Remove(_modifiedMaterial);
|
||||
_modifiedMaterial = null;
|
||||
MaterialRepository.Release(ref _modifiedMaterial);
|
||||
return modifiedMaterial;
|
||||
}
|
||||
|
||||
//
|
||||
var id = _parent.m_AnimatableProperties.Length == 0 ? 0 : GetInstanceID();
|
||||
var hash = new Hash128(
|
||||
modifiedMaterial ? (uint)modifiedMaterial.GetInstanceID() : 0,
|
||||
texture ? (uint)texture.GetInstanceID() : 0,
|
||||
0 < _parent.m_AnimatableProperties.Length ? (uint)GetInstanceID() : 0,
|
||||
#if UNITY_EDITOR
|
||||
var props = EditorJsonUtility.ToJson(modifiedMaterial).GetHashCode();
|
||||
(uint)EditorJsonUtility.ToJson(modifiedMaterial).GetHashCode()
|
||||
#else
|
||||
var props = 0;
|
||||
0
|
||||
#endif
|
||||
modifiedMaterial = ModifiedMaterial.Add(modifiedMaterial, texture, id, props);
|
||||
ModifiedMaterial.Remove(_modifiedMaterial);
|
||||
_modifiedMaterial = modifiedMaterial;
|
||||
);
|
||||
if (!MaterialRepository.Valid(hash, _modifiedMaterial))
|
||||
{
|
||||
MaterialRepository.Get(hash, ref _modifiedMaterial, x => new Material(x.mat)
|
||||
{
|
||||
hideFlags = HideFlags.HideAndDontSave,
|
||||
mainTexture = x.texture ? x.texture : x.mat.mainTexture
|
||||
}, (mat: modifiedMaterial, texture));
|
||||
}
|
||||
|
||||
return modifiedMaterial;
|
||||
return _modifiedMaterial;
|
||||
}
|
||||
|
||||
public void Set(UIParticle parent, ParticleSystem ps, bool isTrail)
|
||||
@@ -219,20 +231,20 @@ namespace Coffee.UIExtensions
|
||||
gameObject.layer = parent.gameObject.layer;
|
||||
|
||||
_particleSystem = ps;
|
||||
_prewarm = _particleSystem.main.prewarm;
|
||||
_preWarm = _particleSystem.main.prewarm;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isPlaying)
|
||||
#endif
|
||||
{
|
||||
if (_particleSystem.isPlaying || _prewarm)
|
||||
if (_particleSystem.isPlaying || _preWarm)
|
||||
{
|
||||
_particleSystem.Clear();
|
||||
_particleSystem.Pause();
|
||||
}
|
||||
}
|
||||
|
||||
_renderer = ps.GetComponent<ParticleSystemRenderer>();
|
||||
ps.TryGetComponent(out _renderer);
|
||||
_renderer.enabled = false;
|
||||
|
||||
//_emitter = emitter;
|
||||
@@ -404,97 +416,69 @@ namespace Coffee.UIExtensions
|
||||
_lastBounds = bounds;
|
||||
|
||||
// Convert linear color to gamma color.
|
||||
if (QualitySettings.activeColorSpace == ColorSpace.Linear)
|
||||
if (UIParticleProjectSettings.enableLinearToGamma && canvas.ShouldGammaToLinearInMesh())
|
||||
{
|
||||
Profiler.BeginSample("[UIParticleRenderer] Convert Linear to Gamma");
|
||||
workerMesh.GetColors(s_Colors);
|
||||
var count_c = s_Colors.Count;
|
||||
for (var i = 0; i < count_c; i++)
|
||||
{
|
||||
var c = s_Colors[i];
|
||||
c.r = c.r.LinearToGamma();
|
||||
c.g = c.g.LinearToGamma();
|
||||
c.b = c.b.LinearToGamma();
|
||||
s_Colors[i] = c;
|
||||
workerMesh.LinearToGamma();
|
||||
}
|
||||
|
||||
workerMesh.SetColors(s_Colors);
|
||||
Profiler.EndSample();
|
||||
}
|
||||
var components = ListPool<Component>.Rent();
|
||||
GetComponents(typeof(IMeshModifier), components);
|
||||
|
||||
GetComponents(typeof(IMeshModifier), s_Components);
|
||||
for (var i = 0; i < s_Components.Count; i++)
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
((IMeshModifier)s_Components[i]).ModifyMesh(workerMesh);
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
for (var i = 0; i < components.Count; i++)
|
||||
{
|
||||
((IMeshModifier)components[i]).ModifyMesh(workerMesh);
|
||||
}
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
s_Components.Clear();
|
||||
ListPool<Component>.Return(ref components);
|
||||
}
|
||||
|
||||
Profiler.EndSample();
|
||||
|
||||
// Update animatable material properties.
|
||||
Profiler.BeginSample("[UIParticleRenderer] Update Animatable Material Properties");
|
||||
UpdateMaterialProperties();
|
||||
Profiler.EndSample();
|
||||
|
||||
// Get grouped renderers.
|
||||
Profiler.BeginSample("[UIParticleRenderer] Set Mesh");
|
||||
s_Renderers.Clear();
|
||||
if (_parent.useMeshSharing)
|
||||
{
|
||||
UIParticleUpdater.GetGroupedRenderers(_parent.groupId, _index, s_Renderers);
|
||||
}
|
||||
|
||||
// Set mesh to the CanvasRenderer.
|
||||
Profiler.BeginSample("[UIParticleRenderer] Set Mesh");
|
||||
for (var i = 0; i < s_Renderers.Count; i++)
|
||||
{
|
||||
if (s_Renderers[i] == this) continue;
|
||||
|
||||
s_Renderers[i].canvasRenderer.SetMesh(workerMesh);
|
||||
s_Renderers[i]._lastBounds = _lastBounds;
|
||||
s_Renderers[i].canvasRenderer.materialCount = 1;
|
||||
s_Renderers[i].canvasRenderer.SetMaterial(materialForRendering, 0);
|
||||
}
|
||||
|
||||
if (!_parent.canRender)
|
||||
if (_parent.canRender)
|
||||
{
|
||||
canvasRenderer.SetMesh(workerMesh);
|
||||
}
|
||||
else
|
||||
{
|
||||
workerMesh.Clear();
|
||||
}
|
||||
|
||||
canvasRenderer.SetMesh(workerMesh);
|
||||
Profiler.EndSample();
|
||||
|
||||
// Update animatable material properties.
|
||||
Profiler.BeginSample("[UIParticleRenderer] Update Animatable Material Properties");
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (_modifiedMaterial != material)
|
||||
{
|
||||
_renderer.GetSharedMaterials(s_Materials);
|
||||
material = s_Materials[_isTrail ? 1 : 0];
|
||||
s_Materials.Clear();
|
||||
SetMaterialDirty();
|
||||
}
|
||||
#endif
|
||||
|
||||
UpdateMaterialProperties();
|
||||
if (_parent.useMeshSharing)
|
||||
{
|
||||
if (!_currentMaterialForRendering)
|
||||
{
|
||||
_currentMaterialForRendering = materialForRendering;
|
||||
}
|
||||
|
||||
for (var i = 0; i < s_Renderers.Count; i++)
|
||||
{
|
||||
if (s_Renderers[i] == this) continue;
|
||||
|
||||
s_Renderers[i].canvasRenderer.materialCount = 1;
|
||||
s_Renderers[i].canvasRenderer.SetMaterial(_currentMaterialForRendering, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Profiler.EndSample();
|
||||
|
||||
s_Renderers.Clear();
|
||||
}
|
||||
|
||||
public override void SetMaterialDirty()
|
||||
{
|
||||
_materialForRendering = null;
|
||||
base.SetMaterialDirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call to update the geometry of the Graphic onto the CanvasRenderer.
|
||||
/// </summary>
|
||||
@@ -561,10 +545,7 @@ namespace Coffee.UIExtensions
|
||||
return Matrix4x4.Scale(scale);
|
||||
case ParticleSystemSimulationSpace.Custom:
|
||||
return Matrix4x4.Translate(_particleSystem.main.customSimulationSpace.position.GetScaled(scale))
|
||||
//* Matrix4x4.Translate(wpos)
|
||||
* Matrix4x4.Scale(scale)
|
||||
//* Matrix4x4.Translate(-wpos)
|
||||
;
|
||||
* Matrix4x4.Scale(scale);
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
@@ -580,7 +561,8 @@ namespace Coffee.UIExtensions
|
||||
var screenSize = new Vector2Int(Screen.width, Screen.height);
|
||||
var isWorldSpace = _particleSystem.IsWorldSpace();
|
||||
var canvasScale = _parent.canvas ? _parent.canvas.scaleFactor : 1f;
|
||||
var resolutionChanged = _prevScreenSize != screenSize || _prevCanvasScale != canvasScale;
|
||||
var resolutionChanged = _prevScreenSize != screenSize
|
||||
|| !Mathf.Approximately(_prevCanvasScale, canvasScale);
|
||||
if (resolutionChanged && isWorldSpace)
|
||||
{
|
||||
// Update particle array size and get particles.
|
||||
@@ -588,7 +570,7 @@ namespace Coffee.UIExtensions
|
||||
var particles = ParticleSystemExtensions.GetParticleArray(size);
|
||||
_particleSystem.GetParticles(particles, size);
|
||||
|
||||
// Resolusion resolver:
|
||||
// Resolution resolver:
|
||||
// (psPos / scale) / (prevPsPos / prevScale) -> psPos * scale.inv * prevPsPos.inv * prevScale
|
||||
var modifier = psPos.GetScaled(
|
||||
scale.Inverse(),
|
||||
@@ -622,11 +604,11 @@ namespace Coffee.UIExtensions
|
||||
? Time.unscaledDeltaTime
|
||||
: Time.deltaTime;
|
||||
|
||||
// Prewarm:
|
||||
if (0 < deltaTime && _prewarm)
|
||||
// Pre-warm:
|
||||
if (0 < deltaTime && _preWarm)
|
||||
{
|
||||
deltaTime += main.duration;
|
||||
_prewarm = false;
|
||||
_preWarm = false;
|
||||
}
|
||||
|
||||
// get world position.
|
||||
@@ -698,12 +680,12 @@ namespace Coffee.UIExtensions
|
||||
if (s_Mpb.isEmpty) return;
|
||||
|
||||
// #41: Copy the value from MaterialPropertyBlock to CanvasRenderer
|
||||
if (!_modifiedMaterial) return;
|
||||
if (!materialForRendering) return;
|
||||
|
||||
for (var i = 0; i < _parent.m_AnimatableProperties.Length; i++)
|
||||
{
|
||||
var ap = _parent.m_AnimatableProperties[i];
|
||||
ap.UpdateMaterialProperties(_modifiedMaterial, s_Mpb);
|
||||
ap.UpdateMaterialProperties(materialForRendering, s_Mpb);
|
||||
}
|
||||
|
||||
s_Mpb.Clear();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using Coffee.UIParticleInternal;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -39,12 +40,12 @@ namespace Coffee.UIExtensions
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[InitializeOnLoadMethod]
|
||||
#else
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||
#endif
|
||||
[RuntimeInitializeOnLoadMethod]
|
||||
private static void InitializeOnLoad()
|
||||
{
|
||||
Canvas.willRenderCanvases -= Refresh;
|
||||
Canvas.willRenderCanvases += Refresh;
|
||||
UIExtraCallbacks.onAfterCanvasRebuild += Refresh;
|
||||
}
|
||||
|
||||
private static void Refresh()
|
||||
|
||||
8
Runtime/Utilities.meta
Normal file
8
Runtime/Utilities.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66c42f0f30de84ca4bd8305a1188af85
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,96 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Coffee.UIParticleExtensions
|
||||
namespace Coffee.UIParticleInternal
|
||||
{
|
||||
public static class Color32Extensions
|
||||
{
|
||||
private static byte[] s_LinearToGammaLut;
|
||||
|
||||
public static byte LinearToGamma(this byte self)
|
||||
{
|
||||
if (s_LinearToGammaLut == null)
|
||||
{
|
||||
s_LinearToGammaLut = new byte[256];
|
||||
for (var i = 0; i < 256; i++)
|
||||
{
|
||||
s_LinearToGammaLut[i] = (byte)(Mathf.LinearToGammaSpace(i / 255f) * 255f);
|
||||
}
|
||||
}
|
||||
|
||||
return s_LinearToGammaLut[self];
|
||||
}
|
||||
}
|
||||
|
||||
public static class Vector3Extensions
|
||||
{
|
||||
public static Vector3 Inverse(this Vector3 self)
|
||||
{
|
||||
self.x = Mathf.Approximately(self.x, 0) ? 1 : 1 / self.x;
|
||||
self.y = Mathf.Approximately(self.y, 0) ? 1 : 1 / self.y;
|
||||
self.z = Mathf.Approximately(self.z, 0) ? 1 : 1 / self.z;
|
||||
return self;
|
||||
}
|
||||
|
||||
public static Vector3 GetScaled(this Vector3 self, Vector3 other1)
|
||||
{
|
||||
self.Scale(other1);
|
||||
return self;
|
||||
}
|
||||
|
||||
public static Vector3 GetScaled(this Vector3 self, Vector3 other1, Vector3 other2)
|
||||
{
|
||||
self.Scale(other1);
|
||||
self.Scale(other2);
|
||||
return self;
|
||||
}
|
||||
|
||||
public static Vector3 GetScaled(this Vector3 self, Vector3 other1, Vector3 other2, Vector3 other3)
|
||||
{
|
||||
self.Scale(other1);
|
||||
self.Scale(other2);
|
||||
self.Scale(other3);
|
||||
return self;
|
||||
}
|
||||
|
||||
public static bool IsVisible(this Vector3 self)
|
||||
{
|
||||
return 0 < Mathf.Abs(self.x * self.y * self.z);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class SpriteExtensions
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
private static readonly Type s_SpriteEditorExtensionType =
|
||||
Type.GetType("UnityEditor.Experimental.U2D.SpriteEditorExtension, UnityEditor")
|
||||
?? Type.GetType("UnityEditor.U2D.SpriteEditorExtension, UnityEditor");
|
||||
|
||||
private static readonly MethodInfo s_GetActiveAtlasTextureMethodInfo = s_SpriteEditorExtensionType
|
||||
.GetMethod("GetActiveAtlasTexture", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
|
||||
public static Texture2D GetActualTexture(this Sprite self)
|
||||
{
|
||||
if (!self) return null;
|
||||
|
||||
if (Application.isPlaying) return self.texture;
|
||||
var ret = s_GetActiveAtlasTextureMethodInfo.Invoke(null, new object[] { self }) as Texture2D;
|
||||
return ret
|
||||
? ret
|
||||
: self.texture;
|
||||
}
|
||||
#else
|
||||
internal static Texture2D GetActualTexture(this Sprite self)
|
||||
{
|
||||
return self ? self.texture : null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public static class ParticleSystemExtensions
|
||||
internal static class ParticleSystemExtensions
|
||||
{
|
||||
private static ParticleSystem.Particle[] s_TmpParticles = new ParticleSystem.Particle[2048];
|
||||
|
||||
@@ -250,59 +165,11 @@ namespace Coffee.UIParticleExtensions
|
||||
|
||||
public static void Exec(this List<ParticleSystem> self, Action<ParticleSystem> action)
|
||||
{
|
||||
self.RemoveAll(p => !p);
|
||||
self.ForEach(action);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class Misc
|
||||
foreach (var p in self)
|
||||
{
|
||||
public static void Destroy(Object obj)
|
||||
{
|
||||
if (!obj) return;
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
Object.DestroyImmediate(obj);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
Object.Destroy(obj);
|
||||
if (!p) continue;
|
||||
action.Invoke(p);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DestroyImmediate(Object obj)
|
||||
{
|
||||
if (!obj) return;
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isEditor)
|
||||
{
|
||||
Object.DestroyImmediate(obj);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
Object.Destroy(obj);
|
||||
}
|
||||
}
|
||||
|
||||
#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
|
||||
{
|
||||
if (!self) return null;
|
||||
if (!includeInactive) return self.GetComponentInParent<T>();
|
||||
|
||||
var current = self.transform;
|
||||
while (current)
|
||||
{
|
||||
var component = current.GetComponent<T>();
|
||||
if (component) return component;
|
||||
current = current.parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
11
Runtime/Utilities/ParticleSystemExtensions.cs.meta
Normal file
11
Runtime/Utilities/ParticleSystemExtensions.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e51604bfb810e44519e2710fd1b8af90
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "com.coffee.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.",
|
||||
"version": "4.6.6",
|
||||
"version": "5.0.0-preview.1",
|
||||
"unity": "2018.2",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
Reference in New Issue
Block a user