Compare commits

..

3 Commits

Author SHA1 Message Date
mob-sakai
36ab069eba feat: add asset update system from v4 to v5 2024-05-23 13:05:23 +09:00
mob-sakai
66c76fb92b refactor: using Coffee.Internal 2024-05-23 13:04:15 +09:00
mob-sakai
b3a49514b5 chore: update test workflow 2024-05-23 13:04:15 +09:00
51 changed files with 1093 additions and 502 deletions

View File

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

View File

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

1
.gitignore vendored
View File

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

View File

@@ -1,15 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f22a23b9d98e440478697f4adf30e61c, type: 3}
m_Name: UIParticle
m_EditorClassIdentifier:
m_LinearToGamma: 1

View File

@@ -5,7 +5,7 @@
"depth": 0, "depth": 0,
"source": "git", "source": "git",
"dependencies": {}, "dependencies": {},
"hash": "4a57c0a498ba7ce667290ec39510b1474030471a" "hash": "41a1b604af8769b600d9c75db02ff35ec30611dc"
}, },
"com.coffee.nano-monitor": { "com.coffee.nano-monitor": {
"version": "https://github.com/mob-sakai/Coffee.Internal.git?path=Packages/NanoMonitor", "version": "https://github.com/mob-sakai/Coffee.Internal.git?path=Packages/NanoMonitor",
@@ -14,7 +14,7 @@
"dependencies": { "dependencies": {
"com.unity.ugui": "1.0.0" "com.unity.ugui": "1.0.0"
}, },
"hash": "4a57c0a498ba7ce667290ec39510b1474030471a" "hash": "41a1b604af8769b600d9c75db02ff35ec30611dc"
}, },
"com.coffee.simple-scene-navigator": { "com.coffee.simple-scene-navigator": {
"version": "https://github.com/mob-sakai/Coffee.Internal.git?path=Packages/SceneNavigator", "version": "https://github.com/mob-sakai/Coffee.Internal.git?path=Packages/SceneNavigator",
@@ -23,7 +23,7 @@
"dependencies": { "dependencies": {
"com.unity.ugui": "1.0.0" "com.unity.ugui": "1.0.0"
}, },
"hash": "4a57c0a498ba7ce667290ec39510b1474030471a" "hash": "41a1b604af8769b600d9c75db02ff35ec30611dc"
}, },
"com.coffee.sub-asset-editor": { "com.coffee.sub-asset-editor": {
"version": "https://github.com/mob-sakai/SubAssetEditor.git", "version": "https://github.com/mob-sakai/SubAssetEditor.git",

View File

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

View File

@@ -1,57 +1,3 @@
# [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)
### 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) ## [4.6.4](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.6.3...v4.6.4) (2024-05-22)

View File

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

View File

@@ -0,0 +1,31 @@
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,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: f22a23b9d98e440478697f4adf30e61c guid: d3378b5e701274218b04cb5588b8a3bd
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

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

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

View File

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

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

View File

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

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

View File

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

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

View File

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

View File

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

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

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

View File

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

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

View File

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

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

View File

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

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

View File

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

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

View File

@@ -2,22 +2,19 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using UnityEditor; using UnityEditor;
using UnityEditor.UI;
using UnityEditorInternal; using UnityEditorInternal;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
#if UNITY_2021_2_OR_NEWER #if UNITY_2021_2_OR_NEWER
using UnityEditor.Overlays; using UnityEditor.Overlays;
#else #else
using System; using System;
using System.Reflection; using System.Reflection;
using Coffee.UIParticleInternal;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
#endif #endif
#if UNITY_2021_2_OR_NEWER #if UNITY_2021_2_OR_NEWER
using UnityEditor.SceneManagement; using UnityEditor.SceneManagement;
#elif UNITY_2018_3_OR_NEWER #elif UNITY_2018_3_OR_NEWER
using UnityEditor.Experimental.SceneManagement; using UnityEditor.Experimental.SceneManagement;
#endif #endif
@@ -26,8 +23,31 @@ namespace Coffee.UIExtensions
{ {
[CustomEditor(typeof(UIParticle))] [CustomEditor(typeof(UIParticle))]
[CanEditMultipleObjects] [CanEditMultipleObjects]
internal class UIParticleEditor : Editor internal class UIParticleEditor : GraphicEditor
{ {
#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. // Constant or Static Members.
//################################ //################################
@@ -39,6 +59,7 @@ namespace Coffee.UIExtensions
private static readonly GUIContent s_Content3D = new GUIContent("3D"); private static readonly GUIContent s_Content3D = new GUIContent("3D");
private static readonly GUIContent s_ContentRandom = new GUIContent("Random"); private static readonly GUIContent s_ContentRandom = new GUIContent("Random");
private static readonly GUIContent s_ContentScale = new GUIContent("Scale"); private static readonly GUIContent s_ContentScale = new GUIContent("Scale");
private static SerializedObject s_SerializedObject;
private static bool s_XYZMode; private static bool s_XYZMode;
private SerializedProperty _maskable; private SerializedProperty _maskable;
@@ -66,14 +87,66 @@ namespace Coffee.UIExtensions
"_ColorMask" "_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. // Public/Protected Members.
//################################ //################################
/// <summary> /// <summary>
/// This function is called when the object becomes enabled and active. /// This function is called when the object becomes enabled and active.
/// </summary> /// </summary>
private void OnEnable() protected override void OnEnable()
{ {
base.OnEnable();
_maskable = serializedObject.FindProperty("m_Maskable"); _maskable = serializedObject.FindProperty("m_Maskable");
_scale3D = serializedObject.FindProperty("m_Scale3D"); _scale3D = serializedObject.FindProperty("m_Scale3D");
_animatableProperties = serializedObject.FindProperty("m_AnimatableProperties"); _animatableProperties = serializedObject.FindProperty("m_AnimatableProperties");
@@ -401,14 +474,61 @@ namespace Coffee.UIExtensions
private static void DrawAutoScaling(SerializedProperty prop, IEnumerable<UIParticle> uiParticles) private static void DrawAutoScaling(SerializedProperty prop, IEnumerable<UIParticle> uiParticles)
{ {
var isTransformMode = prop.intValue == (int)UIParticle.AutoScalingMode.Transform;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(prop); 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) private void DestroyUIParticle(UIParticle p, bool ignoreCurrent = false)
{ {
if (!p || (ignoreCurrent && target == p)) return; if (!p || (ignoreCurrent && target == p)) return;
var cr = p.canvasRenderer;
DestroyImmediate(p); DestroyImmediate(p);
DestroyImmediate(cr);
#if UNITY_2018_3_OR_NEWER #if UNITY_2018_3_OR_NEWER
var stage = PrefabStageUtility.GetCurrentPrefabStage(); var stage = PrefabStageUtility.GetCurrentPrefabStage();

View File

@@ -1,4 +1,4 @@
Copyright 2018-2024 mob-sakai Copyright 2018-2023 mob-sakai
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@@ -1,4 +1,4 @@
# <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) # Particle Effect For UGUI (UI Particle)
This package provides a component to render particle effects for uGUI in Unity 2018.2 or later. 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. The particle rendering is maskable and sortable, without the need for an extra Camera, RenderTexture, or Canvas.
@@ -117,16 +117,6 @@ Or, use [UpmGitExtension](https://github.com/mob-sakai/UpmGitExtension) to insta
<br><br> <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 ## 🚀 Usage
### UIParticle Component ### UIParticle Component
@@ -186,10 +176,9 @@ section.
### Script usage ### Script usage
```cs ```cs
// Instantiate ParticleSystem prefab with UIParticle on runtime. // Instant ParticleSystem prefab with UIParticle on runtime.
var go = GameObject.Instantiate(prefab); var go = GameObject.Instantiate(prefab);
var uiParticle = go.AddComponent<UIParticle>(); var uiParticle = go.AddComponent<UIParticle>();
uiParticle.scale = 100;
// Control by ParticleSystem. // Control by ParticleSystem.
particleSystem.Play(); particleSystem.Play();

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Generic;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
using UnityEngine.Profiling; using UnityEngine.Profiling;
@@ -12,48 +11,6 @@ namespace Coffee.UIParticleInternal
/// </summary> /// </summary>
internal static class ComponentExtensions 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> /// <summary>
/// Get or add a component of a specific type to a GameObject. /// Get or add a component of a specific type to a GameObject.
/// </summary> /// </summary>

View File

@@ -1,13 +1,11 @@
using System; using System;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using Conditional = System.Diagnostics.ConditionalAttribute;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
#if ENABLE_COFFEE_LOGGER #if ENABLE_COFFEE_LOGGER
using System.Reflection; using System.Reflection;
using System.Collections.Generic; using System.Collections.Generic;
#else
using Conditional = System.Diagnostics.ConditionalAttribute;
#endif #endif
namespace Coffee.UIParticleInternal namespace Coffee.UIParticleInternal

View File

@@ -42,34 +42,6 @@ namespace Coffee.UIParticleInternal
Profiler.EndSample(); 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> /// <summary>
/// Adds or retrieves a cached material based on the hash. /// Adds or retrieves a cached material based on the hash.
/// </summary> /// </summary>

View File

@@ -6,6 +6,7 @@ using UnityEngine;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using UnityEngine.Rendering; using UnityEngine.Rendering;
using UnityEngine.Serialization; using UnityEngine.Serialization;
using UnityEngine.UI;
using Random = UnityEngine.Random; using Random = UnityEngine.Random;
[assembly: InternalsVisibleTo("Coffee.UIParticle.Editor")] [assembly: InternalsVisibleTo("Coffee.UIParticle.Editor")]
@@ -18,7 +19,7 @@ namespace Coffee.UIExtensions
[ExecuteAlways] [ExecuteAlways]
[RequireComponent(typeof(RectTransform))] [RequireComponent(typeof(RectTransform))]
[RequireComponent(typeof(CanvasRenderer))] [RequireComponent(typeof(CanvasRenderer))]
public class UIParticle : UIBehaviour, ISerializationCallbackReceiver public class UIParticle : MaskableGraphic, ISerializationCallbackReceiver
{ {
public enum AutoScalingMode public enum AutoScalingMode
{ {
@@ -44,23 +45,20 @@ namespace Coffee.UIExtensions
[HideInInspector] [HideInInspector]
[SerializeField] [SerializeField]
[Obsolete]
internal bool m_IsTrail; internal bool m_IsTrail;
[HideInInspector] [HideInInspector]
[FormerlySerializedAs("m_IgnoreParent")] [FormerlySerializedAs("m_IgnoreParent")]
[SerializeField] [SerializeField]
[Obsolete]
private bool m_IgnoreCanvasScaler; private bool m_IgnoreCanvasScaler;
[HideInInspector] [HideInInspector]
[SerializeField] [SerializeField]
[Obsolete] private bool m_AbsoluteMode;
internal bool m_AbsoluteMode;
[Tooltip("Particle effect scale")] [Tooltip("Particle effect scale")]
[SerializeField] [SerializeField]
private Vector3 m_Scale3D = new Vector3(1, 1, 1); private Vector3 m_Scale3D = new Vector3(10, 10, 10);
[Tooltip("Animatable material properties.\n" + [Tooltip("Animatable material properties.\n" +
"If you want to change the material properties of the ParticleSystem in Animation, enable it.")] "If you want to change the material properties of the ParticleSystem in Animation, enable it.")]
@@ -95,56 +93,25 @@ namespace Coffee.UIExtensions
[SerializeField] [SerializeField]
[Tooltip("Prevent the root-Canvas scale from affecting the hierarchy-scaled ParticleSystem.")] [Tooltip("Prevent the root-Canvas scale from affecting the hierarchy-scaled ParticleSystem.")]
[Obsolete] private bool m_AutoScaling = true;
internal bool m_AutoScaling;
[SerializeField] [SerializeField]
[Tooltip("Transform: Transform.lossyScale (=world scale) will be set to (1, 1, 1)." + [Tooltip("Transform: Transform.lossyScale (=world scale) will be set to (1, 1, 1)." +
"UIParticle: UIParticle.scale will be adjusted.")] "UIParticle: UIParticle.scale will be adjusted.")]
private AutoScalingMode m_AutoScalingMode = AutoScalingMode.Transform; private AutoScalingMode m_AutoScalingMode = AutoScalingMode.Transform;
[SerializeField]
private bool m_Maskable = true;
private readonly List<UIParticleRenderer> _renderers = new List<UIParticleRenderer>(); private readonly List<UIParticleRenderer> _renderers = new List<UIParticleRenderer>();
private Canvas _canvas;
private int _groupId; private int _groupId;
private Camera _bakeCamera; private Camera _orthoCamera;
private DrivenRectTransformTracker _tracker; 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> /// <summary>
/// Does this graphic allow masking. /// Should this graphic be considered a target for ray-casting?
/// </summary> /// </summary>
public bool maskable public override bool raycastTarget
{ {
get => m_Maskable; get => false;
set set { }
{
if (value == m_Maskable) return;
m_Maskable = value;
UpdateRendererMaterial();
}
} }
/// <summary> /// <summary>
@@ -234,12 +201,7 @@ namespace Coffee.UIExtensions
{ {
if (m_AutoScalingMode == value) return; if (m_AutoScalingMode == value) return;
m_AutoScalingMode = value; m_AutoScalingMode = value;
UpdateTracker();
if (autoScalingMode != AutoScalingMode.Transform && _isScaleStored)
{
transform.localScale = _storedScale;
_isScaleStored = false;
}
} }
} }
@@ -282,9 +244,9 @@ namespace Coffee.UIExtensions
/// <summary> /// <summary>
/// Particle effect scale. /// Particle effect scale.
/// </summary> /// </summary>
public Vector3 scale3DForCalc => autoScalingMode == AutoScalingMode.Transform public Vector3 scale3DForCalc => autoScalingMode == AutoScalingMode.UIParticle
? m_Scale3D ? m_Scale3D.GetScaled(canvasScale)
: m_Scale3D.GetScaled(canvasScale, transform.localScale); : m_Scale3D;
public List<ParticleSystem> particles => m_Particles; public List<ParticleSystem> particles => m_Particles;
@@ -304,6 +266,8 @@ namespace Coffee.UIExtensions
} }
} }
public override Material materialForRendering => null;
/// <summary> /// <summary>
/// Paused. /// Paused.
/// </summary> /// </summary>
@@ -311,15 +275,15 @@ namespace Coffee.UIExtensions
public Vector3 parentScale { get; private set; } public Vector3 parentScale { get; private set; }
private Vector3 canvasScale { get; set; } public Vector3 canvasScale { get; private set; }
protected override void OnEnable() protected override void OnEnable()
{ {
_isScaleStored = false;
ResetGroupId(); ResetGroupId();
UpdateTracker();
UIParticleUpdater.Register(this); UIParticleUpdater.Register(this);
RegisterDirtyMaterialCallback(UpdateRendererMaterial);
//
if (0 < particles.Count) if (0 < particles.Count)
{ {
RefreshParticles(particles); RefreshParticles(particles);
@@ -329,7 +293,7 @@ namespace Coffee.UIExtensions
RefreshParticles(); RefreshParticles();
} }
UpdateRendererMaterial(); base.OnEnable();
} }
/// <summary> /// <summary>
@@ -337,24 +301,12 @@ namespace Coffee.UIExtensions
/// </summary> /// </summary>
protected override void OnDisable() protected override void OnDisable()
{ {
_tracker.Clear(); UpdateTracker();
if (autoScalingMode == AutoScalingMode.Transform && _isScaleStored)
{
transform.localScale = _storedScale;
}
_isScaleStored = false;
UIParticleUpdater.Unregister(this); UIParticleUpdater.Unregister(this);
_renderers.ForEach(r => r.Reset()); _renderers.ForEach(r => r.Reset());
_canvas = null; UnregisterDirtyMaterialCallback(UpdateRendererMaterial);
}
/// <summary> base.OnDisable();
/// Called when the state of the parent Canvas is changed.
/// </summary>
protected override void OnCanvasHierarchyChanged()
{
_canvas = null;
} }
/// <summary> /// <summary>
@@ -364,13 +316,13 @@ namespace Coffee.UIExtensions
{ {
} }
/// <summary> #if UNITY_EDITOR
/// This function is called when a direct or indirect parent of the transform of the GameObject has changed. protected override void OnValidate()
/// </summary>
protected override void OnTransformParentChanged()
{ {
_canvas = null; base.OnValidate();
UpdateTracker();
} }
#endif
void ISerializationCallbackReceiver.OnBeforeSerialize() void ISerializationCallbackReceiver.OnBeforeSerialize()
{ {
@@ -378,7 +330,6 @@ namespace Coffee.UIExtensions
void ISerializationCallbackReceiver.OnAfterDeserialize() void ISerializationCallbackReceiver.OnAfterDeserialize()
{ {
#pragma warning disable CS0612 // Type or member is obsolete
if (m_IgnoreCanvasScaler || m_AutoScaling) if (m_IgnoreCanvasScaler || m_AutoScaling)
{ {
m_IgnoreCanvasScaler = false; m_IgnoreCanvasScaler = false;
@@ -391,47 +342,31 @@ namespace Coffee.UIExtensions
m_AbsoluteMode = false; m_AbsoluteMode = false;
m_PositionMode = PositionMode.Absolute; m_PositionMode = PositionMode.Absolute;
} }
#pragma warning restore CS0612 // Type or member is obsolete
} }
/// <summary>
/// Play the ParticleSystems.
/// </summary>
public void Play() public void Play()
{ {
particles.Exec(p => p.Simulate(0, false, true)); particles.Exec(p => p.Simulate(0, false, true));
isPaused = false; isPaused = false;
} }
/// <summary>
/// Pause the ParticleSystems.
/// </summary>
public void Pause() public void Pause()
{ {
particles.Exec(p => p.Pause()); particles.Exec(p => p.Pause());
isPaused = true; isPaused = true;
} }
/// <summary>
/// Unpause the ParticleSystems.
/// </summary>
public void Resume() public void Resume()
{ {
isPaused = false; isPaused = false;
} }
/// <summary>
/// Stop the ParticleSystems.
/// </summary>
public void Stop() public void Stop()
{ {
particles.Exec(p => p.Stop()); particles.Exec(p => p.Stop());
isPaused = true; isPaused = true;
} }
/// <summary>
/// Start emission of the ParticleSystems.
/// </summary>
public void StartEmission() public void StartEmission()
{ {
particles.Exec(p => particles.Exec(p =>
@@ -441,9 +376,6 @@ namespace Coffee.UIExtensions
}); });
} }
/// <summary>
/// Stop emission of the ParticleSystems.
/// </summary>
public void StopEmission() public void StopEmission()
{ {
particles.Exec(p => particles.Exec(p =>
@@ -453,34 +385,24 @@ namespace Coffee.UIExtensions
}); });
} }
/// <summary>
/// Clear the particles of the ParticleSystems.
/// </summary>
public void Clear() public void Clear()
{ {
particles.Exec(p => p.Clear()); particles.Exec(p => p.Clear());
isPaused = true; isPaused = true;
} }
/// <summary>
/// Refresh UIParticle using the ParticleSystem instance.
/// </summary>
public void SetParticleSystemInstance(GameObject instance) public void SetParticleSystemInstance(GameObject instance)
{ {
SetParticleSystemInstance(instance, true); SetParticleSystemInstance(instance, true);
} }
/// <summary>
/// Refresh UIParticle using the ParticleSystem instance.
/// </summary>
public void SetParticleSystemInstance(GameObject instance, bool destroyOldParticles) public void SetParticleSystemInstance(GameObject instance, bool destroyOldParticles)
{ {
if (!instance) return; if (!instance) return;
var childCount = transform.childCount; foreach (Transform child in transform)
for (var i = 0; i < childCount; i++)
{ {
var go = transform.GetChild(i).gameObject; var go = child.gameObject;
go.SetActive(false); go.SetActive(false);
if (destroyOldParticles) if (destroyOldParticles)
{ {
@@ -495,10 +417,6 @@ namespace Coffee.UIExtensions
RefreshParticles(instance); RefreshParticles(instance);
} }
/// <summary>
/// Refresh UIParticle using the prefab.
/// The prefab is automatically instantiated.
/// </summary>
public void SetParticleSystemPrefab(GameObject prefab) public void SetParticleSystemPrefab(GameObject prefab)
{ {
if (!prefab) return; if (!prefab) return;
@@ -506,31 +424,16 @@ namespace Coffee.UIExtensions
SetParticleSystemInstance(Instantiate(prefab.gameObject), true); SetParticleSystemInstance(Instantiate(prefab.gameObject), true);
} }
/// <summary>
/// Refresh UIParticle.
/// Collect ParticleSystems under the GameObject and refresh the UIParticle.
/// </summary>
public void RefreshParticles() public void RefreshParticles()
{ {
RefreshParticles(gameObject); RefreshParticles(gameObject);
} }
/// <summary>
/// Refresh UIParticle.
/// Collect ParticleSystems under the GameObject and refresh the UIParticle.
/// </summary>
private void RefreshParticles(GameObject root) private void RefreshParticles(GameObject root)
{ {
if (!root) return; if (!root) return;
root.GetComponentsInChildren(true, particles); root.GetComponentsInChildren(true, particles);
for (var i = particles.Count - 1; 0 <= i; i--) particles.RemoveAll(x => x.GetComponentInParent<UIParticle>(true) != this);
{
var ps = particles[i];
if (!ps || ps.GetComponentInParent<UIParticle>(true) != this)
{
particles.RemoveAt(i);
}
}
for (var i = 0; i < particles.Count; i++) for (var i = 0; i < particles.Count; i++)
{ {
@@ -545,39 +448,31 @@ namespace Coffee.UIExtensions
RefreshParticles(particles); RefreshParticles(particles);
} }
/// <summary> public void RefreshParticles(List<ParticleSystem> particles)
/// 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 // #246: Nullptr exceptions when using nested UIParticle components in hierarchy
_renderers.Clear(); _renderers.Clear();
var childCount = transform.childCount; foreach (Transform child in transform)
for (var i = 0; i < childCount; i++)
{ {
var child = transform.GetChild(i); var uiParticleRenderer = child.GetComponent<UIParticleRenderer>();
if (child.TryGetComponent(out UIParticleRenderer uiParticleRenderer))
if (uiParticleRenderer != null)
{ {
_renderers.Add(uiParticleRenderer); _renderers.Add(uiParticleRenderer);
} }
} }
// Reset the UIParticleRenderer components.
for (var i = 0; i < _renderers.Count; i++) for (var i = 0; i < _renderers.Count; i++)
{ {
_renderers[i].Reset(i); _renderers[i].Reset(i);
} }
// Set the ParticleSystem to the UIParticleRenderer. If the trail is enabled, set it additionally.
var j = 0; var j = 0;
for (var i = 0; i < particleSystems.Count; i++) for (var i = 0; i < particles.Count; i++)
{ {
var ps = particleSystems[i]; var ps = particles[i];
if (!ps) continue; if (!ps) continue;
GetRenderer(j++).Set(this, ps, false); GetRenderer(j++).Set(this, ps, false);
// If the trail is enabled, set it additionally.
if (ps.trails.enabled) if (ps.trails.enabled)
{ {
GetRenderer(j++).Set(this, ps, true); GetRenderer(j++).Set(this, ps, true);
@@ -587,26 +482,12 @@ namespace Coffee.UIExtensions
internal void UpdateTransformScale() internal void UpdateTransformScale()
{ {
_tracker.Clear();
canvasScale = canvas.rootCanvas.transform.localScale.Inverse(); canvasScale = canvas.rootCanvas.transform.localScale.Inverse();
parentScale = transform.parent.lossyScale; parentScale = transform.parent.lossyScale;
if (autoScalingMode != AutoScalingMode.Transform) if (autoScalingMode != AutoScalingMode.Transform) return;
{
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(); var newScale = parentScale.Inverse();
if (currentScale != newScale) if (transform.localScale != newScale)
{ {
transform.localScale = newScale; transform.localScale = newScale;
} }
@@ -619,10 +500,11 @@ namespace Coffee.UIExtensions
for (var i = 0; i < _renderers.Count; i++) for (var i = 0; i < _renderers.Count; i++)
{ {
var r = _renderers[i]; var r = _renderers[i];
if (r) continue; if (!r)
{
RefreshParticles(particles); RefreshParticles(particles);
break; break;
}
} }
var bakeCamera = GetBakeCamera(); var bakeCamera = GetBakeCamera();
@@ -630,7 +512,6 @@ namespace Coffee.UIExtensions
{ {
var r = _renderers[i]; var r = _renderers[i];
if (!r) continue; if (!r) continue;
r.UpdateMesh(bakeCamera); r.UpdateMesh(bakeCamera);
} }
} }
@@ -642,6 +523,17 @@ namespace Coffee.UIExtensions
: Random.Range(m_GroupId, m_GroupMaxId + 1); : Random.Range(m_GroupId, m_GroupMaxId + 1);
} }
protected override void UpdateMaterial()
{
}
/// <summary>
/// Call to update the geometry of the Graphic onto the CanvasRenderer.
/// </summary>
protected override void UpdateGeometry()
{
}
private void UpdateRendererMaterial() private void UpdateRendererMaterial()
{ {
for (var i = 0; i < _renderers.Count; i++) for (var i = 0; i < _renderers.Count; i++)
@@ -671,46 +563,64 @@ namespace Coffee.UIExtensions
private Camera GetBakeCamera() private Camera GetBakeCamera()
{ {
if (!canvas) return Camera.main; if (!canvas) return Camera.main;
if (_bakeCamera) return _bakeCamera;
// Find existing baking camera. // When render mode is ScreenSpaceCamera or WorldSpace, use world camera.
var childCount = transform.childCount; var root = canvas.rootCanvas;
for (var i = 0; i < childCount; i++) if (root.renderMode != RenderMode.ScreenSpaceOverlay)
{ {
if (transform.GetChild(i).TryGetComponent<Camera>(out var cam) return root.worldCamera ? root.worldCamera : Camera.main;
&& cam.name == "[generated] UIParticle BakingCamera") }
// When render mode is ScreenSpaceOverlay, use ortho-camera.
if (!_orthoCamera)
{
// Find existing ortho-camera.
foreach (Transform child in transform)
{ {
_bakeCamera = cam; var cam = child.GetComponent<Camera>();
break; 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;
} }
} }
// Create baking camera. //
if (!_bakeCamera) 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
{ {
var go = new GameObject("[generated] UIParticle BakingCamera") _tracker.Clear();
{ }
hideFlags = HideFlags.HideAndDontSave else
}; {
go.SetActive(false); _tracker.Add(this, rectTransform, DrivenTransformProperties.Scale);
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;
} }
} }
} }

View File

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

View File

@@ -22,25 +22,25 @@ namespace Coffee.UIExtensions
[AddComponentMenu("")] [AddComponentMenu("")]
internal class UIParticleRenderer : MaskableGraphic 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 CombineInstance[] s_CombineInstances = { new CombineInstance() };
private static readonly List<Material> s_Materials = new List<Material>(2); private static readonly List<Material> s_Materials = new List<Material>(2);
private static MaterialPropertyBlock s_Mpb; private static MaterialPropertyBlock s_Mpb;
private static readonly List<UIParticleRenderer> s_Renderers = new List<UIParticleRenderer>(8); private static readonly 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 static readonly Vector3[] s_Corners = new Vector3[4];
private bool _delay; private bool _delay;
private int _index; private int _index;
private bool _isTrail; private bool _isTrail;
private Bounds _lastBounds; private Bounds _lastBounds;
private Material _materialForRendering;
private Material _modifiedMaterial; private Material _modifiedMaterial;
private UIParticle _parent; private UIParticle _parent;
private ParticleSystem _particleSystem; private ParticleSystem _particleSystem;
private float _prevCanvasScale; private float _prevCanvasScale;
private Vector3 _prevPsPos; private Vector3 _prevPsPos;
private Vector3 _prevScale; private Vector3 _prevScale;
private bool _isPrevStored;
private Vector2Int _prevScreenSize; private Vector2Int _prevScreenSize;
private bool _preWarm; private bool _prewarm;
private ParticleSystemRenderer _renderer; private ParticleSystemRenderer _renderer;
public override Texture mainTexture => _isTrail ? null : _particleSystem.GetTextureForSprite(); public override Texture mainTexture => _isTrail ? null : _particleSystem.GetTextureForSprite();
@@ -91,19 +91,6 @@ namespace Coffee.UIExtensions
} }
} }
public override Material materialForRendering
{
get
{
if (!_materialForRendering)
{
_materialForRendering = base.materialForRendering;
}
return _materialForRendering;
}
}
public void Reset(int index = -1) public void Reset(int index = -1)
{ {
if (_renderer) if (_renderer)
@@ -123,7 +110,8 @@ namespace Coffee.UIExtensions
if (this && isActiveAndEnabled) if (this && isActiveAndEnabled)
{ {
material = null; material = null;
canvasRenderer.Clear(); workerMesh.Clear();
canvasRenderer.SetMesh(workerMesh);
_lastBounds = new Bounds(); _lastBounds = new Bounds();
enabled = false; enabled = false;
} }
@@ -154,7 +142,6 @@ namespace Coffee.UIExtensions
MaterialRepository.Release(ref _modifiedMaterial); MaterialRepository.Release(ref _modifiedMaterial);
_materialForRendering = null; _materialForRendering = null;
_isPrevStored = false;
} }
public static UIParticleRenderer AddRenderer(UIParticle parent, int index) public static UIParticleRenderer AddRenderer(UIParticle parent, int index)
@@ -215,11 +202,10 @@ namespace Coffee.UIExtensions
); );
if (!MaterialRepository.Valid(hash, _modifiedMaterial)) if (!MaterialRepository.Valid(hash, _modifiedMaterial))
{ {
MaterialRepository.Get(hash, ref _modifiedMaterial, x => new Material(x.mat) MaterialRepository.Get(hash, ref _modifiedMaterial, () => new Material(modifiedMaterial)
{ {
hideFlags = HideFlags.HideAndDontSave, hideFlags = HideFlags.HideAndDontSave
mainTexture = x.texture ? x.texture : x.mat.mainTexture });
}, (mat: modifiedMaterial, texture));
} }
return _modifiedMaterial; return _modifiedMaterial;
@@ -233,20 +219,20 @@ namespace Coffee.UIExtensions
gameObject.layer = parent.gameObject.layer; gameObject.layer = parent.gameObject.layer;
_particleSystem = ps; _particleSystem = ps;
_preWarm = _particleSystem.main.prewarm; _prewarm = _particleSystem.main.prewarm;
#if UNITY_EDITOR #if UNITY_EDITOR
if (Application.isPlaying) if (Application.isPlaying)
#endif #endif
{ {
if (_particleSystem.isPlaying || _preWarm) if (_particleSystem.isPlaying || _prewarm)
{ {
_particleSystem.Clear(); _particleSystem.Clear();
_particleSystem.Pause(); _particleSystem.Pause();
} }
} }
ps.TryGetComponent(out _renderer); _renderer = ps.GetComponent<ParticleSystemRenderer>();
_renderer.enabled = false; _renderer.enabled = false;
//_emitter = emitter; //_emitter = emitter;
@@ -418,7 +404,7 @@ namespace Coffee.UIExtensions
_lastBounds = bounds; _lastBounds = bounds;
// Convert linear color to gamma color. // Convert linear color to gamma color.
if (UIParticleProjectSettings.enableLinearToGamma && canvas.ShouldGammaToLinearInMesh()) if (canvas.ShouldGammaToLinearInMesh())
{ {
workerMesh.LinearToGamma(); workerMesh.LinearToGamma();
} }
@@ -438,49 +424,66 @@ namespace Coffee.UIExtensions
Profiler.EndSample(); Profiler.EndSample();
// Update animatable material properties.
Profiler.BeginSample("[UIParticleRenderer] Update Animatable Material Properties");
UpdateMaterialProperties();
Profiler.EndSample();
// Get grouped renderers. // Get grouped renderers.
Profiler.BeginSample("[UIParticleRenderer] Set Mesh");
s_Renderers.Clear(); s_Renderers.Clear();
if (_parent.useMeshSharing) if (_parent.useMeshSharing)
{ {
UIParticleUpdater.GetGroupedRenderers(_parent.groupId, _index, s_Renderers); 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++) for (var i = 0; i < s_Renderers.Count; i++)
{ {
if (s_Renderers[i] == this) continue; if (s_Renderers[i] == this) continue;
s_Renderers[i].canvasRenderer.SetMesh(workerMesh); s_Renderers[i].canvasRenderer.SetMesh(workerMesh);
s_Renderers[i]._lastBounds = _lastBounds; 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(); 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(); Profiler.EndSample();
s_Renderers.Clear(); s_Renderers.Clear();
} }
public override void SetMaterialDirty()
{
_materialForRendering = null;
base.SetMaterialDirty();
}
/// <summary> /// <summary>
/// Call to update the geometry of the Graphic onto the CanvasRenderer. /// Call to update the geometry of the Graphic onto the CanvasRenderer.
/// </summary> /// </summary>
@@ -510,7 +513,7 @@ namespace Coffee.UIExtensions
&& _particleSystem.main.scalingMode == ParticleSystemScalingMode.Local && _particleSystem.main.scalingMode == ParticleSystemScalingMode.Local
&& _parent.canvas) && _parent.canvas)
{ {
scale = scale.GetScaled(_parent.canvas.rootCanvas.transform.localScale); scale = scale.GetScaled(_parent.canvas.transform.localScale);
} }
Profiler.EndSample(); Profiler.EndSample();
@@ -547,7 +550,10 @@ namespace Coffee.UIExtensions
return Matrix4x4.Scale(scale); return Matrix4x4.Scale(scale);
case ParticleSystemSimulationSpace.Custom: case ParticleSystemSimulationSpace.Custom:
return Matrix4x4.Translate(_particleSystem.main.customSimulationSpace.position.GetScaled(scale)) return Matrix4x4.Translate(_particleSystem.main.customSimulationSpace.position.GetScaled(scale))
* Matrix4x4.Scale(scale); //* Matrix4x4.Translate(wpos)
* Matrix4x4.Scale(scale)
//* Matrix4x4.Translate(-wpos)
;
default: default:
throw new NotSupportedException(); throw new NotSupportedException();
} }
@@ -563,16 +569,15 @@ namespace Coffee.UIExtensions
var screenSize = new Vector2Int(Screen.width, Screen.height); var screenSize = new Vector2Int(Screen.width, Screen.height);
var isWorldSpace = _particleSystem.IsWorldSpace(); var isWorldSpace = _particleSystem.IsWorldSpace();
var canvasScale = _parent.canvas ? _parent.canvas.scaleFactor : 1f; var canvasScale = _parent.canvas ? _parent.canvas.scaleFactor : 1f;
var resolutionChanged = _prevScreenSize != screenSize var resolutionChanged = _prevScreenSize != screenSize || _prevCanvasScale != canvasScale;
|| !Mathf.Approximately(_prevCanvasScale, canvasScale); if (resolutionChanged && isWorldSpace)
if (resolutionChanged && isWorldSpace && _isPrevStored)
{ {
// Update particle array size and get particles. // Update particle array size and get particles.
var size = _particleSystem.particleCount; var size = _particleSystem.particleCount;
var particles = ParticleSystemExtensions.GetParticleArray(size); var particles = ParticleSystemExtensions.GetParticleArray(size);
_particleSystem.GetParticles(particles, size); _particleSystem.GetParticles(particles, size);
// Resolution resolver: // Resolusion resolver:
// (psPos / scale) / (prevPsPos / prevScale) -> psPos * scale.inv * prevPsPos.inv * prevScale // (psPos / scale) / (prevPsPos / prevScale) -> psPos * scale.inv * prevPsPos.inv * prevScale
var modifier = psPos.GetScaled( var modifier = psPos.GetScaled(
scale.Inverse(), scale.Inverse(),
@@ -591,7 +596,6 @@ namespace Coffee.UIExtensions
_delay = true; _delay = true;
_prevScale = scale; _prevScale = scale;
_prevPsPos = psPos; _prevPsPos = psPos;
_isPrevStored = true;
} }
_prevCanvasScale = canvas ? canvas.scaleFactor : 1f; _prevCanvasScale = canvas ? canvas.scaleFactor : 1f;
@@ -607,25 +611,23 @@ namespace Coffee.UIExtensions
? Time.unscaledDeltaTime ? Time.unscaledDeltaTime
: Time.deltaTime; : Time.deltaTime;
// Pre-warm: // Prewarm:
if (0 < deltaTime && _preWarm) if (0 < deltaTime && _prewarm)
{ {
deltaTime += main.duration; deltaTime += main.duration;
_preWarm = false; _prewarm = false;
} }
// get world position. // get world position.
var isLocalSpace = _particleSystem.IsLocalSpace(); var isLocalSpace = _particleSystem.IsLocalSpace();
var psTransform = _particleSystem.transform; var psTransform = _particleSystem.transform;
var originLocalPosition = psTransform.localPosition;
var originLocalRotation = psTransform.localRotation;
var originWorldPosition = psTransform.position; var originWorldPosition = psTransform.position;
var originWorldRotation = psTransform.rotation; var originWorldRotation = psTransform.rotation;
var emission = _particleSystem.emission; var emission = _particleSystem.emission;
var rateOverDistance = emission.enabled var rateOverDistance = emission.enabled
&& 0 < emission.rateOverDistance.constant && 0 < emission.rateOverDistance.constant
&& 0 < emission.rateOverDistanceMultiplier; && 0 < emission.rateOverDistanceMultiplier;
if (rateOverDistance && !paused && _isPrevStored) if (rateOverDistance && !paused)
{ {
// (For rate-over-distance emission,) Move to previous scaled position, simulate (delta = 0). // (For rate-over-distance emission,) Move to previous scaled position, simulate (delta = 0).
var prevScaledPos = isLocalSpace var prevScaledPos = isLocalSpace
@@ -641,8 +643,7 @@ namespace Coffee.UIExtensions
: originWorldPosition.GetScaled(scale.Inverse()); : originWorldPosition.GetScaled(scale.Inverse());
psTransform.SetPositionAndRotation(scaledPos, originWorldRotation); psTransform.SetPositionAndRotation(scaledPos, originWorldRotation);
_particleSystem.Simulate(deltaTime, false, false, false); _particleSystem.Simulate(deltaTime, false, false, false);
psTransform.localPosition = originLocalPosition; psTransform.SetPositionAndRotation(originWorldPosition, originWorldRotation);
psTransform.localRotation = originLocalRotation;
} }
#if UNITY_EDITOR #if UNITY_EDITOR
@@ -686,12 +687,12 @@ namespace Coffee.UIExtensions
if (s_Mpb.isEmpty) return; if (s_Mpb.isEmpty) return;
// #41: Copy the value from MaterialPropertyBlock to CanvasRenderer // #41: Copy the value from MaterialPropertyBlock to CanvasRenderer
if (!materialForRendering) return; if (!_modifiedMaterial) return;
for (var i = 0; i < _parent.m_AnimatableProperties.Length; i++) for (var i = 0; i < _parent.m_AnimatableProperties.Length; i++)
{ {
var ap = _parent.m_AnimatableProperties[i]; var ap = _parent.m_AnimatableProperties[i];
ap.UpdateMaterialProperties(materialForRendering, s_Mpb); ap.UpdateMaterialProperties(_modifiedMaterial, s_Mpb);
} }
s_Mpb.Clear(); s_Mpb.Clear();

View File

@@ -58,8 +58,9 @@ namespace Coffee.UIExtensions
for (var i = 0; i < s_ActiveParticles.Count; i++) for (var i = 0; i < s_ActiveParticles.Count; i++)
{ {
var uip = s_ActiveParticles[i]; var uip = s_ActiveParticles[i];
if (!uip || !uip.canvas || !uip.isPrimary || !s_UpdatedGroupIds.Add(uip.groupId)) continue; if (!uip || !uip.canvas || !uip.isPrimary || s_UpdatedGroupIds.Contains(uip.groupId)) continue;
s_UpdatedGroupIds.Add(uip.groupId);
uip.UpdateTransformScale(); uip.UpdateTransformScale();
uip.UpdateRenderers(); uip.UpdateRenderers();
} }
@@ -76,8 +77,9 @@ namespace Coffee.UIExtensions
{ {
uip.UpdateRenderers(); uip.UpdateRenderers();
} }
else if (s_UpdatedGroupIds.Add(uip.groupId)) else if (!s_UpdatedGroupIds.Contains(uip.groupId))
{ {
s_UpdatedGroupIds.Add(uip.groupId);
uip.UpdateRenderers(); uip.UpdateRenderers();
} }
} }

View File

@@ -14461,7 +14461,7 @@ Canvas:
m_GameObject: {fileID: 1074082869} m_GameObject: {fileID: 1074082869}
m_Enabled: 1 m_Enabled: 1
serializedVersion: 3 serializedVersion: 3
m_RenderMode: 1 m_RenderMode: 0
m_Camera: {fileID: 1023393581} m_Camera: {fileID: 1023393581}
m_PlaneDistance: 100 m_PlaneDistance: 100
m_PixelPerfect: 0 m_PixelPerfect: 0

View File

@@ -2,7 +2,7 @@
"name": "com.coffee.ui-particle", "name": "com.coffee.ui-particle",
"displayName": "UI Particle", "displayName": "UI Particle",
"description": "This package provides a component to render particle effects for uGUI.\nThe particle rendering is maskable and sortable, without the need for an extra Camera, RenderTexture, or Canvas.", "description": "This package provides a component to render particle effects for uGUI.\nThe particle rendering is maskable and sortable, without the need for an extra Camera, RenderTexture, or Canvas.",
"version": "5.0.0-preview.3", "version": "4.6.4",
"unity": "2018.2", "unity": "2018.2",
"license": "MIT", "license": "MIT",
"repository": { "repository": {