Compare commits

..

16 Commits

Author SHA1 Message Date
semantic-release-bot
0322d7eb95 chore(release): 4.7.0 [skip ci]
# [4.7.0](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.8...v4.7.0) (2024-06-19)

### Bug Fixes

* `UIParticle.transform.localScale` does not scale particles ([1d40e24](1d40e24c74)), closes [#313](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/313)
* UIParticle is scaled by canvas size even when `AutoScalingMode.None` and `ScalingMode.Local` ([54a4b1c](54a4b1cdfd)), closes [#313](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/313)
* UIParticle is scaled incorrectly with nested canvases ([f26920f](f26920f982)), closes [#313](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/313)

### Features

* reset previous position on start play for world space simulation ([3880484](3880484ce5)), closes [#303](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/303)
* restore `Transform.localScale` when setting `autoScalingMode` to something other than `Transform` ([5505247](5505247a94))
2024-06-19 04:30:47 +00:00
mob-sakai
e7be0e77de fix demo 2024-06-19 13:23:40 +09:00
mob-sakai
3880484ce5 feat: reset previous position on start play for world space simulation
close #303
2024-06-19 11:07:29 +09:00
mob-sakai
1d40e24c74 fix: UIParticle.transform.localScale does not scale particles
close #313
2024-06-19 11:07:29 +09:00
mob-sakai
54a4b1cdfd fix: UIParticle is scaled by canvas size even when AutoScalingMode.None and ScalingMode.Local
close #313
2024-06-19 11:07:29 +09:00
mob-sakai
5505247a94 feat: restore Transform.localScale when setting autoScalingMode to something other than Transform 2024-06-19 11:07:29 +09:00
mob-sakai
f26920f982 fix: UIParticle is scaled incorrectly with nested canvases
close #313
2024-06-18 19:44:00 +09:00
mob-sakai
5a1e65ec56 refactor: refactor 2024-06-18 17:11:14 +09:00
semantic-release-bot
accd3f8410 chore(release): 4.6.8 [skip ci]
## [4.6.8](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.7...v4.6.8) (2024-06-14)

### Bug Fixes

* 'Resource ID out of range in GetResource' error in overlay rendering mode ([05286ce](05286cedfd)), closes [#308](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/308)
2024-06-14 01:48:23 +00:00
SAMYTHEBIGJUICY
05286cedfd fix: 'Resource ID out of range in GetResource' error in overlay rendering mode
close #308
2024-06-14 10:47:32 +09:00
semantic-release-bot
cbd9c960e2 chore(release): 4.6.7 [skip ci]
## [4.6.7](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.6...v4.6.7) (2024-05-24)

### Bug Fixes

* the ParticleSystem's localPosition drifts at certain scales due to floating-point precision issues ([e924eb4](e924eb4596)), closes [#299](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/299) [#312](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/312)
2024-05-24 08:38:56 +00:00
mob-sakai
e924eb4596 fix: the ParticleSystem's localPosition drifts at certain scales due to floating-point precision issues
close #299, close #312
2024-05-24 17:37:32 +09:00
semantic-release-bot
19989cf18c chore(release): 4.6.6 [skip ci]
## [4.6.6](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.5...v4.6.6) (2024-05-23)

### Bug Fixes

* fix release workflow ([30b0076](30b00762f6))
2024-05-23 11:04:05 +00:00
mob-sakai
30b00762f6 fix: fix release workflow 2024-05-23 20:03:17 +09:00
semantic-release-bot
154a04c022 chore(release): 4.6.5 [skip ci]
## [4.6.5](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.4...v4.6.5) (2024-05-23)

### Bug Fixes

* update workflows (for preview and v4) ([3eab097](3eab0979b9))
2024-05-23 08:50:10 +00:00
mob-sakai
3eab0979b9 fix: update workflows (for preview and v4) 2024-05-23 17:49:30 +09:00
86 changed files with 402 additions and 2793 deletions

View File

@@ -4,8 +4,9 @@ on:
workflow_dispatch:
push:
branches:
- preview
- release
- release-preview
- release-v4
tags-ignore:
- "**"
@@ -22,7 +23,8 @@ jobs:
released: ${{ steps.release.outputs.new_release_published }}
tag: ${{ steps.release.outputs.new_release_git_tag }}
version: ${{ steps.release.outputs.new_release_version }}
notes: ${{ steps.release.outputs.new_release_notes }}
merge_to: ${{ steps.summary.outputs.merge_to }}
split_to: ${{ steps.summary.outputs.split_to }}
steps:
- name: 🚚 Checkout (${{ github.ref_name }})
uses: actions/checkout@v4
@@ -38,37 +40,51 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
- run: |
- id: summary
run: |
echo "🔖 New release published: '${{ steps.release.outputs.new_release_published }}'" | tee -a $GITHUB_STEP_SUMMARY
echo "🔖 New release version: '${{ steps.release.outputs.new_release_version }}'" | tee -a $GITHUB_STEP_SUMMARY
echo "🔖 New release channel: '${{ steps.release.outputs.new_release_channel }}'" | tee -a $GITHUB_STEP_SUMMARY
echo "🔖 New release git tag: '${{ steps.release.outputs.new_release_git_tag }}'" | tee -a $GITHUB_STEP_SUMMARY
if [ '${{ steps.release.outputs.new_release_published }}' = 'false' ]; then
echo "No new release published." | tee -a $GITHUB_STEP_SUMMARY
elif [ '${{ github.ref_name }}' = 'release' ]; then
echo "merge_to=develop" | tee -a $GITHUB_OUTPUT
echo "split_to=main" | tee -a $GITHUB_OUTPUT
elif [ '${{ github.ref_name }}' = 'release-preview' ]; then
echo "merge_to=develop-preview" | tee -a $GITHUB_OUTPUT
echo "split_to=preview" | tee -a $GITHUB_OUTPUT
elif [ '${{ github.ref_name }}' = 'release-4.x' ]; then
echo "merge_to=develop-4.x" | tee -a $GITHUB_OUTPUT
echo "split_to=4.x" | tee -a $GITHUB_OUTPUT
fi
merge-to-develop:
if: needs.release.outputs.released == 'true'
merge-to:
if: needs.release.outputs.merge_to != ''
needs: release
name: 🔀 Merge to develop
name: 🔀 Merge to ${{ needs.release.outputs.merge_to }}
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: 🚚 Checkout (develop)
- name: 🚚 Checkout (${{ needs.release.outputs.merge_to }})
uses: actions/checkout@v4
with:
ref: develop
ref: ${{ needs.release.outputs.merge_to }}
fetch-depth: 0
- name: 🔀 Merge '${{ needs.release.outputs.tag }}' into 'develop'
- name: 🔀 Merge '${{ needs.release.outputs.tag }}' into '${{ needs.release.outputs.merge_to }}'
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git merge ${{ needs.release.outputs.tag }}
git push origin develop
git push origin ${{ needs.release.outputs.merge_to }}
split-to-main:
if: needs.release.outputs.released == 'true'
split-to:
if: needs.release.outputs.split_to != ''
needs: release
name: 🔀 Split package
name: 🔀 Split package to ${{ needs.release.outputs.split_to }}
runs-on: ubuntu-latest
permissions:
contents: write
@@ -78,9 +94,10 @@ jobs:
with:
ref: ${{ needs.release.outputs.tag }}
fetch-depth: 0
- name: 🔀 Split subtree 'Packages/src' to 'main'
- name: 🔀 Split subtree 'Packages/src' to '${{ needs.release.outputs.split_to }}'
run: |
git branch main origin/main
git subtree split --prefix=Packages/src --branch main
git tag ${{ needs.release.outputs.version }} main
git push origin ${{ needs.release.outputs.version }} main:main
split_to=${{ needs.release.outputs.split_to }}
git branch $split_to origin/$split_to
git subtree split --prefix=Packages/src --branch $split_to
git tag ${{ needs.release.outputs.version }} $split_to
git push origin ${{ needs.release.outputs.version }} $split_to:$split_to

View File

@@ -15,7 +15,8 @@ on:
push:
branches:
- develop
- develop_v5
- develop-preview
- develop-4.x
tags:
- "!*"
paths-ignore:

1
.gitignore vendored
View File

@@ -28,3 +28,4 @@ Assets/Plugins/
obj/
bin/
UserSettings/
*.app

View File

@@ -1,10 +1,6 @@
{
"dependencies": {
"com.unity.ide.rider": "3.0.27",
"com.coffee.development": "https://github.com/mob-sakai/Coffee.Internal.git?path=Packages/Development",
"com.coffee.nano-monitor": "https://github.com/mob-sakai/Coffee.Internal.git?path=Packages/NanoMonitor",
"com.coffee.simple-scene-navigator": "https://github.com/mob-sakai/Coffee.Internal.git?path=Packages/SceneNavigator",
"com.coffee.sub-asset-editor": "https://github.com/mob-sakai/SubAssetEditor.git",
"com.unity.test-framework": "1.1.33",
"com.unity.modules.animation": "1.0.0",
"com.unity.modules.physics": "1.0.0"

View File

@@ -1,37 +1,5 @@
{
"dependencies": {
"com.coffee.development": {
"version": "https://github.com/mob-sakai/Coffee.Internal.git?path=Packages/Development",
"depth": 0,
"source": "git",
"dependencies": {},
"hash": "41a1b604af8769b600d9c75db02ff35ec30611dc"
},
"com.coffee.nano-monitor": {
"version": "https://github.com/mob-sakai/Coffee.Internal.git?path=Packages/NanoMonitor",
"depth": 0,
"source": "git",
"dependencies": {
"com.unity.ugui": "1.0.0"
},
"hash": "41a1b604af8769b600d9c75db02ff35ec30611dc"
},
"com.coffee.simple-scene-navigator": {
"version": "https://github.com/mob-sakai/Coffee.Internal.git?path=Packages/SceneNavigator",
"depth": 0,
"source": "git",
"dependencies": {
"com.unity.ugui": "1.0.0"
},
"hash": "41a1b604af8769b600d9c75db02ff35ec30611dc"
},
"com.coffee.sub-asset-editor": {
"version": "https://github.com/mob-sakai/SubAssetEditor.git",
"depth": 0,
"source": "git",
"dependencies": {},
"hash": "01464178eec1e4dbe741c11c9baeb94a151c99ee"
},
"com.coffee.ui-particle": {
"version": "file:src",
"depth": 0,

View File

@@ -1 +0,0 @@
s/Coffee.Internal/Coffee.UIParticleInternal/g

View File

@@ -1,8 +1,9 @@
{
"branches": [
"release",
"release-4.x",
{
"name": "preview",
"name": "release-preview",
"prerelease": true
}
],

View File

@@ -1,3 +1,46 @@
# [4.7.0](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.8...v4.7.0) (2024-06-19)
### Bug Fixes
* `UIParticle.transform.localScale` does not scale particles ([1d40e24](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/1d40e24c742741e97f03c55468ccb1e505341133)), closes [#313](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/313)
* UIParticle is scaled by canvas size even when `AutoScalingMode.None` and `ScalingMode.Local` ([54a4b1c](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/54a4b1cdfd06400c7be89c1ee704bb42a659c7c2)), closes [#313](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/313)
* UIParticle is scaled incorrectly with nested canvases ([f26920f](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/f26920f9825547222a4afbb31cc5dc5a002c3e9b)), closes [#313](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/313)
### Features
* reset previous position on start play for world space simulation ([3880484](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/3880484ce5190c42fc79c81d0b69e3fbeda09dd0)), closes [#303](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/303)
* restore `Transform.localScale` when setting `autoScalingMode` to something other than `Transform` ([5505247](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/5505247a94a929ff89635fde512a9b95691e0043))
## [4.6.8](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.7...v4.6.8) (2024-06-14)
### Bug Fixes
* 'Resource ID out of range in GetResource' error in overlay rendering mode ([05286ce](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/05286cedfd17b1a0cb90a5e918513644f47cd831)), closes [#308](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/308)
## [4.6.7](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.6...v4.6.7) (2024-05-24)
### Bug Fixes
* the ParticleSystem's localPosition drifts at certain scales due to floating-point precision issues ([e924eb4](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/e924eb45968a112347471cabaeabc274e4c37ce4)), closes [#299](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/299) [#312](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/312)
## [4.6.6](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.5...v4.6.6) (2024-05-23)
### Bug Fixes
* fix release workflow ([30b0076](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/30b00762f6da166c043587798b1552f27b4cc604))
## [4.6.5](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.4...v4.6.5) (2024-05-23)
### Bug Fixes
* update workflows (for preview and v4) ([3eab097](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/3eab0979b9b85919b804442ab05735b7120eade5))
## [4.6.4](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.3...v4.6.4) (2024-05-22)

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 1006234332be4f329fc1830319b31aaa
timeCreated: 1704502465

View File

@@ -1,31 +0,0 @@
using Coffee.UIExtensions;
using Coffee.UIParticleInternal.AssetModification;
using UnityEditor;
#pragma warning disable CS0612 // Type or member is obsolete
namespace Coffee.UIParticleInternal
{
internal class UIParticleComponentModifier_AbsoluteMode : ComponentModifier<UIParticle>
{
protected override bool ModifyComponent(UIParticle uip, bool dryRun)
{
if (!uip.m_AbsoluteMode) return false;
uip.m_AbsoluteMode = false;
uip.positionMode = UIParticle.PositionMode.Absolute;
if (!dryRun)
{
EditorUtility.SetDirty(uip);
}
return true;
}
public override string Report()
{
return " -> UIParticle.absoluteMode is obsolete. Use UIParticle.positionMode instead.\n";
}
}
}

View File

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

View File

@@ -1,36 +0,0 @@
using Coffee.UIExtensions;
using Coffee.UIParticleInternal.AssetModification;
using UnityEditor;
using UnityEngine;
#pragma warning disable CS0612 // Type or member is obsolete
namespace Coffee.UIParticleInternal
{
internal class UIParticleComponentModifier_AutoScaling : ComponentModifier<UIParticle>
{
protected override bool ModifyComponent(UIParticle uip, bool dryRun)
{
if (!uip.m_AutoScaling) return false;
uip.m_AutoScaling = false;
uip.autoScalingMode = UIParticle.AutoScalingMode.Transform;
uip.transform.localScale = Vector3.one;
if (!dryRun)
{
EditorUtility.SetDirty(uip);
EditorUtility.SetDirty(uip.transform);
}
return true;
}
public override string Report()
{
return
" -> UIParticle.ignoreCanvasScaler and UIParticle.autoScaling are obsolete." +
" Use UIParticle.autoScalingMode instead.\n";
}
}
}

View File

@@ -1,31 +0,0 @@
using Coffee.UIExtensions;
using Coffee.UIParticleInternal.AssetModification;
using UnityEditor;
using UnityEngine;
#pragma warning disable CS0612 // Type or member is obsolete
namespace Coffee.UIParticleInternal
{
internal class UIParticleComponentModifier_IsTrail : ComponentModifier<UIParticle>
{
protected override bool ModifyComponent(UIParticle uip, bool dryRun)
{
if (!uip.m_IsTrail) return false;
if (!dryRun)
{
var go = uip.gameObject;
Object.DestroyImmediate(uip);
EditorUtility.SetDirty(go);
}
return true;
}
public override string Report()
{
return " -> UIParticle for trail is no longer needed. Removed.";
}
}
}

View File

@@ -1,45 +0,0 @@
using System;
using System.Collections.Generic;
using Coffee.UIParticleInternal.AssetModification;
using UnityEditor;
namespace Coffee.UIParticleInternal
{
internal class UIParticleModifierRunner : Runner
{
public UIParticleModifierRunner()
: base("UIParticle v5", new List<(string, Func<string, Modifier>)>
{
(".unity", x => new SceneModifier
{
path = x,
componentModifiers = new IComponentModifier[]
{
new UIParticleRendererComponentModifier(),
new UIParticleComponentModifier_AutoScaling(),
new UIParticleComponentModifier_AbsoluteMode(),
new UIParticleComponentModifier_IsTrail()
}
}),
(".prefab", x => new PrefabModifier
{
path = x,
componentModifiers = new IComponentModifier[]
{
new UIParticleRendererComponentModifier(),
new UIParticleComponentModifier_AutoScaling(),
new UIParticleComponentModifier_AbsoluteMode(),
new UIParticleComponentModifier_IsTrail()
}
})
})
{
}
[MenuItem("UIParticleModifierRunner/Run")]
private static void Run()
{
new UIParticleModifierRunner().RunIfUserWantsTo();
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: edd678db452e49869caeca7e7d269e5d
timeCreated: 1704502476

View File

@@ -1,29 +0,0 @@
using Coffee.UIExtensions;
using Coffee.UIParticleInternal.AssetModification;
using UnityEditor;
using UnityEngine;
namespace Coffee.UIParticleInternal
{
internal class UIParticleRendererComponentModifier : ComponentModifier<UIParticleRenderer>
{
protected override bool ModifyComponent(UIParticleRenderer c, bool dryRun)
{
if (c.hideFlags.HasFlag(HideFlags.DontSave | HideFlags.NotEditable)) return false;
if (!dryRun)
{
var go = c.gameObject;
Object.DestroyImmediate(c);
EditorUtility.SetDirty(go);
}
return true;
}
public override string Report()
{
return " -> UIParticleRenderer component is now auto-generated object. Remove them.\n";
}
}
}

View File

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

View File

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

View File

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

View File

@@ -1,33 +0,0 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Coffee.UIParticleInternal.AssetModification
{
internal abstract class ComponentModifier<T> : IComponentModifier where T : Component
{
private static readonly List<T> s_Components = new List<T>();
public bool isModified { get; private set; }
public bool ModifyComponent(GameObject root, bool dryRun)
{
root.GetComponentsInChildren(true, s_Components);
foreach (var c in s_Components)
{
if (PrefabUtility.IsPartOfAnyPrefab(c.gameObject)) continue;
if (ModifyComponent(c, dryRun))
{
isModified = true;
}
}
return isModified;
}
public abstract string Report();
protected abstract bool ModifyComponent(T component, bool dryRun);
}
}

View File

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

View File

@@ -1,43 +0,0 @@
using System.Linq;
using System.Text;
using UnityEngine;
namespace Coffee.UIParticleInternal.AssetModification
{
internal abstract class GameObjectModifier : Modifier
{
private static readonly StringBuilder s_ReportLog = new StringBuilder();
public IComponentModifier[] componentModifiers;
protected bool ModifyGameObject(GameObject root, bool dryRun)
{
foreach (var modifier in componentModifiers)
{
modifier.ModifyComponent(root, dryRun);
}
return componentModifiers.Any(x => x.isModified);
}
protected override string ModificationReport()
{
if (!hasUpgraded) return string.Empty;
s_ReportLog.Length = 0;
foreach (var componentModifier in componentModifiers)
{
if (componentModifier.isModified)
{
s_ReportLog.Append(componentModifier.Report());
}
}
if (0 < s_ReportLog.Length)
{
s_ReportLog.Length--;
}
return s_ReportLog.ToString();
}
}
}

View File

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

View File

@@ -1,11 +0,0 @@
using UnityEngine;
namespace Coffee.UIParticleInternal.AssetModification
{
internal interface IComponentModifier
{
bool isModified { get; }
bool ModifyComponent(GameObject root, bool dryRun);
string Report();
}
}

View File

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

View File

@@ -1,9 +0,0 @@
using System.Text;
namespace Coffee.UIParticleInternal.AssetModification
{
internal interface ITextModifier
{
bool ModifyText(StringBuilder sb, string text);
}
}

View File

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

View File

@@ -1,41 +0,0 @@
using System;
namespace Coffee.UIParticleInternal.AssetModification
{
internal abstract class Modifier
{
private string _error;
public string path;
protected abstract string id { get; }
protected bool hasUpgraded { private set; get; }
public void Modify(bool dryRun)
{
try
{
hasUpgraded = RunModify(dryRun);
}
catch (Exception e)
{
_error = e.Message;
}
}
public string GetModificationReport()
{
return !string.IsNullOrEmpty(_error)
? $"<b><color=red>[{id} (Error)]</color> {path}</b> {_error}\n"
: hasUpgraded
? $"<b><color=green>[{id}]</color> {path}</b> {ModificationReport()}\n"
: string.Empty;
}
protected abstract bool RunModify(bool dryRun);
protected virtual string ModificationReport()
{
return string.Empty;
}
}
}

View File

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

View File

@@ -1,48 +0,0 @@
using System;
using UnityEditor;
using UnityEngine;
namespace Coffee.UIParticleInternal.AssetModification
{
internal class PrefabModifier : GameObjectModifier
{
protected override string id => "Prefab";
protected override bool RunModify(bool dryRun)
{
using (var scope = new EditScope(path))
{
var changed = ModifyGameObject(scope.root, dryRun);
if (!dryRun && changed)
{
scope.Save();
}
return changed;
}
}
private readonly struct EditScope : IDisposable
{
private readonly string _path;
public readonly GameObject root;
public EditScope(string path)
{
_path = path;
root = PrefabUtility.LoadPrefabContents(path);
}
public void Dispose()
{
PrefabUtility.UnloadPrefabContents(root);
}
public void Save()
{
PrefabUtility.SaveAsPrefabAsset(root, _path);
}
}
}
}

View File

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

View File

@@ -1,97 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
namespace Coffee.UIParticleInternal.AssetModification
{
internal class Runner
{
private readonly List<(string ext, Func<string, Modifier> create)> _factory;
private readonly string _name;
protected Runner(string name, List<(string ext, Func<string, Modifier> create)> factory)
{
_name = name;
_factory = factory;
}
private Modifier CreateFromPath(string assetPath)
{
var ext = Path.GetExtension(assetPath);
return _factory
.FirstOrDefault(x => x.ext == ext)
.create?.Invoke(assetPath);
}
private Modifier[] GetModifiers(string[] assetPaths)
{
return assetPaths
.Where(x => x.StartsWith("Assets/", StringComparison.Ordinal))
.Select(CreateFromPath)
.Where(x => x != null)
.OrderBy(x => Path.GetExtension(x.path))
.ToArray();
}
public void RunIfUserWantsTo()
{
var select = EditorUtility.DisplayDialogComplex($"Upgrade {_name}",
"Upgrade all assets in this project?\n\n" +
"'Go Ahead': Upgrades all assets in this project using the old APIs. You should make a backup before proceeding.\n\n" +
"'Dry Run': Outputs the upgrade summary to the console without changing.", "I Made a Backup. Go Ahead!",
"No Thanks", "Dry Run");
if (select == 1) return;
if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) return;
var assetPaths = AssetDatabase.GetAllAssetPaths();
var dryRun = select == 2;
Run(assetPaths, dryRun);
}
public void Run(string[] assetPaths, bool dryRun)
{
var modifiers = GetModifiers(assetPaths);
var canceled = false;
try
{
AssetDatabase.StartAssetEditing();
EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
for (var i = 0; i < modifiers.Length; i++)
{
var percentage = (float)i / modifiers.Length;
var m = modifiers[i];
if (EditorUtility.DisplayCancelableProgressBar("Upgrading...", m.path, percentage))
{
canceled = true;
break;
}
m.Modify(dryRun);
}
}
catch (Exception e)
{
Debug.LogException(e);
}
finally
{
var sb = new StringBuilder();
sb.Append(dryRun ? "<b>[DryRun]</b> " : "");
sb.AppendLine($"<b>Modify '{_name}' is {(canceled ? "canceled" : "completed")}.</b>");
sb.AppendLine("==== Modifications ====");
Debug.Log(modifiers.Aggregate(sb, (x, m) => x.Append(m.GetModificationReport())));
EditorUtility.ClearProgressBar();
AssetDatabase.StopAssetEditing();
AssetDatabase.Refresh();
}
}
}
}

View File

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

View File

@@ -1,53 +0,0 @@
using System;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
namespace Coffee.UIParticleInternal.AssetModification
{
internal class SceneModifier : GameObjectModifier
{
protected override string id => "Scene";
protected override bool RunModify(bool dryRun)
{
using (var scope = new EditScope(path))
{
var changed = false;
foreach (var root in scope.scene.GetRootGameObjects())
{
if (ModifyGameObject(root, dryRun))
{
changed = true;
}
}
if (!dryRun && changed)
{
scope.Save();
}
return changed;
}
}
private readonly struct EditScope : IDisposable
{
public readonly Scene scene;
public EditScope(string path)
{
scene = EditorSceneManager.OpenScene(path, OpenSceneMode.Additive);
}
public void Dispose()
{
EditorSceneManager.CloseScene(scene, true);
}
public void Save()
{
EditorSceneManager.SaveScene(scene);
}
}
}
}

View File

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

View File

@@ -1,78 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Coffee.UIParticleInternal.AssetModification
{
internal class TextAssetModifier : Modifier
{
public ITextModifier[] textModifiers;
protected override string id => "Text";
protected virtual string savePath => path;
protected override bool RunModify(bool dryRun)
{
var changed = false;
using (var scope = new EditScope(path, savePath))
{
foreach (var line in scope.lines)
{
if (ModifyLine(scope.sb, line))
{
changed = true;
}
else
{
scope.sb.AppendLine(line);
}
}
if (!dryRun && changed)
{
scope.Save();
}
return changed;
}
}
private bool ModifyLine(StringBuilder sb, string line)
{
foreach (var modifier in textModifiers)
{
if (modifier.ModifyText(sb, line))
{
return true;
}
}
return false;
}
private readonly struct EditScope : IDisposable
{
private static readonly StringBuilder s_File = new StringBuilder();
private readonly string _path;
private readonly string _savePath;
public IEnumerable<string> lines => File.ReadLines(_path);
public StringBuilder sb => s_File;
public EditScope(string path, string savePath)
{
s_File.Length = 0;
_path = path;
_savePath = savePath;
}
public void Dispose()
{
}
public void Save()
{
File.WriteAllText(_savePath, sb.ToString());
}
}
}
}

View File

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

View File

@@ -1,25 +0,0 @@
using System.Text;
using System.Text.RegularExpressions;
namespace Coffee.UIParticleInternal.AssetModification
{
internal class TextReplaceModifier : ITextModifier
{
private readonly Regex _pattern;
private readonly string _replace;
public TextReplaceModifier(string pattern, string replace)
{
_pattern = new Regex(pattern);
_replace = replace;
}
public bool ModifyText(StringBuilder sb, string text)
{
if (!_pattern.IsMatch(text)) return false;
sb.AppendLine(_pattern.Replace(text, _replace));
return true;
}
}
}

View File

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

View File

@@ -6,6 +6,7 @@ using UnityEditor.UI;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.UI;
using Coffee.UIParticleExtensions;
#if UNITY_2021_2_OR_NEWER
using UnityEditor.Overlays;
#else
@@ -474,20 +475,7 @@ 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

View File

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

View File

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

View File

@@ -1,134 +0,0 @@
#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();
}
}
}

View File

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

View File

@@ -1,77 +0,0 @@
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();
}
}
}

View File

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

View File

@@ -1,155 +0,0 @@
using System;
using UnityEditor;
using UnityEngine;
using UnityEngine.Profiling;
using Object = UnityEngine.Object;
namespace Coffee.UIParticleInternal
{
/// <summary>
/// Extension methods for Component class.
/// </summary>
internal static class ComponentExtensions
{
/// <summary>
/// Get or add a component of a specific type to a GameObject.
/// </summary>
public static T GetOrAddComponent<T>(this Component self) where T : Component
{
if (!self) return null;
return self.TryGetComponent<T>(out var component)
? component
: self.gameObject.AddComponent<T>();
}
/// <summary>
/// Get the root component of a specific type in the hierarchy of a GameObject.
/// </summary>
public static T GetRootComponent<T>(this Component self) where T : Component
{
T component = null;
var transform = self.transform;
while (transform)
{
if (transform.TryGetComponent<T>(out var c))
{
component = c;
}
transform = transform.parent;
}
return component;
}
/// <summary>
/// Get a component of a specific type in the parent hierarchy of a GameObject.
/// </summary>
public static T GetComponentInParent<T>(this Component self, bool includeSelf, Transform stopAfter,
Predicate<T> valid)
where T : Component
{
var tr = includeSelf ? self.transform : self.transform.parent;
while (tr)
{
if (tr.TryGetComponent<T>(out var c) && valid(c)) return c;
if (tr == stopAfter) return null;
tr = tr.parent;
}
return null;
}
/// <summary>
/// Add a component of a specific type to the children of a GameObject.
/// </summary>
public static void AddComponentOnChildren<T>(this Component self, HideFlags hideFlags, bool includeSelf)
where T : Component
{
if (self == null) return;
Profiler.BeginSample("(COF)[ComponentExt] AddComponentOnChildren > Self");
if (includeSelf && !self.TryGetComponent<T>(out _))
{
var c = self.gameObject.AddComponent<T>();
c.hideFlags = hideFlags;
}
Profiler.EndSample();
Profiler.BeginSample("(COF)[ComponentExt] AddComponentOnChildren > Child");
var childCount = self.transform.childCount;
for (var i = 0; i < childCount; i++)
{
var child = self.transform.GetChild(i);
if (child.TryGetComponent<T>(out _)) continue;
var c = child.gameObject.AddComponent<T>();
c.hideFlags = hideFlags;
}
Profiler.EndSample();
}
#if !UNITY_2021_2_OR_NEWER && !UNITY_2020_3_45 && !UNITY_2020_3_46 && !UNITY_2020_3_47 && !UNITY_2020_3_48
public static T GetComponentInParent<T>(this Component self, bool includeInactive) where T : Component
{
if (!self) return null;
if (!includeInactive) return self.GetComponentInParent<T>();
var current = self.transform;
while (current)
{
if (current.TryGetComponent<T>(out var c)) return c;
current = current.parent;
}
return null;
}
#endif
#if UNITY_EDITOR
/// <summary>
/// Verify whether it can be converted to the specified component.
/// </summary>
internal static bool CanConvertTo<T>(this Object context) where T : MonoBehaviour
{
return context && context.GetType() != typeof(T);
}
/// <summary>
/// Convert to the specified component.
/// </summary>
internal static void ConvertTo<T>(this Object context) where T : MonoBehaviour
{
var target = context as MonoBehaviour;
if (target == null) return;
var so = new SerializedObject(target);
so.Update();
var oldEnable = target.enabled;
target.enabled = false;
// Find MonoScript of the specified component.
foreach (var script in Resources.FindObjectsOfTypeAll<MonoScript>())
{
if (script.GetClass() != typeof(T))
{
continue;
}
// Set 'm_Script' to convert.
so.FindProperty("m_Script").objectReferenceValue = script;
so.ApplyModifiedProperties();
break;
}
if (so.targetObject is MonoBehaviour mb)
{
mb.enabled = oldEnable;
}
}
#endif
}
}

View File

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

View File

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

View File

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

View File

@@ -1,48 +0,0 @@
using System.Diagnostics;
using UnityEditor;
using UnityEngine;
namespace Coffee.UIParticleInternal
{
internal static class Misc
{
public static void Destroy(Object obj)
{
if (!obj) return;
#if UNITY_EDITOR
if (!Application.isPlaying)
{
Object.DestroyImmediate(obj);
}
else
#endif
{
Object.Destroy(obj);
}
}
public static void DestroyImmediate(Object obj)
{
if (!obj) return;
#if UNITY_EDITOR
if (Application.isEditor)
{
Object.DestroyImmediate(obj);
}
else
#endif
{
Object.Destroy(obj);
}
}
[Conditional("UNITY_EDITOR")]
public static void SetDirty(Object obj)
{
#if UNITY_EDITOR
if (!obj) return;
EditorUtility.SetDirty(obj);
#endif
}
}
}

View File

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

View File

@@ -1,58 +0,0 @@
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
}
}

View File

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

View File

@@ -1,46 +0,0 @@
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);
}
}
}

View File

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

View File

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

View File

@@ -1,217 +0,0 @@
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
}
}

View File

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

View File

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

View File

@@ -1,81 +0,0 @@
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());
}
}
}

