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
24 Commits
5.0.0-prev
...
5.0.0-prev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b068238e0a | ||
|
|
42fb0ecf23 | ||
|
|
79635e81d7 | ||
|
|
f388eb45ec | ||
|
|
f08809772a | ||
|
|
f96604ffc9 | ||
|
|
beac3b8048 | ||
|
|
a00bcd8a35 | ||
|
|
1f72048ef5 | ||
|
|
65f7555482 | ||
|
|
433f828ad9 | ||
|
|
1b8f0aac4a | ||
|
|
70271ae5e9 | ||
|
|
c3540a056c | ||
|
|
0cf4ae88c5 | ||
|
|
e579f524ba | ||
|
|
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": [
|
||||
|
||||
40
CHANGELOG.md
40
CHANGELOG.md
@@ -1,3 +1,43 @@
|
||||
# [5.0.0-preview.3](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v5.0.0-preview.2...v5.0.0-preview.3) (2024-06-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* generated baking-camera object remains in the prefab or scene ([fd66928](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/fd66928efe584aeb4f6347b9a9dca97d9512eb88))
|
||||
|
||||
# [5.0.0-preview.2](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v5.0.0-preview.1...v5.0.0-preview.2) (2024-06-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 'Resource ID out of range in GetResource' error in overlay rendering mode ([ff78b6f](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/ff78b6fe32ceed8ddad50e63dcb7a202eab95266)), closes [#308](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/308)
|
||||
* `UIParticle.transform.localScale` does not scale particles ([491ee7b](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/491ee7b0c3e528e1e577ae5ff2588d7c3bd8ecdb)), closes [#313](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/313)
|
||||
* despite not using the size module, particles become smaller based on their z position ([c96ddf2](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/c96ddf293e855f7ebccaaaf3b112092955545e61)), closes [#316](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/316)
|
||||
* the ParticleSystem's localPosition drifts at certain scales due to floating-point precision issues ([a9c2b19](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/a9c2b19edf00e1c86c928ef23405906952ede852)), closes [#299](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/299) [#312](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/312)
|
||||
* UIParticle is scaled by canvas size even when `AutoScalingMode.None` and `ScalingMode.Local` ([63b24d8](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/63b24d8a8b478b3165733ece3eec524e88b28855)), closes [#313](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/313)
|
||||
* UIParticle is scaled incorrectly with nested canvases ([c95d8c6](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/c95d8c6b1774396ff252d13121ad32a3cab0fe5c)), closes [#313](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/313)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* remove overlay window (editor) ([fc3fbdd](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/fc3fbdd230595ad3471875ec6fec384a3dad0d17))
|
||||
* reset previous position on start play for world space simulation ([e741584](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/e7415845074143abae23e3ae7eedc767a01d020d)), closes [#303](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/303)
|
||||
* restore `Transform.localScale` when setting `autoScalingMode` to something other than `Transform` ([dfb94f4](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/dfb94f4badfb3035a4374579c53293460b4fd946))
|
||||
|
||||
# [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,31 +26,8 @@ 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
|
||||
[Overlay(typeof(SceneView), "Scene View/UI Particles", "UI Particles", true,
|
||||
defaultDockPosition = DockPosition.Bottom,
|
||||
defaultDockZone = DockZone.Floating,
|
||||
defaultLayout = Layout.Panel)]
|
||||
#else
|
||||
[Overlay(typeof(SceneView), "Scene View/UI Particles", "UI Particles", true)]
|
||||
#endif
|
||||
private class UIParticleOverlay : IMGUIOverlay, ITransientOverlay
|
||||
{
|
||||
public bool visible => s_SerializedObject != null;
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
if (visible)
|
||||
{
|
||||
WindowFunction();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//################################
|
||||
// Constant or Static Members.
|
||||
//################################
|
||||
@@ -60,7 +39,6 @@ namespace Coffee.UIExtensions
|
||||
private static readonly GUIContent s_Content3D = new GUIContent("3D");
|
||||
private static readonly GUIContent s_ContentRandom = new GUIContent("Random");
|
||||
private static readonly GUIContent s_ContentScale = new GUIContent("Scale");
|
||||
private static SerializedObject s_SerializedObject;
|
||||
private static bool s_XYZMode;
|
||||
|
||||
private SerializedProperty _maskable;
|
||||
@@ -88,66 +66,14 @@ namespace Coffee.UIExtensions
|
||||
"_ColorMask"
|
||||
};
|
||||
|
||||
[InitializeOnLoadMethod]
|
||||
private static void Init()
|
||||
{
|
||||
#if !UNITY_2021_2_OR_NEWER
|
||||
var miSceneViewOverlayWindow = Type.GetType("UnityEditor.SceneViewOverlay, UnityEditor")
|
||||
?.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
||||
.First(x => x.Name == "Window" && 5 <= x.GetParameters().Length);
|
||||
var windowFunction = (Action<Object, SceneView>)WindowFunction;
|
||||
var windowFunctionType = Type.GetType("UnityEditor.SceneViewOverlay+WindowFunction, UnityEditor");
|
||||
var windowFunctionDelegate = Delegate.CreateDelegate(windowFunctionType, windowFunction.Method);
|
||||
var windowTitle = new GUIContent(ObjectNames.NicifyVariableName(nameof(UIParticle)));
|
||||
#if UNITY_2019_2_OR_NEWER
|
||||
//public static void Window(GUIContent title, WindowFunction sceneViewFunc, int order, Object target, WindowDisplayOption option, EditorWindow window = null)
|
||||
var sceneViewArgs = new object[] { windowTitle, windowFunctionDelegate, 599, null, 2, null };
|
||||
#else
|
||||
//public static void Window(GUIContent title, WindowFunction sceneViewFunc, int order, Object target, WindowDisplayOption option)
|
||||
var sceneViewArgs = new object[] { windowTitle, windowFunctionDelegate, 599, null, 2 };
|
||||
#endif
|
||||
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
SceneView.duringSceneGui += _ =>
|
||||
#else
|
||||
SceneView.onSceneGUIDelegate += _ =>
|
||||
#endif
|
||||
{
|
||||
if (s_SerializedObject != null)
|
||||
{
|
||||
miSceneViewOverlayWindow.Invoke(null, sceneViewArgs);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
SerializedObject CreateSerializeObject()
|
||||
{
|
||||
var uiParticles = Selection.gameObjects.Select(x => x.GetComponent<ParticleSystem>())
|
||||
.Where(x => x)
|
||||
.Select(x => x.GetComponentInParent<UIParticle>(true))
|
||||
.Where(x => x && x.canvas)
|
||||
.Concat(Selection.gameObjects.Select(x => x.GetComponent<UIParticle>())
|
||||
.Where(x => x && x.canvas))
|
||||
.Distinct()
|
||||
.OfType<Object>()
|
||||
.ToArray();
|
||||
return 0 < uiParticles.Length ? new SerializedObject(uiParticles) : null;
|
||||
}
|
||||
|
||||
s_SerializedObject = CreateSerializeObject();
|
||||
Selection.selectionChanged += () => s_SerializedObject = CreateSerializeObject();
|
||||
}
|
||||
|
||||
//################################
|
||||
// Public/Protected Members.
|
||||
//################################
|
||||
/// <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");
|
||||
@@ -475,61 +401,14 @@ namespace Coffee.UIExtensions
|
||||
|
||||
private static void DrawAutoScaling(SerializedProperty prop, IEnumerable<UIParticle> uiParticles)
|
||||
{
|
||||
var isTransformMode = prop.intValue == (int)UIParticle.AutoScalingMode.Transform;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(prop);
|
||||
if (!EditorGUI.EndChangeCheck() || !isTransformMode) return;
|
||||
|
||||
// on changed true->false, reset scale.
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
foreach (var uip in uiParticles)
|
||||
{
|
||||
if (!uip) continue;
|
||||
uip.transform.localScale = Vector3.one;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
private static void WindowFunction()
|
||||
#else
|
||||
private static void WindowFunction(Object _, SceneView __)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
if (s_SerializedObject == null || !s_SerializedObject.targetObject) return;
|
||||
var uiParticles = s_SerializedObject.targetObjects.OfType<UIParticle>().ToArray();
|
||||
if (uiParticles.Any(x => !x || !x.canvas)) return;
|
||||
|
||||
s_SerializedObject.Update();
|
||||
using (new EditorGUILayout.VerticalScope(GUILayout.Width(220f)))
|
||||
{
|
||||
var labelWidth = EditorGUIUtility.labelWidth;
|
||||
EditorGUIUtility.labelWidth = 100;
|
||||
EditorGUILayout.PropertyField(s_SerializedObject.FindProperty("m_Enabled"));
|
||||
s_XYZMode = DrawFloatOrVector3Field(s_SerializedObject.FindProperty("m_Scale3D"), s_XYZMode);
|
||||
EditorGUILayout.PropertyField(s_SerializedObject.FindProperty("m_PositionMode"));
|
||||
DrawAutoScaling(s_SerializedObject.FindProperty("m_AutoScalingMode"), uiParticles);
|
||||
EditorGUIUtility.labelWidth = labelWidth;
|
||||
}
|
||||
|
||||
s_SerializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private void DestroyUIParticle(UIParticle p, bool ignoreCurrent = false)
|
||||
{
|
||||
if (!p || (ignoreCurrent && target == p)) return;
|
||||
|
||||
var cr = p.canvasRenderer;
|
||||
DestroyImmediate(p);
|
||||
DestroyImmediate(cr);
|
||||
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
var stage = PrefabStageUtility.GetCurrentPrefabStage();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright 2018-2023 mob-sakai
|
||||
Copyright 2018-2024 mob-sakai
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
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
|
||||
198
Runtime/Internal/Extensions/ComponentExtensions.cs
Normal file
198
Runtime/Internal/Extensions/ComponentExtensions.cs
Normal file
@@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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 components in children of a specific type in the hierarchy of a GameObject.
|
||||
/// </summary>
|
||||
public static T[] GetComponentsInChildren<T>(this Component self, int depth)
|
||||
where T : Component
|
||||
{
|
||||
var results = ListPool<T>.Rent();
|
||||
self.GetComponentsInChildren_Internal(results, depth);
|
||||
var array = results.ToArray();
|
||||
ListPool<T>.Return(ref results);
|
||||
return array;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get components in children of a specific type in the hierarchy of a GameObject.
|
||||
/// </summary>
|
||||
public static void GetComponentsInChildren<T>(this Component self, List<T> results, int depth)
|
||||
where T : Component
|
||||
{
|
||||
results.Clear();
|
||||
self.GetComponentsInChildren_Internal(results, depth);
|
||||
}
|
||||
|
||||
private static void GetComponentsInChildren_Internal<T>(this Component self, List<T> results, int depth)
|
||||
where T : Component
|
||||
{
|
||||
if (!self || results == null || depth < 0) return;
|
||||
|
||||
var tr = self.transform;
|
||||
if (tr.TryGetComponent<T>(out var t))
|
||||
{
|
||||
results.Add(t);
|
||||
}
|
||||
|
||||
if (depth - 1 < 0) return;
|
||||
var childCount = tr.childCount;
|
||||
for (var i = 0; i < childCount; i++)
|
||||
{
|
||||
tr.GetChild(i).GetComponentsInChildren(results, depth - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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:
|
||||
259
Runtime/Internal/Utilities/Logging.cs
Normal file
259
Runtime/Internal/Utilities/Logging.cs
Normal file
@@ -0,0 +1,259 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
#if ENABLE_COFFEE_LOGGER
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#else
|
||||
using Conditional = System.Diagnostics.ConditionalAttribute;
|
||||
#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:
|
||||
93
Runtime/Internal/Utilities/MaterialRepository.cs
Normal file
93
Runtime/Internal/Utilities/MaterialRepository.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
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(Hash128 hash, ref Material material, string shaderName)
|
||||
{
|
||||
Profiler.BeginSample("(COF)[MaterialRepository] Get");
|
||||
s_Repository.Get(hash, ref material, x => new Material(Shader.Find(x))
|
||||
{
|
||||
hideFlags = HideFlags.DontSave | HideFlags.NotEditable
|
||||
}, shaderName);
|
||||
Profiler.EndSample();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds or retrieves a cached material based on the hash.
|
||||
/// </summary>
|
||||
public static void Get(Hash128 hash, ref Material material, string shaderName, string[] keywords)
|
||||
{
|
||||
Profiler.BeginSample("(COF)[MaterialRepository] Get");
|
||||
s_Repository.Get(hash, ref material, x => new Material(Shader.Find(x.shaderName))
|
||||
{
|
||||
hideFlags = HideFlags.DontSave | HideFlags.NotEditable,
|
||||
shaderKeywords = x.keywords
|
||||
}, (shaderName, keywords));
|
||||
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,56 @@ 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 _bakeCamera;
|
||||
private DrivenRectTransformTracker _tracker;
|
||||
private Vector3 _storedScale;
|
||||
private bool _isScaleStored;
|
||||
|
||||
public RectTransform rectTransform => transform as RectTransform;
|
||||
|
||||
public Canvas canvas
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_canvas) return _canvas;
|
||||
|
||||
var tr = transform;
|
||||
while (tr && !_canvas)
|
||||
{
|
||||
if (tr.TryGetComponent(out _canvas)) return _canvas;
|
||||
tr = tr.parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Should this graphic be considered a target for ray-casting?
|
||||
/// Does this graphic allow masking.
|
||||
/// </summary>
|
||||
public override bool raycastTarget
|
||||
public bool maskable
|
||||
{
|
||||
get => false;
|
||||
set { }
|
||||
get => m_Maskable;
|
||||
set
|
||||
{
|
||||
if (value == m_Maskable) return;
|
||||
m_Maskable = value;
|
||||
UpdateRendererMaterial();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -201,7 +234,12 @@ namespace Coffee.UIExtensions
|
||||
{
|
||||
if (m_AutoScalingMode == value) return;
|
||||
m_AutoScalingMode = value;
|
||||
UpdateTracker();
|
||||
|
||||
if (autoScalingMode != AutoScalingMode.Transform && _isScaleStored)
|
||||
{
|
||||
transform.localScale = _storedScale;
|
||||
_isScaleStored = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,9 +282,9 @@ namespace Coffee.UIExtensions
|
||||
/// <summary>
|
||||
/// Particle effect scale.
|
||||
/// </summary>
|
||||
public Vector3 scale3DForCalc => autoScalingMode == AutoScalingMode.UIParticle
|
||||
? m_Scale3D.GetScaled(canvasScale)
|
||||
: m_Scale3D;
|
||||
public Vector3 scale3DForCalc => autoScalingMode == AutoScalingMode.Transform
|
||||
? m_Scale3D
|
||||
: m_Scale3D.GetScaled(canvasScale, transform.localScale);
|
||||
|
||||
public List<ParticleSystem> particles => m_Particles;
|
||||
|
||||
@@ -266,8 +304,6 @@ namespace Coffee.UIExtensions
|
||||
}
|
||||
}
|
||||
|
||||
public override Material materialForRendering => null;
|
||||
|
||||
/// <summary>
|
||||
/// Paused.
|
||||
/// </summary>
|
||||
@@ -275,15 +311,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()
|
||||
{
|
||||
_isScaleStored = false;
|
||||
ResetGroupId();
|
||||
UpdateTracker();
|
||||
UIParticleUpdater.Register(this);
|
||||
RegisterDirtyMaterialCallback(UpdateRendererMaterial);
|
||||
|
||||
//
|
||||
if (0 < particles.Count)
|
||||
{
|
||||
RefreshParticles(particles);
|
||||
@@ -293,7 +329,7 @@ namespace Coffee.UIExtensions
|
||||
RefreshParticles();
|
||||
}
|
||||
|
||||
base.OnEnable();
|
||||
UpdateRendererMaterial();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -301,12 +337,24 @@ namespace Coffee.UIExtensions
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
UpdateTracker();
|
||||
_tracker.Clear();
|
||||
if (autoScalingMode == AutoScalingMode.Transform && _isScaleStored)
|
||||
{
|
||||
transform.localScale = _storedScale;
|
||||
}
|
||||
|
||||
_isScaleStored = false;
|
||||
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,13 +364,13 @@ namespace Coffee.UIExtensions
|
||||
{
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate()
|
||||
/// <summary>
|
||||
/// This function is called when a direct or indirect parent of the transform of the GameObject has changed.
|
||||
/// </summary>
|
||||
protected override void OnTransformParentChanged()
|
||||
{
|
||||
base.OnValidate();
|
||||
UpdateTracker();
|
||||
_canvas = null;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ISerializationCallbackReceiver.OnBeforeSerialize()
|
||||
{
|
||||
@@ -330,6 +378,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 +391,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 +441,9 @@ namespace Coffee.UIExtensions
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop emission of the ParticleSystems.
|
||||
/// </summary>
|
||||
public void StopEmission()
|
||||
{
|
||||
particles.Exec(p =>
|
||||
@@ -385,24 +453,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 +495,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 +506,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 +545,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);
|
||||
@@ -482,12 +587,26 @@ namespace Coffee.UIExtensions
|
||||
|
||||
internal void UpdateTransformScale()
|
||||
{
|
||||
_tracker.Clear();
|
||||
canvasScale = canvas.rootCanvas.transform.localScale.Inverse();
|
||||
parentScale = transform.parent.lossyScale;
|
||||
if (autoScalingMode != AutoScalingMode.Transform) return;
|
||||
if (autoScalingMode != AutoScalingMode.Transform)
|
||||
{
|
||||
if (_isScaleStored)
|
||||
{
|
||||
transform.localScale = _storedScale;
|
||||
}
|
||||
|
||||
_isScaleStored = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var currentScale = transform.localScale;
|
||||
_storedScale = currentScale;
|
||||
_isScaleStored = true;
|
||||
_tracker.Add(this, rectTransform, DrivenTransformProperties.Scale);
|
||||
var newScale = parentScale.Inverse();
|
||||
if (transform.localScale != newScale)
|
||||
if (currentScale != newScale)
|
||||
{
|
||||
transform.localScale = newScale;
|
||||
}
|
||||
@@ -500,11 +619,10 @@ namespace Coffee.UIExtensions
|
||||
for (var i = 0; i < _renderers.Count; i++)
|
||||
{
|
||||
var r = _renderers[i];
|
||||
if (!r)
|
||||
{
|
||||
RefreshParticles(particles);
|
||||
break;
|
||||
}
|
||||
if (r) continue;
|
||||
|
||||
RefreshParticles(particles);
|
||||
break;
|
||||
}
|
||||
|
||||
var bakeCamera = GetBakeCamera();
|
||||
@@ -512,6 +630,7 @@ namespace Coffee.UIExtensions
|
||||
{
|
||||
var r = _renderers[i];
|
||||
if (!r) continue;
|
||||
|
||||
r.UpdateMesh(bakeCamera);
|
||||
}
|
||||
}
|
||||
@@ -523,17 +642,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++)
|
||||
@@ -563,64 +671,46 @@ namespace Coffee.UIExtensions
|
||||
private Camera GetBakeCamera()
|
||||
{
|
||||
if (!canvas) return Camera.main;
|
||||
if (_bakeCamera) return _bakeCamera;
|
||||
|
||||
// When render mode is ScreenSpaceCamera or WorldSpace, use world camera.
|
||||
var root = canvas.rootCanvas;
|
||||
if (root.renderMode != RenderMode.ScreenSpaceOverlay)
|
||||
// Find existing baking camera.
|
||||
var childCount = transform.childCount;
|
||||
for (var i = 0; i < childCount; i++)
|
||||
{
|
||||
return root.worldCamera ? root.worldCamera : Camera.main;
|
||||
}
|
||||
|
||||
// When render mode is ScreenSpaceOverlay, use ortho-camera.
|
||||
if (!_orthoCamera)
|
||||
{
|
||||
// Find existing ortho-camera.
|
||||
foreach (Transform child in transform)
|
||||
if (transform.GetChild(i).TryGetComponent<Camera>(out var cam)
|
||||
&& cam.name == "[generated] UIParticle BakingCamera")
|
||||
{
|
||||
var cam = child.GetComponent<Camera>();
|
||||
if (cam && cam.name == "[generated] UIParticleOverlayCamera")
|
||||
{
|
||||
_orthoCamera = cam;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create ortho-camera.
|
||||
if (!_orthoCamera)
|
||||
{
|
||||
var go = new GameObject("[generated] UIParticleOverlayCamera")
|
||||
{
|
||||
hideFlags = HideFlags.HideAndDontSave
|
||||
};
|
||||
go.SetActive(false);
|
||||
go.transform.SetParent(transform, false);
|
||||
_orthoCamera = go.AddComponent<Camera>();
|
||||
_orthoCamera.enabled = false;
|
||||
_bakeCamera = cam;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
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;
|
||||
|
||||
return _orthoCamera;
|
||||
}
|
||||
|
||||
private void UpdateTracker()
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (!enabled || !autoScaling || autoScalingMode != AutoScalingMode.Transform)
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
// Create baking camera.
|
||||
if (!_bakeCamera)
|
||||
{
|
||||
_tracker.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
_tracker.Add(this, rectTransform, DrivenTransformProperties.Scale);
|
||||
var go = new GameObject("[generated] UIParticle BakingCamera")
|
||||
{
|
||||
hideFlags = HideFlags.HideAndDontSave
|
||||
};
|
||||
go.SetActive(false);
|
||||
go.transform.SetParent(transform, false);
|
||||
_bakeCamera = go.AddComponent<Camera>();
|
||||
}
|
||||
|
||||
// Setup baking camera.
|
||||
_bakeCamera.enabled = false;
|
||||
_bakeCamera.orthographicSize = 1000;
|
||||
_bakeCamera.transform.SetPositionAndRotation(new Vector3(0, 0, -1000), Quaternion.identity);
|
||||
_bakeCamera.orthographic = true;
|
||||
_bakeCamera.farClipPlane = 2000f;
|
||||
_bakeCamera.clearFlags = CameraClearFlags.Nothing;
|
||||
_bakeCamera.cullingMask = 0; // Nothing
|
||||
_bakeCamera.allowHDR = false;
|
||||
_bakeCamera.allowMSAA = false;
|
||||
_bakeCamera.renderingPath = RenderingPath.Forward;
|
||||
_bakeCamera.useOcclusionCulling = false;
|
||||
|
||||
return _bakeCamera;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,26 +22,25 @@ 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;
|
||||
private float _prevCanvasScale;
|
||||
private Vector3 _prevPsPos;
|
||||
private Vector3 _prevScale;
|
||||
private bool _isPrevStored;
|
||||
private Vector2Int _prevScreenSize;
|
||||
private bool _prewarm;
|
||||
private bool _preWarm;
|
||||
private ParticleSystemRenderer _renderer;
|
||||
|
||||
public override Texture mainTexture => _isTrail ? null : _particleSystem.GetTextureForSprite();
|
||||
@@ -91,6 +91,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 +123,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 +146,15 @@ 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;
|
||||
_isPrevStored = false;
|
||||
}
|
||||
|
||||
public static UIParticleRenderer AddRenderer(UIParticle parent, int index)
|
||||
@@ -177,12 +186,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 +198,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 +233,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 +418,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.SetColors(s_Colors);
|
||||
Profiler.EndSample();
|
||||
workerMesh.LinearToGamma();
|
||||
}
|
||||
|
||||
GetComponents(typeof(IMeshModifier), s_Components);
|
||||
for (var i = 0; i < s_Components.Count; i++)
|
||||
{
|
||||
var components = ListPool<Component>.Rent();
|
||||
GetComponents(typeof(IMeshModifier), components);
|
||||
|
||||
#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>
|
||||
@@ -524,7 +510,7 @@ namespace Coffee.UIExtensions
|
||||
&& _particleSystem.main.scalingMode == ParticleSystemScalingMode.Local
|
||||
&& _parent.canvas)
|
||||
{
|
||||
scale = scale.GetScaled(_parent.canvas.transform.localScale);
|
||||
scale = scale.GetScaled(_parent.canvas.rootCanvas.transform.localScale);
|
||||
}
|
||||
|
||||
Profiler.EndSample();
|
||||
@@ -561,10 +547,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,15 +563,16 @@ 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;
|
||||
if (resolutionChanged && isWorldSpace)
|
||||
var resolutionChanged = _prevScreenSize != screenSize
|
||||
|| !Mathf.Approximately(_prevCanvasScale, canvasScale);
|
||||
if (resolutionChanged && isWorldSpace && _isPrevStored)
|
||||
{
|
||||
// Update particle array size and get particles.
|
||||
var size = _particleSystem.particleCount;
|
||||
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(),
|
||||
@@ -607,6 +591,7 @@ namespace Coffee.UIExtensions
|
||||
_delay = true;
|
||||
_prevScale = scale;
|
||||
_prevPsPos = psPos;
|
||||
_isPrevStored = true;
|
||||
}
|
||||
|
||||
_prevCanvasScale = canvas ? canvas.scaleFactor : 1f;
|
||||
@@ -622,23 +607,25 @@ 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.
|
||||
var isLocalSpace = _particleSystem.IsLocalSpace();
|
||||
var psTransform = _particleSystem.transform;
|
||||
var originLocalPosition = psTransform.localPosition;
|
||||
var originLocalRotation = psTransform.localRotation;
|
||||
var originWorldPosition = psTransform.position;
|
||||
var originWorldRotation = psTransform.rotation;
|
||||
var emission = _particleSystem.emission;
|
||||
var rateOverDistance = emission.enabled
|
||||
&& 0 < emission.rateOverDistance.constant
|
||||
&& 0 < emission.rateOverDistanceMultiplier;
|
||||
if (rateOverDistance && !paused)
|
||||
if (rateOverDistance && !paused && _isPrevStored)
|
||||
{
|
||||
// (For rate-over-distance emission,) Move to previous scaled position, simulate (delta = 0).
|
||||
var prevScaledPos = isLocalSpace
|
||||
@@ -654,7 +641,8 @@ namespace Coffee.UIExtensions
|
||||
: originWorldPosition.GetScaled(scale.Inverse());
|
||||
psTransform.SetPositionAndRotation(scaledPos, originWorldRotation);
|
||||
_particleSystem.Simulate(deltaTime, false, false, false);
|
||||
psTransform.SetPositionAndRotation(originWorldPosition, originWorldRotation);
|
||||
psTransform.localPosition = originLocalPosition;
|
||||
psTransform.localRotation = originLocalRotation;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
@@ -698,12 +686,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()
|
||||
@@ -57,9 +58,8 @@ namespace Coffee.UIExtensions
|
||||
for (var i = 0; i < s_ActiveParticles.Count; i++)
|
||||
{
|
||||
var uip = s_ActiveParticles[i];
|
||||
if (!uip || !uip.canvas || !uip.isPrimary || s_UpdatedGroupIds.Contains(uip.groupId)) continue;
|
||||
if (!uip || !uip.canvas || !uip.isPrimary || !s_UpdatedGroupIds.Add(uip.groupId)) continue;
|
||||
|
||||
s_UpdatedGroupIds.Add(uip.groupId);
|
||||
uip.UpdateTransformScale();
|
||||
uip.UpdateRenderers();
|
||||
}
|
||||
@@ -76,9 +76,8 @@ namespace Coffee.UIExtensions
|
||||
{
|
||||
uip.UpdateRenderers();
|
||||
}
|
||||
else if (!s_UpdatedGroupIds.Contains(uip.groupId))
|
||||
else if (s_UpdatedGroupIds.Add(uip.groupId))
|
||||
{
|
||||
s_UpdatedGroupIds.Add(uip.groupId);
|
||||
uip.UpdateRenderers();
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
public static void Destroy(Object obj)
|
||||
{
|
||||
if (!obj) return;
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
foreach (var p in self)
|
||||
{
|
||||
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:
|
||||
@@ -14461,7 +14461,7 @@ Canvas:
|
||||
m_GameObject: {fileID: 1074082869}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 0
|
||||
m_RenderMode: 1
|
||||
m_Camera: {fileID: 1023393581}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
|
||||
@@ -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.3",
|
||||
"unity": "2018.2",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
Reference in New Issue
Block a user