View File

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

View File

@@ -1,102 +0,0 @@
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;
}
}
}
}

View File

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

View File

@@ -1,257 +0,0 @@
using System;
using System.Text;
using UnityEngine;
using Conditional = System.Diagnostics.ConditionalAttribute;
using Object = UnityEngine.Object;
#if ENABLE_COFFEE_LOGGER
using System.Reflection;
using System.Collections.Generic;
#endif
namespace Coffee.UIParticleInternal
{
internal static class Logging
{
#if !ENABLE_COFFEE_LOGGER
private const string k_DisableSymbol = "DISABLE_COFFEE_LOGGER";
[Conditional(k_DisableSymbol)]
#endif
private static void Log_Internal(LogType type, object tag, object message, Object context)
{
#if ENABLE_COFFEE_LOGGER
AppendTag(s_Sb, tag);
s_Sb.Append(message);
switch (type)
{
case LogType.Error:
case LogType.Assert:
case LogType.Exception:
Debug.LogError(s_Sb, context);
break;
case LogType.Warning:
Debug.LogWarning(s_Sb, context);
break;
case LogType.Log:
Debug.Log(s_Sb, context);
break;
}
s_Sb.Length = 0;
#endif
}
#if !ENABLE_COFFEE_LOGGER
[Conditional(k_DisableSymbol)]
#endif
public static void LogIf(bool enable, object tag, object message, Object context = null)
{
if (!enable) return;
Log_Internal(LogType.Log, tag, message, context ? context : tag as Object);
}
#if !ENABLE_COFFEE_LOGGER
[Conditional(k_DisableSymbol)]
#endif
public static void Log(object tag, object message, Object context = null)
{
Log_Internal(LogType.Log, tag, message, context ? context : tag as Object);
}
#if !ENABLE_COFFEE_LOGGER
[Conditional(k_DisableSymbol)]
#endif
public static void LogWarning(object tag, object message, Object context = null)
{
Log_Internal(LogType.Warning, tag, message, context ? context : tag as Object);
}
public static void LogError(object tag, object message, Object context = null)
{
#if ENABLE_COFFEE_LOGGER
Log_Internal(LogType.Error, tag, message, context ? context : tag as Object);
#else
Debug.LogError($"{tag}: {message}", context);
#endif
}
#if !ENABLE_COFFEE_LOGGER
[Conditional(k_DisableSymbol)]
#endif
public static void LogMulticast(Type type, string fieldName, object instance = null, string message = null)
{
#if ENABLE_COFFEE_LOGGER
AppendTag(s_Sb, instance ?? type);
var handler = type
.GetField(fieldName,
BindingFlags.Static | BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic)
?.GetValue(instance);
var list = ((MulticastDelegate)handler)?.GetInvocationList() ?? Array.Empty<Delegate>();
s_Sb.Append("<color=orange>");
s_Sb.Append(type.Name);
s_Sb.Append(".");
s_Sb.Append(fieldName);
s_Sb.Append(" has ");
s_Sb.Append(list.Length);
s_Sb.Append(" callbacks");
if (message != null)
{
s_Sb.Append(" (");
s_Sb.Append(message);
s_Sb.Append(")");
}
s_Sb.Append(":</color>");
for (var i = 0; i < list.Length; i++)
{
s_Sb.Append("\n - ");
s_Sb.Append(list[i].Method.DeclaringType?.Name);
s_Sb.Append(".");
s_Sb.Append(list[i].Method.Name);
}
Debug.Log(s_Sb);
s_Sb.Length = 0;
#endif
}
#if !ENABLE_COFFEE_LOGGER
[Conditional(k_DisableSymbol)]
#endif
private static void AppendTag(StringBuilder sb, object tag)
{
#if ENABLE_COFFEE_LOGGER
try
{
sb.Append("f");
sb.Append(Time.frameCount);
sb.Append(":<color=#");
AppendReadableCode(sb, tag);
sb.Append("><b>[");
switch (tag)
{
case Type type:
AppendType(sb, type);
break;
case Object uObject:
AppendType(sb, tag.GetType());
sb.Append(" #");
sb.Append(uObject.name);
break;
default:
AppendType(sb, tag.GetType());
break;
}
sb.Append("]</b></color> ");
}
catch
{
sb.Append("f");
sb.Append(Time.frameCount);
sb.Append(":<b>[");
sb.Append(tag);
sb.Append("]</b> ");
}
#endif
}
#if !ENABLE_COFFEE_LOGGER
[Conditional(k_DisableSymbol)]
#endif
private static void AppendType(StringBuilder sb, Type type)
{
#if ENABLE_COFFEE_LOGGER
if (s_TypeNameCache.TryGetValue(type, out var name))
{
sb.Append(name);
return;
}
// New type found
var start = sb.Length;
if (0 < start && sb[start - 1] == '<' && (type.Name == "Material" || type.Name == "Color"))
{
sb.Append('@');
}
sb.Append(type.Name);
if (type.IsGenericType)
{
sb.Length -= 2;
sb.Append("<");
foreach (var gType in type.GetGenericArguments())
{
AppendType(sb, gType);
sb.Append(", ");
}
sb.Length -= 2;
sb.Append(">");
}
s_TypeNameCache.Add(type, sb.ToString(start, sb.Length - start));
#endif
}
#if !ENABLE_COFFEE_LOGGER
[Conditional(k_DisableSymbol)]
#endif
private static void AppendReadableCode(StringBuilder sb, object tag)
{
#if ENABLE_COFFEE_LOGGER
int hash;
try
{
switch (tag)
{
case string text:
hash = text.GetHashCode();
break;
case Type type:
type = type.IsGenericType ? type.GetGenericTypeDefinition() : type;
hash = type.FullName?.GetHashCode() ?? 0;
break;
default:
hash = tag.GetType().FullName?.GetHashCode() ?? 0;
break;
}
}
catch
{
sb.Append("FFFFFF");
return;
}
hash = hash & (s_Codes.Length - 1);
if (s_Codes[hash] == null)
{
var hue = hash / (float)s_Codes.Length;
var modifier = 1f - Mathf.Clamp01(Mathf.Abs(hue - 0.65f) / 0.2f);
var saturation = 0.7f + modifier * -0.2f;
var value = 0.8f + modifier * 0.3f;
s_Codes[hash] = ColorUtility.ToHtmlStringRGB(Color.HSVToRGB(hue, saturation, value));
}
sb.Append(s_Codes[hash]);
#endif
}
#if ENABLE_COFFEE_LOGGER
private static readonly StringBuilder s_Sb = new StringBuilder();
private static readonly string[] s_Codes = new string[64];
private static readonly Dictionary<Type, string> s_TypeNameCache = new Dictionary<Type, string>();
#endif
}
}

View File

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

View File

@@ -1,65 +0,0 @@
using System;
using UnityEngine;
using UnityEngine.Profiling;
namespace Coffee.UIParticleInternal
{
/// <summary>
/// Provides functionality to manage materials.
/// </summary>
internal static class MaterialRepository
{
private static readonly ObjectRepository<Material> s_Repository = new ObjectRepository<Material>();
public static int count => s_Repository.count;
#if UNITY_EDITOR
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void Clear()
{
s_Repository.Clear();
}
#endif
/// <summary>
/// Retrieves a cached material based on the hash.
/// </summary>
public static bool Valid(Hash128 hash, Material material)
{
Profiler.BeginSample("(COF)[MaterialRegistry] Valid");
var ret = s_Repository.Valid(hash, material);
Profiler.EndSample();
return ret;
}
/// <summary>
/// Adds or retrieves a cached material based on the hash.
/// </summary>
public static void Get(Hash128 hash, ref Material material, Func<Material> onCreate)
{
Profiler.BeginSample("(COF)[MaterialRepository] Get");
s_Repository.Get(hash, ref material, onCreate);
Profiler.EndSample();
}
/// <summary>
/// Adds or retrieves a cached material based on the hash.
/// </summary>
public static void Get<T>(Hash128 hash, ref Material material, Func<T, Material> onCreate, T source)
{
Profiler.BeginSample("(COF)[MaterialRepository] Get");
s_Repository.Get(hash, ref material, onCreate, source);
Profiler.EndSample();
}
/// <summary>
/// Removes a soft mask material from the cache.
/// </summary>
public static void Release(ref Material material)
{
Profiler.BeginSample("(COF)[MaterialRepository] Release");
s_Repository.Release(ref material);
Profiler.EndSample();
}
}
}

View File

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

View File

@@ -1,85 +0,0 @@
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);
}
}
}

View File

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

View File

@@ -1,218 +0,0 @@
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}";
}
}
}
}

View File

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

View File

@@ -1,92 +0,0 @@
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();
}
}
}

View File

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

View File

@@ -0,0 +1,72 @@
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;
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 3d6d0ca7ae8c641aa98b66fd91c05264
guid: b0beae5bb1cb142b9ab90dc0d371f026
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -1,9 +1,8 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Coffee.UIParticleInternal;
using Coffee.UIParticleExtensions;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Rendering;
using UnityEngine.Serialization;
using UnityEngine.UI;
@@ -104,6 +103,8 @@ namespace Coffee.UIExtensions
private int _groupId;
private Camera _orthoCamera;
private DrivenRectTransformTracker _tracker;
private Vector3 _storedScale;
private bool _isScaleStored;
/// <summary>
/// Should this graphic be considered a target for ray-casting?
@@ -201,7 +202,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 +250,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;
@@ -279,8 +285,8 @@ namespace Coffee.UIExtensions
protected override void OnEnable()
{
_isScaleStored = false;
ResetGroupId();
UpdateTracker();
UIParticleUpdater.Register(this);
RegisterDirtyMaterialCallback(UpdateRendererMaterial);
@@ -301,7 +307,13 @@ 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);
@@ -316,14 +328,6 @@ namespace Coffee.UIExtensions
{
}
#if UNITY_EDITOR
protected override void OnValidate()
{
base.OnValidate();
UpdateTracker();
}
#endif
void ISerializationCallbackReceiver.OnBeforeSerialize()
{
}
@@ -482,12 +486,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;
}
@@ -605,22 +623,14 @@ namespace Coffee.UIExtensions
_orthoCamera.transform.SetPositionAndRotation(new Vector3(0, 0, -1000), Quaternion.identity);
_orthoCamera.orthographic = true;
_orthoCamera.farClipPlane = 2000f;
_orthoCamera.clearFlags = CameraClearFlags.Nothing;
_orthoCamera.cullingMask = 0; // Nothing
_orthoCamera.allowHDR = false;
_orthoCamera.allowMSAA = false;
_orthoCamera.renderingPath = RenderingPath.Forward;
_orthoCamera.useOcclusionCulling = false;
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
{
_tracker.Clear();
}
else
{
_tracker.Add(this, rectTransform, DrivenTransformProperties.Scale);
}
}
}
}

View File

@@ -1,5 +1,5 @@
using System;
using Coffee.UIParticleInternal;
using Coffee.UIParticleExtensions;
using UnityEngine;
using UnityEngine.Events;

View File

@@ -4,10 +4,9 @@
#elif UNITY_2022_3_OR_NEWER
#define PS_BAKE_API_V2
#endif
using System;
using System.Collections.Generic;
using Coffee.UIParticleInternal;
using Coffee.UIParticleExtensions;
using UnityEditor;
using UnityEngine;
using UnityEngine.Profiling;
@@ -29,6 +28,7 @@ namespace Coffee.UIExtensions
private static readonly List<UIParticleRenderer> s_Renderers = new List<UIParticleRenderer>();
private static readonly List<Color32> s_Colors = new List<Color32>();
private static readonly Vector3[] s_Corners = new Vector3[4];
private Material _currentMaterialForRendering;
private bool _delay;
private int _index;
private bool _isTrail;
@@ -39,6 +39,7 @@ namespace Coffee.UIExtensions
private float _prevCanvasScale;
private Vector3 _prevPsPos;
private Vector3 _prevScale;
private bool _isPrevStored;
private Vector2Int _prevScreenSize;
private bool _prewarm;
private ParticleSystemRenderer _renderer;
@@ -117,8 +118,9 @@ namespace Coffee.UIExtensions
}
else
{
MaterialRepository.Release(ref _modifiedMaterial);
_materialForRendering = null;
ModifiedMaterial.Remove(_modifiedMaterial);
_modifiedMaterial = null;
_currentMaterialForRendering = null;
}
}
@@ -134,14 +136,18 @@ namespace Coffee.UIExtensions
hideFlags = HideFlags.HideAndDontSave
};
}
_currentMaterialForRendering = null;
}
protected override void OnDisable()
{
base.OnDisable();
MaterialRepository.Release(ref _modifiedMaterial);
_materialForRendering = null;
ModifiedMaterial.Remove(_modifiedMaterial);
_modifiedMaterial = null;
_currentMaterialForRendering = null;
_isPrevStored = false;
}
public static UIParticleRenderer AddRenderer(UIParticle parent, int index)
@@ -173,9 +179,12 @@ namespace Coffee.UIExtensions
/// </summary>
public override Material GetModifiedMaterial(Material baseMaterial)
{
_currentMaterialForRendering = null;
if (!IsActive() || !_parent)
{
MaterialRepository.Release(ref _modifiedMaterial);
ModifiedMaterial.Remove(_modifiedMaterial);
_modifiedMaterial = null;
return baseMaterial;
}
@@ -185,30 +194,23 @@ namespace Coffee.UIExtensions
var texture = mainTexture;
if (texture == null && _parent.m_AnimatableProperties.Length == 0)
{
MaterialRepository.Release(ref _modifiedMaterial);
ModifiedMaterial.Remove(_modifiedMaterial);
_modifiedMaterial = null;
return modifiedMaterial;
}
//
var hash = new Hash128(
modifiedMaterial ? (uint)modifiedMaterial.GetInstanceID() : 0,
texture ? (uint)texture.GetInstanceID() : 0,
0 < _parent.m_AnimatableProperties.Length ? (uint)GetInstanceID() : 0,
var id = _parent.m_AnimatableProperties.Length == 0 ? 0 : GetInstanceID();
#if UNITY_EDITOR
(uint)EditorJsonUtility.ToJson(modifiedMaterial).GetHashCode()
var props = EditorJsonUtility.ToJson(modifiedMaterial).GetHashCode();
#else
0
var props = 0;
#endif
);
if (!MaterialRepository.Valid(hash, _modifiedMaterial))
{
MaterialRepository.Get(hash, ref _modifiedMaterial, () => new Material(modifiedMaterial)
{
hideFlags = HideFlags.HideAndDontSave
});
}
modifiedMaterial = ModifiedMaterial.Add(modifiedMaterial, texture, id, props);
ModifiedMaterial.Remove(_modifiedMaterial);
_modifiedMaterial = modifiedMaterial;
return _modifiedMaterial;
return modifiedMaterial;
}
public void Set(UIParticle parent, ParticleSystem ps, bool isTrail)
@@ -404,22 +406,33 @@ namespace Coffee.UIExtensions
_lastBounds = bounds;
// Convert linear color to gamma color.
if (canvas.ShouldGammaToLinearInMesh())
if (QualitySettings.activeColorSpace == ColorSpace.Linear)
{
workerMesh.LinearToGamma();
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();
}
var components = ListPool<Component>.Rent();
GetComponents(typeof(IMeshModifier), components);
GetComponents(typeof(IMeshModifier), s_Components);
for (var i = 0; i < s_Components.Count; i++)
{
#pragma warning disable CS0618 // Type or member is obsolete
for (var i = 0; i < components.Count; i++)
{
((IMeshModifier)components[i]).ModifyMesh(workerMesh);
}
((IMeshModifier)s_Components[i]).ModifyMesh(workerMesh);
#pragma warning restore CS0618 // Type or member is obsolete
}
ListPool<Component>.Return(ref components);
s_Components.Clear();
}
Profiler.EndSample();
@@ -513,7 +526,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();
@@ -570,7 +583,7 @@ namespace Coffee.UIExtensions
var isWorldSpace = _particleSystem.IsWorldSpace();
var canvasScale = _parent.canvas ? _parent.canvas.scaleFactor : 1f;
var resolutionChanged = _prevScreenSize != screenSize || _prevCanvasScale != canvasScale;
if (resolutionChanged && isWorldSpace)
if (resolutionChanged && isWorldSpace && _isPrevStored)
{
// Update particle array size and get particles.
var size = _particleSystem.particleCount;
@@ -596,6 +609,7 @@ namespace Coffee.UIExtensions
_delay = true;
_prevScale = scale;
_prevPsPos = psPos;
_isPrevStored = true;
}
_prevCanvasScale = canvas ? canvas.scaleFactor : 1f;
@@ -621,13 +635,15 @@ namespace Coffee.UIExtensions
// 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
@@ -643,7 +659,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

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using Coffee.UIParticleInternal;
using UnityEditor;
using UnityEngine;
@@ -40,12 +39,12 @@ namespace Coffee.UIExtensions
#if UNITY_EDITOR
[InitializeOnLoadMethod]
#else
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
#endif
[RuntimeInitializeOnLoadMethod]
private static void InitializeOnLoad()
{
UIExtraCallbacks.onAfterCanvasRebuild += Refresh;
Canvas.willRenderCanvases -= Refresh;
Canvas.willRenderCanvases += Refresh;
}
private static void Refresh()
@@ -58,9 +57,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();
}
@@ -77,9 +75,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();
}
}

View File

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

View File

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

View File

@@ -1,11 +1,96 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Coffee.UIParticleInternal
namespace Coffee.UIParticleExtensions
{
internal static class ParticleSystemExtensions
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
{
private static ParticleSystem.Particle[] s_TmpParticles = new ParticleSystem.Particle[2048];
@@ -165,11 +250,59 @@ namespace Coffee.UIParticleInternal
public static void Exec(this List<ParticleSystem> self, Action<ParticleSystem> action)
{
foreach (var p in self)
{
if (!p) continue;
action.Invoke(p);
}
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)
{
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);
}
}
#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
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: dfbc9e244a2a040179e7f5b58ec0b978
guid: d188d31b140094ebc84a9caafbc7ac71
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

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

View File

@@ -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.4",
"version": "4.7.0",
"unity": "2018.2",
"license": "MIT",
"repository": {